summaryrefslogtreecommitdiff
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/.gitattributes1
-rw-r--r--t/.gitignore1
-rwxr-xr-xt/Git-SVN/00compile.t14
-rwxr-xr-xt/Git-SVN/Utils/add_path_to_url.t27
-rwxr-xr-xt/Git-SVN/Utils/can_compress.t11
-rwxr-xr-xt/Git-SVN/Utils/canonicalize_url.t26
-rwxr-xr-xt/Git-SVN/Utils/collapse_dotdot.t23
-rwxr-xr-xt/Git-SVN/Utils/fatal.t34
-rwxr-xr-xt/Git-SVN/Utils/join_paths.t32
-rw-r--r--t/Makefile69
-rw-r--r--t/README446
-rwxr-xr-xt/aggregate-results.sh14
-rw-r--r--t/annotate-tests.sh656
-rwxr-xr-xt/check-non-portable-shell.pl28
-rw-r--r--t/diff-lib.sh2
-rw-r--r--t/diff-lib/COPYING361
-rw-r--r--t/diff-lib/README46
-rw-r--r--t/gitweb-lib.sh49
-rw-r--r--t/helper/.gitignore33
-rw-r--r--t/helper/test-chmtime.c119
-rw-r--r--t/helper/test-config.c152
-rw-r--r--t/helper/test-ctype.c42
-rw-r--r--t/helper/test-date.c99
-rw-r--r--t/helper/test-delta.c78
-rw-r--r--t/helper/test-dump-cache-tree.c67
-rw-r--r--t/helper/test-dump-split-index.c36
-rw-r--r--t/helper/test-dump-untracked-cache.c66
-rw-r--r--t/helper/test-fake-ssh.c30
-rw-r--r--t/helper/test-genrandom.c33
-rw-r--r--t/helper/test-hashmap.c264
-rw-r--r--t/helper/test-index-version.c14
-rw-r--r--t/helper/test-line-buffer.c91
-rw-r--r--t/helper/test-match-trees.c26
-rw-r--r--t/helper/test-mergesort.c52
-rw-r--r--t/helper/test-mktemp.c14
-rw-r--r--t/helper/test-parse-options.c179
-rw-r--r--t/helper/test-path-utils.c262
-rw-r--r--t/helper/test-prio-queue.c39
-rw-r--r--t/helper/test-read-cache.c13
-rw-r--r--t/helper/test-regex.c20
-rw-r--r--t/helper/test-revision-walking.c68
-rw-r--r--t/helper/test-run-command.c87
-rw-r--r--t/helper/test-scrap-cache-tree.c17
-rw-r--r--t/helper/test-sha1-array.c34
-rw-r--r--t/helper/test-sha1.c56
-rwxr-xr-xt/helper/test-sha1.sh83
-rw-r--r--t/helper/test-sigchain.c22
-rw-r--r--t/helper/test-string-list.c103
-rw-r--r--t/helper/test-submodule-config.c76
-rw-r--r--t/helper/test-subprocess.c19
-rw-r--r--t/helper/test-svn-fe.c52
-rw-r--r--t/helper/test-urlmatch-normalization.c50
-rw-r--r--t/helper/test-wildmatch.c21
-rw-r--r--t/lib-bash.sh17
-rwxr-xr-xt/lib-credential.sh287
-rw-r--r--t/lib-cvs.sh13
-rw-r--r--t/lib-diff-alternative.sh166
-rw-r--r--t/lib-gettext.sh58
-rw-r--r--t/lib-git-daemon.sh92
-rw-r--r--t/lib-git-p4.sh246
-rw-r--r--t/lib-git-svn.sh95
-rwxr-xr-xt/lib-gpg.sh57
-rw-r--r--t/lib-gpg/keyring.gpg142
-rw-r--r--t/lib-gpg/ownertrust4
-rw-r--r--t/lib-httpd.sh167
-rw-r--r--t/lib-httpd/apache.conf163
-rw-r--r--t/lib-httpd/broken-smart-http.sh10
-rwxr-xr-xt/lib-httpd/error.sh31
-rw-r--r--t/lib-httpd/passwd1
-rw-r--r--t/lib-pack.sh98
-rw-r--r--t/lib-pager.sh2
-rw-r--r--t/lib-patch-mode.sh5
-rw-r--r--t/lib-proto-disable.sh96
-rw-r--r--t/lib-read-tree.sh41
-rw-r--r--t/lib-rebase.sh132
-rwxr-xr-xt/lib-submodule-update.sh680
-rw-r--r--t/lib-t6000.sh106
-rw-r--r--t/lib-terminal.sh36
-rw-r--r--t/perf/.gitignore3
-rw-r--r--t/perf/Makefile15
-rw-r--r--t/perf/README146
-rwxr-xr-xt/perf/aggregate.perl167
-rwxr-xr-xt/perf/min_time.perl21
-rwxr-xr-xt/perf/p0000-perf-lib-sanity.sh55
-rwxr-xr-xt/perf/p0001-rev-list.sh29
-rwxr-xr-xt/perf/p0002-read-cache.sh14
-rwxr-xr-xt/perf/p3404-rebase-interactive.sh36
-rwxr-xr-xt/perf/p4000-diff-algorithms.sh29
-rwxr-xr-xt/perf/p4001-diff-no-index.sh22
-rwxr-xr-xt/perf/p4211-line-log.sh34
-rwxr-xr-xt/perf/p5302-pack-index.sh40
-rwxr-xr-xt/perf/p5310-pack-bitmaps.sh60
-rwxr-xr-xt/perf/p7000-filter-branch.sh19
-rwxr-xr-xt/perf/p7300-clean.sh35
-rwxr-xr-xt/perf/p7810-grep.sh23
-rw-r--r--t/perf/perf-lib.sh216
-rwxr-xr-xt/perf/run82
-rwxr-xr-xt/t0000-basic.sh1181
-rwxr-xr-xt/t0001-init.sh372
-rwxr-xr-xt/t0002-gitfile.sh62
-rwxr-xr-xt/t0003-attributes.sh288
-rwxr-xr-xt/t0004-unwritable.sh60
-rwxr-xr-xt/t0005-signals.sh33
-rwxr-xr-xt/t0006-date.sh62
-rwxr-xr-xt/t0007-git-var.sh49
-rwxr-xr-xt/t0008-ignores.sh844
-rwxr-xr-xt/t0009-prio-queue.sh50
-rwxr-xr-xt/t0010-racy-git.sh4
-rwxr-xr-xt/t0011-hashmap.sh253
-rwxr-xr-xt/t0020-crlf.sh260
-rwxr-xr-xt/t0021-conversion.sh209
-rwxr-xr-xt/t0023-crlf-am.sh2
-rwxr-xr-xt/t0024-crlf-archive.sh14
-rwxr-xr-xt/t0025-crlf-auto.sh122
-rwxr-xr-xt/t0026-eol-config.sh50
-rwxr-xr-xt/t0027-auto-crlf.sh553
-rwxr-xr-xt/t0030-stripspace.sh61
-rwxr-xr-xt/t0040-parse-options.sh408
-rwxr-xr-xt/t0050-filesystem.sh129
-rwxr-xr-xt/t0056-git-C.sh94
-rwxr-xr-xt/t0060-path-utils.sh297
-rwxr-xr-xt/t0061-run-command.sh89
-rwxr-xr-xt/t0062-revision-walking.sh33
-rwxr-xr-xt/t0063-string-list.sh91
-rwxr-xr-xt/t0064-sha1-array.sh94
-rwxr-xr-xt/t0070-fundamental.sh22
-rwxr-xr-xt/t0081-line-buffer.sh90
-rwxr-xr-xt/t0090-cache-tree.sh255
-rwxr-xr-xt/t0100-previous.sh15
-rwxr-xr-xt/t0110-urlmatch-normalization.sh180
-rw-r--r--t/t0110/README9
-rw-r--r--t/t0110/url-11
-rw-r--r--t/t0110/url-101
-rw-r--r--t/t0110/url-111
-rw-r--r--t/t0110/url-21
-rw-r--r--t/t0110/url-31
-rw-r--r--t/t0110/url-41
-rw-r--r--t/t0110/url-51
-rw-r--r--t/t0110/url-61
-rw-r--r--t/t0110/url-71
-rw-r--r--t/t0110/url-81
-rw-r--r--t/t0110/url-91
-rwxr-xr-xt/t0200-gettext-basic.sh108
-rw-r--r--t/t0200/test.c23
-rw-r--r--t/t0200/test.perl14
-rw-r--r--t/t0200/test.sh14
-rwxr-xr-xt/t0201-gettext-fallbacks.sh67
-rwxr-xr-xt/t0202-gettext-perl.sh27
-rwxr-xr-xt/t0202/test.pl110
-rwxr-xr-xt/t0203-gettext-setlocale-sanity.sh26
-rwxr-xr-xt/t0204-gettext-reencode-sanity.sh87
-rwxr-xr-xt/t0205-gettext-poison.sh36
-rwxr-xr-xt/t0300-credentials.sh312
-rwxr-xr-xt/t0301-credential-cache.sh23
-rwxr-xr-xt/t0302-credential-store.sh123
-rwxr-xr-xt/t0303-credential-external.sh60
-rwxr-xr-xt/t1000-read-tree-m-3way.sh87
-rwxr-xr-xt/t1001-read-tree-m-2way.sh85
-rwxr-xr-xt/t1002-read-tree-m-u-2way.sh109
-rwxr-xr-xt/t1003-read-tree-prefix.sh2
-rwxr-xr-xt/t1004-read-tree-m-u-wf.sh30
-rwxr-xr-xt/t1005-read-tree-reset.sh142
-rwxr-xr-xt/t1006-cat-file.sh430
-rwxr-xr-xt/t1007-hash-object.sh32
-rwxr-xr-xt/t1008-read-tree-overlay.sh3
-rwxr-xr-xt/t1011-read-tree-sparse-checkout.sh247
-rwxr-xr-xt/t1012-read-tree-df.sh9
-rwxr-xr-xt/t1013-read-tree-submodule.sh12
-rwxr-xr-xt/t1014-read-tree-confusing.sh62
-rwxr-xr-xt/t1020-subdirectory.sh181
-rwxr-xr-xt/t1021-rerere-in-workdir.sh55
-rwxr-xr-xt/t1050-large.sh186
-rwxr-xr-xt/t1051-large-conversion.sh86
-rwxr-xr-xt/t1060-object-corruption.sh104
-rwxr-xr-xt/t1090-sparse-checkout-scope.sh52
-rwxr-xr-xt/t1100-commit-tree-options.sh22
-rwxr-xr-xt/t1200-tutorial.sh15
-rwxr-xr-xt/t1300-repo-config.sh972
-rwxr-xr-xt/t1301-shared-repo.sh31
-rwxr-xr-xt/t1302-repo-version.sh135
-rwxr-xr-xt/t1303-wacky-config.sh86
-rwxr-xr-xt/t1304-default-acl.sh24
-rwxr-xr-xt/t1305-config-include.sh172
-rwxr-xr-xt/t1306-xdg-files.sh197
-rwxr-xr-xt/t1307-config-blob.sh79
-rwxr-xr-xt/t1308-config-set.sh232
-rwxr-xr-xt/t1350-config-hooks-path.sh37
-rwxr-xr-xt/t1400-update-ref.sh902
-rwxr-xr-xt/t1401-symbolic-ref.sh100
-rwxr-xr-xt/t1402-check-ref-format.sh167
-rwxr-xr-xt/t1403-show-ref.sh167
-rwxr-xr-xt/t1404-update-ref-df-conflicts.sh107
-rwxr-xr-xt/t1410-reflog.sh183
-rwxr-xr-xt/t1411-reflog-show.sh108
-rwxr-xr-xt/t1412-reflog-loop.sh34
-rwxr-xr-xt/t1413-reflog-detach.sh70
-rwxr-xr-xt/t1430-bad-ref-name.sh334
-rwxr-xr-xt/t1450-fsck.sh506
-rwxr-xr-xt/t1500-rev-parse.sh123
-rwxr-xr-xt/t1501-work-tree.sh426
-rwxr-xr-xt/t1501-worktree.sh206
-rwxr-xr-xt/t1502-rev-parse-parseopt.sh165
-rwxr-xr-xt/t1503-rev-parse-verify.sh45
-rwxr-xr-xt/t1504-ceiling-dirs.sh22
-rwxr-xr-xt/t1505-rev-parse-last.sh18
-rwxr-xr-xt/t1506-rev-parse-diagnosis.sh159
-rwxr-xr-xt/t1507-rev-parse-upstream.sh121
-rwxr-xr-xt/t1508-at-combinations.sh91
-rwxr-xr-xt/t1509-root-work-tree.sh (renamed from t/t1509-root-worktree.sh)53
-rwxr-xr-xt/t1509/prepare-chroot.sh40
-rwxr-xr-xt/t1510-repo-setup.sh802
-rwxr-xr-xt/t1511-rev-parse-caret.sh131
-rwxr-xr-xt/t1512-rev-parse-disambiguation.sh294
-rwxr-xr-xt/t1513-rev-parse-prefix.sh96
-rwxr-xr-xt/t1514-rev-parse-push.sh63
-rwxr-xr-xt/t1515-rev-parse-outside-repo.sh45
-rwxr-xr-xt/t1600-index.sh76
-rwxr-xr-xt/t1700-split-index.sh203
-rwxr-xr-xt/t2001-checkout-cache-clash.sh7
-rwxr-xr-xt/t2003-checkout-cache-mkdir.sh169
-rwxr-xr-xt/t2004-checkout-cache-temp.sh401
-rwxr-xr-xt/t2006-checkout-index-basic.sh24
-rwxr-xr-xt/t2007-checkout-symlink.sh12
-rwxr-xr-xt/t2008-checkout-subdir.sh14
-rwxr-xr-xt/t2010-checkout-ambiguous.sh6
-rwxr-xr-xt/t2011-checkout-invalid-head.sh41
-rwxr-xr-xt/t2012-checkout-last.sh34
-rwxr-xr-xt/t2013-checkout-submodule.sh30
-rwxr-xr-xt/t2015-checkout-unborn.sh22
-rwxr-xr-xt/t2016-checkout-patch.sh32
-rwxr-xr-xt/t2017-checkout-orphan.sh8
-rwxr-xr-xt/t2018-checkout-branch.sh201
-rwxr-xr-xt/t2019-checkout-ambiguous-ref.sh59
-rwxr-xr-xt/t2020-checkout-detach.sh166
-rwxr-xr-xt/t2021-checkout-overwrite.sh54
-rwxr-xr-xt/t2022-checkout-paths.sh81
-rwxr-xr-xt/t2023-checkout-m.sh49
-rwxr-xr-xt/t2024-checkout-dwim.sh206
-rwxr-xr-xt/t2025-worktree-add.sh287
-rwxr-xr-xt/t2026-worktree-prune.sh96
-rwxr-xr-xt/t2027-worktree-list.sh99
-rwxr-xr-xt/t2030-unresolve-info.sh27
-rwxr-xr-xt/t2050-git-dir-relative.sh4
-rwxr-xr-xt/t2101-update-index-reupdate.sh8
-rwxr-xr-xt/t2102-update-index-symlinks.sh2
-rwxr-xr-xt/t2104-update-index-skip-worktree.sh4
-rwxr-xr-xt/t2105-update-index-gitfile.sh2
-rwxr-xr-xt/t2107-update-index-basic.sh83
-rwxr-xr-xt/t2200-add-update.sh50
-rwxr-xr-xt/t2201-add-update-typechange.sh2
-rwxr-xr-xt/t2202-add-addremove.sh11
-rwxr-xr-xt/t2203-add-intent.sh28
-rwxr-xr-xt/t2204-add-ignored.sh37
-rwxr-xr-xt/t2300-cd-to-toplevel.sh11
-rwxr-xr-xt/t3000-ls-files-others.sh26
-rwxr-xr-xt/t3001-ls-files-others-exclude.sh97
-rwxr-xr-xt/t3004-ls-files-basic.sh56
-rwxr-xr-xt/t3005-ls-files-relative.sh72
-rwxr-xr-xt/t3006-ls-files-long.sh39
-rwxr-xr-xt/t3010-ls-files-killed-modified.sh157
-rwxr-xr-xt/t3020-ls-files-error-unmatch.sh3
-rwxr-xr-xt/t3030-merge-recursive.sh220
-rwxr-xr-xt/t3031-merge-criscross.sh2
-rwxr-xr-xt/t3032-merge-recursive-space-options.sh207
-rwxr-xr-xt/t3033-merge-toplevel.sh152
-rwxr-xr-xt/t3034-merge-recursive-rename-options.sh312
-rwxr-xr-xt/t3040-subprojects-basic.sh144
-rwxr-xr-xt/t3050-subprojects-fetch.sh4
-rwxr-xr-xt/t3060-ls-files-with-tree.sh28
-rwxr-xr-xt/t3070-wildmatch.sh268
-rwxr-xr-xt/t3100-ls-tree-restrict.sh51
-rwxr-xr-xt/t3101-ls-tree-dirname.sh126
-rwxr-xr-xt/t3102-ls-tree-wildcards.sh36
-rwxr-xr-xt/t3103-ls-tree-misc.sh24
-rwxr-xr-xt/t3200-branch.sh816
-rwxr-xr-xt/t3201-branch-contains.sh64
-rwxr-xr-xt/t3202-show-branch-octopus.sh2
-rwxr-xr-xt/t3203-branch-output.sh128
-rwxr-xr-xt/t3210-pack-refs.sh103
-rwxr-xr-xt/t3211-peel-ref.sh73
-rwxr-xr-xt/t3300-funny-names.sh329
-rwxr-xr-xt/t3301-notes.sh1464
-rwxr-xr-xt/t3302-notes-index-expensive.sh132
-rwxr-xr-xt/t3303-notes-subtrees.sh19
-rwxr-xr-xt/t3305-notes-fanout.sh9
-rwxr-xr-xt/t3306-notes-prune.sh11
-rwxr-xr-xt/t3307-notes-man.sh6
-rwxr-xr-xt/t3308-notes-merge.sh368
-rwxr-xr-xt/t3309-notes-merge-auto-resolve.sh726
-rwxr-xr-xt/t3310-notes-merge-manual-resolve.sh587
-rwxr-xr-xt/t3311-notes-merge-fanout.sh436
-rwxr-xr-xt/t3320-notes-merge-worktrees.sh72
-rwxr-xr-xt/t3400-rebase.sh273
-rwxr-xr-xt/t3401-rebase-partial.sh61
-rwxr-xr-xt/t3402-rebase-merge.sh56
-rwxr-xr-xt/t3403-rebase-skip.sh12
-rwxr-xr-xt/t3404-rebase-interactive.sh808
-rwxr-xr-xt/t3405-rebase-malformed.sh32
-rwxr-xr-xt/t3406-rebase-message.sh68
-rwxr-xr-xt/t3407-rebase-abort.sh28
-rwxr-xr-xt/t3408-rebase-multi-line.sh2
-rwxr-xr-xt/t3409-rebase-preserve-merges.sh93
-rwxr-xr-xt/t3410-rebase-preserve-dropped-merges.sh8
-rwxr-xr-xt/t3411-rebase-preserve-around-merges.sh3
-rwxr-xr-xt/t3412-rebase-root.sh13
-rwxr-xr-xt/t3413-rebase-hook.sh6
-rwxr-xr-xt/t3415-rebase-autosquash.sh229
-rwxr-xr-xt/t3417-rebase-whitespace-fix.sh2
-rwxr-xr-xt/t3418-rebase-continue.sh117
-rwxr-xr-xt/t3419-rebase-patch-id.sh100
-rwxr-xr-xt/t3420-rebase-autostash.sh226
-rwxr-xr-xt/t3421-rebase-topology-linear.sh350
-rwxr-xr-xt/t3425-rebase-topology-merges.sh258
-rwxr-xr-xt/t3426-rebase-submodule.sh46
-rwxr-xr-xt/t3427-rebase-subtree.sh119
-rwxr-xr-xt/t3501-revert-cherry-pick.sh57
-rwxr-xr-xt/t3502-cherry-pick-merge.sh2
-rwxr-xr-xt/t3503-cherry-pick-root.sh54
-rwxr-xr-xt/t3504-cherry-pick-rerere.sh4
-rwxr-xr-xt/t3505-cherry-pick-empty.sh76
-rwxr-xr-xt/t3506-cherry-pick-ff.sh18
-rwxr-xr-xt/t3507-cherry-pick-conflict.sh291
-rwxr-xr-xt/t3508-cherry-pick-many-commits.sh99
-rwxr-xr-xt/t3509-cherry-pick-merge-df.sh101
-rwxr-xr-xt/t3510-cherry-pick-sequence.sh514
-rwxr-xr-xt/t3511-cherry-pick-x.sh247
-rwxr-xr-xt/t3512-cherry-pick-submodule.sh13
-rwxr-xr-xt/t3513-revert-submodule.sh32
-rwxr-xr-xt/t3600-rm.sh700
-rwxr-xr-xt/t3700-add.sh133
-rwxr-xr-xt/t3701-add-interactive.sh151
-rwxr-xr-xt/t3702-add-edit.sh7
-rwxr-xr-xt/t3703-add-magic-pathspec.sh58
-rwxr-xr-xt/t3900-i18n-commit.sh71
-rw-r--r--t/t3900/UTF-16.txtbin0 -> 146 bytes
-rwxr-xr-xt/t3901-i18n-patch.sh89
-rwxr-xr-xt/t3902-quoted.sh22
-rwxr-xr-xt/t3903-stash.sh407
-rwxr-xr-xt/t3904-stash-patch.sh80
-rwxr-xr-xt/t3905-stash-include-untracked.sh188
-rwxr-xr-xt/t3906-stash-submodule.sh24
-rwxr-xr-xt/t3910-mac-os-precompose.sh205
-rwxr-xr-xt/t4000-diff-format.sh48
-rwxr-xr-xt/t4001-diff-rename.sh221
-rwxr-xr-xt/t4002-diff-basic.sh17
-rwxr-xr-xt/t4003-diff-rename-1.sh6
-rwxr-xr-xt/t4004-diff-rename-symlink.sh18
-rwxr-xr-xt/t4005-diff-rename-2.sh8
-rwxr-xr-xt/t4006-diff-mode.sh65
-rwxr-xr-xt/t4007-rename-3.sh2
-rwxr-xr-xt/t4008-diff-break-rewrite.sh292
-rwxr-xr-xt/t4009-diff-rename-4.sh8
-rwxr-xr-xt/t4010-diff-pathspec.sh80
-rwxr-xr-xt/t4011-diff-symlink.sh210
-rwxr-xr-xt/t4012-diff-binary.sh150
-rwxr-xr-xt/t4013-diff-various.sh74
-rw-r--r--t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master6
-rw-r--r--t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side8
-rw-r--r--t/t4013/diff.diff-tree_--cc_--patch-with-stat_master6
-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_--pretty=oneline_--root_--patch-with-stat_initial8
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side8
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial8
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial8
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--root_--stat_initial8
-rw-r--r--t/t4013/diff.diff-tree_--root_--patch-with-stat_initial8
-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_--cached38
-rw-r--r--t/t4013/diff.diff_--cached_--_file015
-rw-r--r--t/t4013/diff.diff_--dirstat-by-file_initial_rearrange3
-rw-r--r--t/t4013/diff.diff_--dirstat_initial_rearrange3
-rw-r--r--t/t4013/diff.diff_--patch-with-stat_-r_initial..side8
-rw-r--r--t/t4013/diff.diff_--patch-with-stat_initial..side8
-rw-r--r--t/t4013/diff.diff_--stat_initial..side8
-rw-r--r--t/t4013/diff.diff_-r_--stat_initial..side8
-rw-r--r--t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side8
-rw-r--r--t/t4013/diff.format-patch_--attach_--stdout_initial..master20
-rw-r--r--t/t4013/diff.format-patch_--attach_--stdout_initial..master^12
-rw-r--r--t/t4013/diff.format-patch_--attach_--stdout_initial..side8
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master20
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master20
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_initial..master20
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_initial..master^12
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_initial..master^^6
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_initial..side8
-rw-r--r--t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^22
-rw-r--r--t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master20
-rw-r--r--t/t4013/diff.format-patch_--stdout_--numbered_initial..master20
-rw-r--r--t/t4013/diff.format-patch_--stdout_initial..master20
-rw-r--r--t/t4013/diff.format-patch_--stdout_initial..master^12
-rw-r--r--t/t4013/diff.format-patch_--stdout_initial..side8
-rw-r--r--t/t4013/diff.log_--decorate=full_--all8
-rw-r--r--t/t4013/diff.log_--decorate_--all8
-rw-r--r--t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_12
-rw-r--r--t/t4013/diff.log_--patch-with-stat_master20
-rw-r--r--t/t4013/diff.log_--patch-with-stat_master_--_dir_12
-rw-r--r--t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master34
-rw-r--r--t/t4013/diff.log_--root_--patch-with-stat_--summary_master28
-rw-r--r--t/t4013/diff.log_--root_--patch-with-stat_master28
-rw-r--r--t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master34
-rw-r--r--t/t4013/diff.log_-GF_-p_--pickaxe-all_master27
-rw-r--r--t/t4013/diff.log_-GF_-p_master18
-rw-r--r--t/t4013/diff.log_-GF_master7
-rw-r--r--t/t4013/diff.log_-SF_master_--max-count=02
-rw-r--r--t/t4013/diff.log_-SF_master_--max-count=17
-rw-r--r--t/t4013/diff.log_-SF_master_--max-count=27
-rw-r--r--t/t4013/diff.log_-S_F_master7
-rw-r--r--t/t4013/diff.show_--patch-with-stat_--summary_side8
-rw-r--r--t/t4013/diff.show_--patch-with-stat_side8
-rw-r--r--t/t4013/diff.show_--stat_--summary_side8
-rw-r--r--t/t4013/diff.show_--stat_side8
-rw-r--r--t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_12
-rw-r--r--t/t4013/diff.whatchanged_--patch-with-stat_master20
-rw-r--r--t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_12
-rw-r--r--t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master34
-rw-r--r--t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master28
-rw-r--r--t/t4013/diff.whatchanged_--root_--patch-with-stat_master28
-rw-r--r--t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master34
-rwxr-xr-xt/t4014-format-patch.sh1018
-rwxr-xr-xt/t4015-diff-whitespace.sh893
-rwxr-xr-xt/t4016-diff-quote.sh44
-rwxr-xr-xt/t4017-diff-retval.sh69
-rwxr-xr-xt/t4018-diff-funcname.sh142
-rw-r--r--t/t4018/README18
-rw-r--r--t/t4018/cpp-c++-function4
-rw-r--r--t/t4018/cpp-class-constructor4
-rw-r--r--t/t4018/cpp-class-constructor-mem-init5
-rw-r--r--t/t4018/cpp-class-definition4
-rw-r--r--t/t4018/cpp-class-definition-derived5
-rw-r--r--t/t4018/cpp-class-destructor4
-rw-r--r--t/t4018/cpp-function-returning-global-type4
-rw-r--r--t/t4018/cpp-function-returning-nested5
-rw-r--r--t/t4018/cpp-function-returning-pointer4
-rw-r--r--t/t4018/cpp-function-returning-reference4
-rw-r--r--t/t4018/cpp-gnu-style-function5
-rw-r--r--t/t4018/cpp-namespace-definition4
-rw-r--r--t/t4018/cpp-operator-definition4
-rw-r--r--t/t4018/cpp-skip-access-specifiers8
-rw-r--r--t/t4018/cpp-skip-comment-block9
-rw-r--r--t/t4018/cpp-skip-labels8
-rw-r--r--t/t4018/cpp-struct-definition9
-rw-r--r--t/t4018/cpp-struct-single-line7
-rw-r--r--t/t4018/cpp-template-function-definition4
-rw-r--r--t/t4018/cpp-union-definition4
-rw-r--r--t/t4018/cpp-void-c-function4
-rw-r--r--t/t4018/custom1-pattern17
-rw-r--r--t/t4018/custom2-match-to-end-of-line8
-rw-r--r--t/t4018/custom3-alternation-in-pattern17
-rw-r--r--t/t4018/fountain-scene4
-rw-r--r--t/t4018/java-class-member-function8
-rw-r--r--t/t4018/perl-skip-end-of-heredoc8
-rw-r--r--t/t4018/perl-skip-forward-decl10
-rw-r--r--t/t4018/perl-skip-sub-in-pod18
-rw-r--r--t/t4018/perl-sub-definition4
-rw-r--r--t/t4018/perl-sub-definition-kr-brace4
-rwxr-xr-xt/t4019-diff-wserror.sh147
-rwxr-xr-xt/t4020-diff-external.sh104
-rwxr-xr-xt/t4021-format-patch-numbered.sh2
-rwxr-xr-xt/t4022-diff-rewrite.sh78
-rwxr-xr-xt/t4023-diff-rename-typechange.sh29
-rwxr-xr-xt/t4024-diff-optimize-common.sh2
-rwxr-xr-xt/t4026-color.sh34
-rwxr-xr-xt/t4027-diff-submodule.sh169
-rwxr-xr-xt/t4030-diff-textconv.sh68
-rwxr-xr-xt/t4031-diff-rewrite-binary.sh15
-rwxr-xr-xt/t4033-diff-patience.sh162
-rwxr-xr-xt/t4034-diff-words.sh511
-rw-r--r--t/t4034/ada/expect27
-rw-r--r--t/t4034/ada/post13
-rw-r--r--t/t4034/ada/pre13
-rw-r--r--t/t4034/bibtex/expect15
-rw-r--r--t/t4034/bibtex/post10
-rw-r--r--t/t4034/bibtex/pre9
-rw-r--r--t/t4034/cpp/expect36
-rw-r--r--t/t4034/cpp/post19
-rw-r--r--t/t4034/cpp/pre19
-rw-r--r--t/t4034/csharp/expect35
-rw-r--r--t/t4034/csharp/post18
-rw-r--r--t/t4034/csharp/pre18
-rw-r--r--t/t4034/fortran/expect10
-rw-r--r--t/t4034/fortran/post5
-rw-r--r--t/t4034/fortran/pre5
-rw-r--r--t/t4034/html/expect8
-rw-r--r--t/t4034/html/post3
-rw-r--r--t/t4034/html/pre3
-rw-r--r--t/t4034/java/expect36
-rw-r--r--t/t4034/java/post19
-rw-r--r--t/t4034/java/pre19
-rw-r--r--t/t4034/matlab/expect14
-rw-r--r--t/t4034/matlab/post9
-rw-r--r--t/t4034/matlab/pre9
-rw-r--r--t/t4034/objc/expect35
-rw-r--r--t/t4034/objc/post18
-rw-r--r--t/t4034/objc/pre18
-rw-r--r--t/t4034/pascal/expect35
-rw-r--r--t/t4034/pascal/post18
-rw-r--r--t/t4034/pascal/pre18
-rw-r--r--t/t4034/perl/expect13
-rw-r--r--t/t4034/perl/post22
-rw-r--r--t/t4034/perl/pre22
-rw-r--r--t/t4034/php/expect35
-rw-r--r--t/t4034/php/post18
-rw-r--r--t/t4034/php/pre18
-rw-r--r--t/t4034/python/expect34
-rw-r--r--t/t4034/python/post17
-rw-r--r--t/t4034/python/pre17
-rw-r--r--t/t4034/ruby/expect34
-rw-r--r--t/t4034/ruby/post17
-rw-r--r--t/t4034/ruby/pre17
-rw-r--r--t/t4034/tex/expect9
-rw-r--r--t/t4034/tex/post4
-rw-r--r--t/t4034/tex/pre4
-rwxr-xr-xt/t4035-diff-quiet.sh145
-rwxr-xr-xt/t4036-format-patch-signer-mime.sh2
-rwxr-xr-xt/t4038-diff-combined.sh346
-rwxr-xr-xt/t4039-diff-assume-unchanged.sh11
-rwxr-xr-xt/t4040-whitespace-status.sh12
-rwxr-xr-xt/t4041-diff-submodule-option.sh668
-rwxr-xr-xt/t4042-diff-textconv-caching.sh8
-rwxr-xr-xt/t4043-diff-rename-binary.sh8
-rwxr-xr-xt/t4045-diff-relative.sh73
-rwxr-xr-xt/t4046-diff-unmerged.sh87
-rwxr-xr-xt/t4047-diff-dirstat.sh991
-rwxr-xr-xt/t4048-diff-combined-binary.sh212
-rwxr-xr-xt/t4049-diff-stat-count.sh68
-rwxr-xr-xt/t4050-diff-histogram.sh12
-rwxr-xr-xt/t4051-diff-function-context.sh182
-rw-r--r--t/t4051/appended1.c15
-rw-r--r--t/t4051/appended2.c35
-rw-r--r--t/t4051/dummy.c7
-rw-r--r--t/t4051/hello.c21
-rw-r--r--t/t4051/includes.c20
-rwxr-xr-xt/t4052-stat-output.sh336
-rwxr-xr-xt/t4053-diff-no-index.sh92
-rwxr-xr-xt/t4054-diff-bogus-tree.sh83
-rwxr-xr-xt/t4055-diff-context.sh92
-rwxr-xr-xt/t4056-diff-order.sh127
-rwxr-xr-xt/t4057-diff-combined-paths.sh106
-rwxr-xr-xt/t4058-diff-duplicates.sh79
-rwxr-xr-xt/t4100-apply-stat.sh4
-rw-r--r--t/t4100/t-apply-8.expect2
-rw-r--r--t/t4100/t-apply-9.expect2
-rwxr-xr-xt/t4102-apply-rename.sh9
-rwxr-xr-xt/t4103-apply-binary.sh83
-rwxr-xr-xt/t4104-apply-boundary.sh32
-rwxr-xr-xt/t4107-apply-ignore-whitespace.sh12
-rwxr-xr-xt/t4108-apply-threeway.sh157
-rwxr-xr-xt/t4111-apply-subdir.sh156
-rwxr-xr-xt/t4114-apply-typechange.sh13
-rwxr-xr-xt/t4115-apply-symlink.sh14
-rwxr-xr-xt/t4116-apply-reverse.sh16
-rwxr-xr-xt/t4117-apply-reject.sh74
-rwxr-xr-xt/t4119-apply-config.sh21
-rwxr-xr-xt/t4120-apply-popt.sh67
-rwxr-xr-xt/t4122-apply-symlink-inside.sh125
-rwxr-xr-xt/t4124-apply-ws-rule.sh102
-rwxr-xr-xt/t4127-apply-same-fn.sh18
-rwxr-xr-xt/t4129-apply-samemode.sh7
-rwxr-xr-xt/t4130-apply-criss-cross-rename.sh2
-rwxr-xr-xt/t4131-apply-fake-ancestor.sh2
-rwxr-xr-xt/t4132-apply-removal.sh2
-rwxr-xr-xt/t4133-apply-filenames.sh10
-rwxr-xr-xt/t4134-apply-submodule.sh2
-rwxr-xr-xt/t4135-apply-weird-filenames.sh92
-rw-r--r--t/t4135/.gitignore3
-rw-r--r--t/t4135/add-plain.diff5
-rw-r--r--t/t4135/add-with backslash.diff5
-rw-r--r--t/t4135/add-with quote.diff5
-rw-r--r--t/t4135/add-with spaces.diff5
-rw-r--r--t/t4135/add-with tab.diff5
-rw-r--r--t/t4135/damaged-tz.diff5
-rw-r--r--t/t4135/damaged.diff5
-rw-r--r--t/t4135/diff-plain.diff5
-rw-r--r--t/t4135/diff-with backslash.diff5
-rw-r--r--t/t4135/diff-with quote.diff5
-rw-r--r--t/t4135/diff-with spaces.diff5
-rw-r--r--t/t4135/diff-with tab.diff5
-rw-r--r--t/t4135/funny-tz.diff5
-rw-r--r--t/t4135/git-plain.diff7
-rw-r--r--t/t4135/git-with backslash.diff7
-rw-r--r--t/t4135/git-with quote.diff7
-rw-r--r--t/t4135/git-with spaces.diff7
-rw-r--r--t/t4135/git-with tab.diff7
-rwxr-xr-xt/t4135/make-patches45
-rwxr-xr-xt/t4136-apply-check.sh32
-rwxr-xr-xt/t4137-apply-submodule.sh20
-rwxr-xr-xt/t4138-apply-ws-expansion.sh121
-rwxr-xr-xt/t4139-apply-escape.sh141
-rwxr-xr-xt/t4150-am.sh856
-rwxr-xr-xt/t4151-am-abort.sh136
-rwxr-xr-xt/t4152-am-subjects.sh77
-rwxr-xr-xt/t4153-am-resume-override-opts.sh102
-rwxr-xr-xt/t4200-rerere.sh566
-rwxr-xr-xt/t4201-shortlog.sh46
-rwxr-xr-xt/t4202-log.sh602
-rwxr-xr-xt/t4203-mailmap.sh307
-rwxr-xr-xt/t4204-patch-id.sh142
-rwxr-xr-xt/t4205-log-pretty-formats.sh468
-rwxr-xr-xt/t4207-log-decoration-colors.sh16
-rwxr-xr-xt/t4208-log-magic-pathspec.sh64
-rwxr-xr-xt/t4209-log-pickaxe.sh109
-rwxr-xr-xt/t4210-log-i18n.sh58
-rwxr-xr-xt/t4211-line-log.sh102
-rw-r--r--t/t4211/expect.beginning-of-file43
-rw-r--r--t/t4211/expect.end-of-file62
-rw-r--r--t/t4211/expect.move-support-f80
-rw-r--r--t/t4211/expect.multiple104
-rw-r--r--t/t4211/expect.multiple-overlapping187
-rw-r--r--t/t4211/expect.multiple-superset187
-rw-r--r--t/t4211/expect.parallel-change-f-to-main160
-rw-r--r--t/t4211/expect.simple-f59
-rw-r--r--t/t4211/expect.simple-f-to-main100
-rw-r--r--t/t4211/expect.simple-main68
-rw-r--r--t/t4211/expect.simple-main-to-end70
-rw-r--r--t/t4211/expect.two-ranges102
-rw-r--r--t/t4211/expect.vanishes-early39
-rw-r--r--t/t4211/history.export406
-rwxr-xr-xt/t4212-log-corrupt.sh90
-rwxr-xr-xt/t4213-log-tabexpand.sh105
-rwxr-xr-xt/t4252-am-options.sh2
-rwxr-xr-xt/t4253-am-keep-cr-dos.sh4
-rwxr-xr-xt/t4254-am-corrupt.sh37
-rwxr-xr-xt/t4255-am-submodule.sh93
-rwxr-xr-xt/t4300-merge-tree.sh114
-rwxr-xr-xt/t5000-tar-tree.sh341
-rw-r--r--t/t5000/pax.tarbin0 -> 10240 bytes
-rwxr-xr-xt/t5001-archive-attr.sh23
-rwxr-xr-xt/t5002-archive-attr-pattern.sh84
-rwxr-xr-xt/t5003-archive-zip.sh172
-rw-r--r--t/t5003/infozip-symlinks.zipbin0 -> 328 bytes
-rwxr-xr-xt/t5004-archive-corner-cases.sh158
-rw-r--r--t/t5004/empty-with-pax-header.tarbin0 -> 10240 bytes
-rw-r--r--t/t5004/empty.zipbin0 -> 62 bytes
-rwxr-xr-xt/t5100-mailinfo.sh36
-rw-r--r--t/t5100/embed-from.expect5
-rw-r--r--t/t5100/embed-from.in13
-rw-r--r--t/t5100/info0012--message-id5
-rw-r--r--t/t5100/info00175
-rw-r--r--t/t5100/msg0012--message-id8
-rw-r--r--t/t5100/msg00172
-rw-r--r--t/t5100/patch00012
-rw-r--r--t/t5100/patch00022
-rw-r--r--t/t5100/patch00032
-rw-r--r--t/t5100/patch00054
-rw-r--r--t/t5100/patch00062
-rw-r--r--t/t5100/patch00102
-rw-r--r--t/t5100/patch00112
-rw-r--r--t/t5100/patch0012--message-id30
-rw-r--r--t/t5100/patch00142
-rw-r--r--t/t5100/patch0014--scissors2
-rw-r--r--t/t5100/patch00176
-rw-r--r--t/t5100/quoted-from.expect3
-rw-r--r--t/t5100/quoted-from.in10
-rw-r--r--t/t5100/sample.mbox34
-rwxr-xr-xt/t5150-request-pull.sh49
-rwxr-xr-xt/t5300-pack-object.sh47
-rwxr-xr-xt/t5301-sliding-window.sh18
-rwxr-xr-xt/t5302-pack-index.sh86
-rwxr-xr-xt/t5303-pack-corruption-resilience.sh45
-rwxr-xr-xt/t5304-prune.sh147
-rwxr-xr-xt/t5305-include-tag.sh12
-rwxr-xr-xt/t5308-pack-detect-duplicates.sh80
-rwxr-xr-xt/t5309-pack-delta-cycles.sh77
-rwxr-xr-xt/t5310-pack-bitmaps.sh194
-rwxr-xr-xt/t5311-pack-bitmaps-shallow.sh39
-rwxr-xr-xt/t5312-prune-corruption.sh114
-rwxr-xr-xt/t5313-pack-bounds-checks.sh179
-rwxr-xr-xt/t5400-send-pack.sh92
-rwxr-xr-xt/t5401-update-hooks.sh13
-rwxr-xr-xt/t5403-post-checkout-hook.sh46
-rwxr-xr-xt/t5404-tracking-branches.sh2
-rwxr-xr-xt/t5407-post-rewrite-hook.sh105
-rwxr-xr-xt/t5408-send-pack-stdin.sh92
-rwxr-xr-xt/t5500-fetch-pack.sh432
-rwxr-xr-xt/t5501-fetch-push-alternates.sh66
-rwxr-xr-xt/t5502-quickfetch.sh2
-rwxr-xr-xt/t5503-tagfollow.sh66
-rwxr-xr-xt/t5504-fetch-receive-strict.sh169
-rwxr-xr-xt/t5505-remote.sh978
-rwxr-xr-xt/t5506-remote-groups.sh8
-rwxr-xr-xt/t5507-remote-environment.sh34
-rwxr-xr-xt/t5509-fetch-push-namespaces.sh126
-rwxr-xr-xt/t5510-fetch.sh509
-rwxr-xr-xt/t5511-refspec.sh17
-rwxr-xr-xt/t5512-ls-remote.sh145
-rwxr-xr-xt/t5513-fetch-track.sh2
-rwxr-xr-xt/t5514-fetch-multiple.sh36
-rwxr-xr-xt/t5515-fetch-merge-logic.sh4
-rw-r--r--t/t5515/fetch.br-branches-default6
-rw-r--r--t/t5515/fetch.br-branches-default-merge8
-rw-r--r--t/t5515/fetch.br-branches-default-merge_branches-default8
-rw-r--r--t/t5515/fetch.br-branches-default-octopus8
-rw-r--r--t/t5515/fetch.br-branches-default-octopus_branches-default8
-rw-r--r--t/t5515/fetch.br-branches-default_branches-default6
-rw-r--r--t/t5515/fetch.br-branches-one6
-rw-r--r--t/t5515/fetch.br-branches-one-merge8
-rw-r--r--t/t5515/fetch.br-branches-one-merge_branches-one8
-rw-r--r--t/t5515/fetch.br-branches-one-octopus6
-rw-r--r--t/t5515/fetch.br-branches-one-octopus_branches-one6
-rw-r--r--t/t5515/fetch.br-branches-one_branches-one6
-rw-r--r--t/t5515/fetch.br-config-explicit6
-rw-r--r--t/t5515/fetch.br-config-explicit-merge8
-rw-r--r--t/t5515/fetch.br-config-explicit-merge_config-explicit8
-rw-r--r--t/t5515/fetch.br-config-explicit-octopus8
-rw-r--r--t/t5515/fetch.br-config-explicit-octopus_config-explicit8
-rw-r--r--t/t5515/fetch.br-config-explicit_config-explicit6
-rw-r--r--t/t5515/fetch.br-config-glob6
-rw-r--r--t/t5515/fetch.br-config-glob-merge8
-rw-r--r--t/t5515/fetch.br-config-glob-merge_config-glob8
-rw-r--r--t/t5515/fetch.br-config-glob-octopus10
-rw-r--r--t/t5515/fetch.br-config-glob-octopus_config-glob10
-rw-r--r--t/t5515/fetch.br-config-glob_config-glob6
-rw-r--r--t/t5515/fetch.br-remote-explicit6
-rw-r--r--t/t5515/fetch.br-remote-explicit-merge8
-rw-r--r--t/t5515/fetch.br-remote-explicit-merge_remote-explicit8
-rw-r--r--t/t5515/fetch.br-remote-explicit-octopus8
-rw-r--r--t/t5515/fetch.br-remote-explicit-octopus_remote-explicit8
-rw-r--r--t/t5515/fetch.br-remote-explicit_remote-explicit6
-rw-r--r--t/t5515/fetch.br-remote-glob6
-rw-r--r--t/t5515/fetch.br-remote-glob-merge8
-rw-r--r--t/t5515/fetch.br-remote-glob-merge_remote-glob8
-rw-r--r--t/t5515/fetch.br-remote-glob-octopus10
-rw-r--r--t/t5515/fetch.br-remote-glob-octopus_remote-glob10
-rw-r--r--t/t5515/fetch.br-remote-glob_remote-glob6
-rw-r--r--t/t5515/fetch.br-unconfig6
-rw-r--r--t/t5515/fetch.br-unconfig_--tags_.._.git7
-rw-r--r--t/t5515/fetch.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file6
-rw-r--r--t/t5515/fetch.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file6
-rw-r--r--t/t5515/fetch.br-unconfig_.._.git_tag_tag-one_tag_tag-three6
-rw-r--r--t/t5515/fetch.br-unconfig_branches-default6
-rw-r--r--t/t5515/fetch.br-unconfig_branches-one6
-rw-r--r--t/t5515/fetch.br-unconfig_config-explicit6
-rw-r--r--t/t5515/fetch.br-unconfig_config-glob6
-rw-r--r--t/t5515/fetch.br-unconfig_remote-explicit6
-rw-r--r--t/t5515/fetch.br-unconfig_remote-glob6
-rw-r--r--t/t5515/fetch.master6
-rw-r--r--t/t5515/fetch.master_--tags_.._.git7
-rw-r--r--t/t5515/fetch.master_.._.git_one_tag_tag-one_tag_tag-three-file6
-rw-r--r--t/t5515/fetch.master_.._.git_tag_tag-one-tree_tag_tag-three-file6
-rw-r--r--t/t5515/fetch.master_.._.git_tag_tag-one_tag_tag-three6
-rw-r--r--t/t5515/fetch.master_branches-default6
-rw-r--r--t/t5515/fetch.master_branches-one6
-rw-r--r--t/t5515/fetch.master_config-explicit6
-rw-r--r--t/t5515/fetch.master_config-glob6
-rw-r--r--t/t5515/fetch.master_remote-explicit6
-rw-r--r--t/t5515/fetch.master_remote-glob6
-rwxr-xr-xt/t5516-fetch-push.sh1250
-rwxr-xr-xt/t5517-push-mirror.sh4
-rwxr-xr-xt/t5519-push-alternates.sh14
-rwxr-xr-xt/t5520-pull.sh505
-rwxr-xr-xt/t5521-pull-options.sh89
-rwxr-xr-xt/t5522-pull-symlink.sh16
-rwxr-xr-xt/t5523-push-upstream.sh52
-rwxr-xr-xt/t5524-pull-msg.sh17
-rwxr-xr-xt/t5525-fetch-tagopt.sh54
-rwxr-xr-xt/t5526-fetch-submodules.sh488
-rwxr-xr-xt/t5527-fetch-odd-refs.sh62
-rwxr-xr-xt/t5528-push-default.sh203
-rwxr-xr-xt/t5529-push-errors.sh48
-rwxr-xr-xt/t5530-upload-pack-error.sh18
-rwxr-xr-xt/t5531-deep-submodule-push.sh400
-rwxr-xr-xt/t5532-fetch-proxy.sh46
-rwxr-xr-xt/t5533-push-cas.sh194
-rwxr-xr-xt/t5534-push-signed.sh171
-rwxr-xr-xt/t5535-fetch-push-symref.sh42
-rwxr-xr-xt/t5536-fetch-conflicts.sh100
-rwxr-xr-xt/t5537-fetch-shallow.sh189
-rwxr-xr-xt/t5538-push-shallow.sh123
-rwxr-xr-xt/t5539-fetch-http-shallow.sh77
-rwxr-xr-xt/t5540-http-push-webdav.sh (renamed from t/t5540-http-push.sh)44
-rwxr-xr-xt/t5541-http-push-smart.sh372
-rwxr-xr-xt/t5541-http-push.sh141
-rwxr-xr-xt/t5542-push-http-shallow.sh94
-rwxr-xr-xt/t5543-atomic-push.sh194
-rwxr-xr-xt/t5550-http-fetch-dumb.sh303
-rwxr-xr-xt/t5550-http-fetch.sh102
-rwxr-xr-xt/t5551-http-fetch-smart.sh303
-rwxr-xr-xt/t5551-http-fetch.sh105
-rwxr-xr-xt/t5560-http-backend-noserver.sh29
-rwxr-xr-xt/t5561-http-backend.sh15
-rwxr-xr-xt/t556x_common16
-rwxr-xr-xt/t5570-git-daemon.sh190
-rwxr-xr-xt/t5571-pre-push-hook.sh128
-rwxr-xr-xt/t5572-pull-submodule.sh45
-rwxr-xr-xt/t5600-clone-fail-cleanup.sh12
-rwxr-xr-xt/t5601-clone.sh358
-rwxr-xr-xt/t5602-clone-remote-exec.sh15
-rwxr-xr-xt/t5603-clone-dirname.sh106
-rwxr-xr-xt/t5604-clone-reference.sh224
-rwxr-xr-xt/t5605-clone-local.sh (renamed from t/t5701-clone-local.sh)106
-rwxr-xr-xt/t5606-clone-options.sh (renamed from t/t5702-clone-options.sh)10
-rwxr-xr-xt/t5607-clone-bundle.sh74
-rwxr-xr-xt/t5608-clone-2gb.sh (renamed from t/t5705-clone-2gb.sh)18
-rwxr-xr-xt/t5609-clone-branch.sh (renamed from t/t5706-clone-branch.sh)16
-rwxr-xr-xt/t5610-clone-detached.sh76
-rwxr-xr-xt/t5611-clone-config.sh60
-rwxr-xr-xt/t5612-clone-refspec.sh156
-rwxr-xr-xt/t5613-info-alternate.sh (renamed from t/t5710-info-alternate.sh)6
-rwxr-xr-xt/t5614-clone-submodules.sh70
-rwxr-xr-xt/t5700-clone-reference.sh149
-rwxr-xr-xt/t5704-bundle.sh49
-rwxr-xr-xt/t5800-remote-helpers.sh82
-rwxr-xr-xt/t5801-remote-helpers.sh304
-rwxr-xr-xt/t5802-connect-helper.sh100
-rwxr-xr-xt/t5810-proto-disable-local.sh14
-rwxr-xr-xt/t5811-proto-disable-git.sh20
-rwxr-xr-xt/t5812-proto-disable-http.sh33
-rwxr-xr-xt/t5813-proto-disable-ssh.sh20
-rwxr-xr-xt/t5814-proto-disable-ext.sh18
-rwxr-xr-xt/t5815-submodule-protos.sh43
-rwxr-xr-xt/t5900-repo-selection.sh100
-rwxr-xr-xt/t6000-rev-list-misc.sh103
-rwxr-xr-xt/t6001-rev-list-graft.sh20
-rwxr-xr-xt/t6002-rev-list-bisect.sh90
-rwxr-xr-xt/t6003-rev-list-topo-order.sh101
-rwxr-xr-xt/t6004-rev-list-path-optim.sh69
-rwxr-xr-xt/t6006-rev-list-format.sh401
-rwxr-xr-xt/t6007-rev-list-cherry-pick-file.sh140
-rwxr-xr-xt/t6009-rev-list-parent.sh135
-rwxr-xr-xt/t6010-merge-base.sh377
-rwxr-xr-xt/t6012-rev-list-simplify.sh74
-rwxr-xr-xt/t6014-rev-list-all.sh4
-rwxr-xr-xt/t6015-rev-list-show-all-parents.sh6
-rwxr-xr-xt/t6016-rev-list-graph-simplify-history.sh29
-rwxr-xr-xt/t6017-rev-list-stdin.sh17
-rwxr-xr-xt/t6018-rev-list-glob.sh110
-rwxr-xr-xt/t6019-rev-list-ancestry-path.sh86
-rwxr-xr-xt/t6020-merge-df.sh96
-rwxr-xr-xt/t6021-merge-criss-cross.sh8
-rwxr-xr-xt/t6022-merge-rename.sh943
-rwxr-xr-xt/t6023-merge-file.sh115
-rwxr-xr-xt/t6024-recursive-merge.sh4
-rwxr-xr-xt/t6026-merge-attr.sh23
-rwxr-xr-xt/t6027-merge-binary.sh2
-rwxr-xr-xt/t6028-merge-up-to-date.sh17
-rwxr-xr-xt/t6029-merge-subtree.sh4
-rwxr-xr-xt/t6030-bisect-porcelain.sh448
-rwxr-xr-xt/t6031-merge-filemode.sh100
-rwxr-xr-xt/t6031-merge-recursive.sh60
-rwxr-xr-xt/t6032-merge-large-rename.sh38
-rwxr-xr-xt/t6034-merge-rename-nocruft.sh66
-rwxr-xr-xt/t6035-merge-dir-to-symlink.sh117
-rwxr-xr-xt/t6036-recursive-corner-cases.sh644
-rwxr-xr-xt/t6037-merge-ours-theirs.sh16
-rwxr-xr-xt/t6038-merge-text-auto.sh205
-rwxr-xr-xt/t6039-merge-ignorecase.sh53
-rwxr-xr-xt/t6040-tracking-info.sh164
-rwxr-xr-xt/t6041-bisect-submodule.sh32
-rwxr-xr-xt/t6042-merge-rename-corner-cases.sh578
-rwxr-xr-xt/t6044-merge-unrelated-index-changes.sh153
-rwxr-xr-xt/t6050-replace.sh323
-rwxr-xr-xt/t6060-merge-index.sh100
-rwxr-xr-xt/t6101-rev-parse-parents.sh127
-rwxr-xr-xt/t6110-rev-list-sparse.sh20
-rwxr-xr-xt/t6111-rev-list-treesame.sh193
-rwxr-xr-xt/t6120-describe.sh37
-rwxr-xr-xt/t6130-pathspec-noglob.sh162
-rwxr-xr-xt/t6131-pathspec-icase.sh109
-rwxr-xr-xt/t6132-pathspec-exclude.sh184
-rwxr-xr-xt/t6133-pathspec-rev-dwim.sh48
-rwxr-xr-xt/t6200-fmt-merge-msg.sh484
-rwxr-xr-xt/t6300-for-each-ref.sh382
-rwxr-xr-xt/t6301-for-each-ref-errors.sh56
-rwxr-xr-xt/t6302-for-each-ref-filter.sh330
-rwxr-xr-xt/t6500-gc.sh46
-rwxr-xr-xt/t6501-freshen-objects.sh147
-rwxr-xr-xt/t7001-mv.sh268
-rwxr-xr-xt/t7003-filter-branch.sh178
-rwxr-xr-xt/t7004-tag.sh503
-rw-r--r--t/t7004/pubring.gpgbin1164 -> 0 bytes
-rw-r--r--t/t7004/random_seedbin600 -> 0 bytes
-rw-r--r--t/t7004/secring.gpgbin1237 -> 0 bytes
-rw-r--r--t/t7004/trustdb.gpgbin1280 -> 0 bytes
-rwxr-xr-xt/t7005-editor.sh10
-rwxr-xr-xt/t7006-pager.sh301
-rwxr-xr-xt/t7006/test-terminal.perl58
-rwxr-xr-xt/t7007-show.sh111
-rwxr-xr-xt/t7008-grep-binary.sh98
-rwxr-xr-xt/t7009-filter-branch-null-sha1.sh49
-rwxr-xr-xt/t7011-skip-worktree-reading.sh8
-rwxr-xr-xt/t7012-skip-worktree-writing.sh6
-rwxr-xr-xt/t7030-verify-tag.sh128
-rwxr-xr-xt/t7060-wtstatus.sh194
-rwxr-xr-xt/t7061-wtstatus-ignore.sh274
-rwxr-xr-xt/t7062-wtstatus-ignorecase.sh20
-rwxr-xr-xt/t7063-status-untracked-cache.sh649
-rwxr-xr-xt/t7101-reset-empty-subdirs.sh (renamed from t/t7101-reset.sh)0
-rwxr-xr-xt/t7102-reset.sh154
-rwxr-xr-xt/t7103-reset-bare.sh2
-rwxr-xr-xt/t7104-reset-hard.sh (renamed from t/t7104-reset.sh)2
-rwxr-xr-xt/t7105-reset-patch.sh32
-rwxr-xr-xt/t7106-reset-unborn-branch.sh70
-rwxr-xr-xt/t7110-reset-merge.sh6
-rwxr-xr-xt/t7112-reset-submodule.sh14
-rwxr-xr-xt/t7201-co.sh63
-rwxr-xr-xt/t7300-clean.sh269
-rwxr-xr-xt/t7301-clean-interactive.sh475
-rwxr-xr-xt/t7400-submodule-basic.sh752
-rwxr-xr-xt/t7401-submodule-summary.sh158
-rwxr-xr-xt/t7402-submodule-rebase.sh30
-rwxr-xr-xt/t7403-submodule-sync.sh340
-rwxr-xr-xt/t7405-submodule-merge.sh220
-rwxr-xr-xt/t7406-submodule-update.sh692
-rwxr-xr-xt/t7407-submodule-foreach.sh213
-rwxr-xr-xt/t7408-submodule-reference.sh10
-rwxr-xr-xt/t7409-submodule-detached-work-tree.sh84
-rwxr-xr-xt/t7410-submodule-checkout-to.sh60
-rwxr-xr-xt/t7411-submodule-config.sh153
-rwxr-xr-xt/t7500-commit.sh130
-rwxr-xr-xt/t7500/add-content-and-comment5
-rwxr-xr-xt/t7500/add-whitespaced-content8
-rwxr-xr-xt/t7500/edit-content4
-rwxr-xr-xt/t7501-commit.sh514
-rwxr-xr-xt/t7502-commit.sh365
-rwxr-xr-xt/t7503-pre-commit-hook.sh51
-rwxr-xr-xt/t7504-commit-msg-hook.sh2
-rwxr-xr-xt/t7505-prepare-commit-msg-hook.sh73
-rwxr-xr-xt/t7506-status-submodule.sh120
-rwxr-xr-xt/t7507-commit-verbose.sh100
-rwxr-xr-xt/t7508-status.sh1466
-rwxr-xr-xt/t7509-commit.sh49
-rwxr-xr-xt/t7510-signed-commit.sh213
-rwxr-xr-xt/t7511-status-index.sh50
-rwxr-xr-xt/t7512-status-help.sh946
-rwxr-xr-xt/t7513-interpret-trailers.sh962
-rwxr-xr-xt/t7514-commit-patch.sh34
-rwxr-xr-xt/t7515-status-symlinks.sh43
-rwxr-xr-xt/t7516-commit-races.sh30
-rwxr-xr-xt/t7517-per-repo-email.sh39
-rwxr-xr-xt/t7600-merge.sh627
-rwxr-xr-xt/t7601-merge-pull-config.sh43
-rwxr-xr-xt/t7602-merge-octopus-many.sh46
-rwxr-xr-xt/t7603-merge-reduce-heads.sh50
-rwxr-xr-xt/t7604-merge-custom-message.sh2
-rwxr-xr-xt/t7605-merge-resolve.sh6
-rwxr-xr-xt/t7606-merge-custom.sh96
-rwxr-xr-xt/t7607-merge-overwrite.sh163
-rwxr-xr-xt/t7608-merge-messages.sh8
-rwxr-xr-xt/t7609-merge-co-error-msgs.sh138
-rwxr-xr-xt/t7610-mergetool.sh635
-rwxr-xr-xt/t7611-merge-abort.sh319
-rwxr-xr-xt/t7612-merge-verify-signatures.sh61
-rwxr-xr-xt/t7613-merge-submodule.sh19
-rwxr-xr-xt/t7700-repack.sh44
-rwxr-xr-xt/t7701-repack-unpack-unreachable.sh29
-rwxr-xr-xt/t7702-repack-cyclic-alternate.sh24
-rwxr-xr-xt/t7800-difftool.sh613
-rwxr-xr-xt/t7810-grep.sh962
-rwxr-xr-xt/t7811-grep-open.sh7
-rwxr-xr-xt/t8001-annotate.sh11
-rwxr-xr-xt/t8002-blame.sh75
-rwxr-xr-xt/t8003-blame-corner-cases.sh (renamed from t/t8003-blame.sh)49
-rwxr-xr-xt/t8004-blame-with-conflicts.sh (renamed from t/t8004-blame.sh)2
-rwxr-xr-xt/t8005-blame-i18n.sh36
-rwxr-xr-xt/t8006-blame-textconv.sh91
-rwxr-xr-xt/t8007-cat-file-textconv.sh36
-rwxr-xr-xt/t8008-blame-formats.sh107
-rwxr-xr-xt/t8009-blame-vs-topicbranches.sh36
-rwxr-xr-xt/t9000-addresses.sh27
-rwxr-xr-xt/t9000/test.pl67
-rwxr-xr-xt/t9001-send-email.sh1498
-rwxr-xr-xt/t9002-column.sh180
-rwxr-xr-xt/t9003-help-autocorrect.sh52
-rwxr-xr-xt/t9010-svn-fe.sh1106
-rwxr-xr-xt/t9011-svn-da.sh248
-rwxr-xr-xt/t9020-remote-svn.sh89
-rwxr-xr-xt/t9100-git-svn-basic.sh166
-rwxr-xr-xt/t9101-git-svn-props.sh81
-rwxr-xr-xt/t9102-git-svn-deep-rmdir.sh17
-rwxr-xr-xt/t9103-git-svn-tracked-directory-removed.sh22
-rwxr-xr-xt/t9104-git-svn-follow-parent.sh160
-rwxr-xr-xt/t9105-git-svn-commit-diff.sh13
-rwxr-xr-xt/t9106-git-svn-commit-diff-clobber.sh46
-rwxr-xr-xt/t9107-git-svn-migrate.sh141
-rwxr-xr-xt/t9108-git-svn-glob.sh29
-rwxr-xr-xt/t9109-git-svn-multi-glob.sh41
-rwxr-xr-xt/t9110-git-svn-use-svm-props.sh20
-rwxr-xr-xt/t9111-git-svn-use-svnsync-props.sh18
-rwxr-xr-xt/t9112-git-svn-md5less-file.sh2
-rwxr-xr-xt/t9114-git-svn-dcommit-merge.sh25
-rwxr-xr-xt/t9115-git-svn-dcommit-funky-renames.sh49
-rwxr-xr-xt/t9116-git-svn-log.sh73
-rwxr-xr-xt/t9117-git-svn-init-clone.sh73
-rwxr-xr-xt/t9118-git-svn-funky-branch-names.sh37
-rwxr-xr-xt/t9119-git-svn-info.sh289
-rwxr-xr-xt/t9120-git-svn-clone-with-percent-escapes.sh25
-rwxr-xr-xt/t9123-git-svn-rebuild-with-rewriteroot.sh8
-rwxr-xr-xt/t9124-git-svn-dcommit-auto-props.sh18
-rwxr-xr-xt/t9125-git-svn-multi-glob-branch-names.sh16
-rwxr-xr-xt/t9127-git-svn-partial-rebuild.sh30
-rwxr-xr-xt/t9128-git-svn-cmd-branch.sh36
-rwxr-xr-xt/t9129-git-svn-i18n-commitencoding.sh16
-rwxr-xr-xt/t9130-git-svn-authors-file.sh26
-rwxr-xr-xt/t9131-git-svn-empty-symlink.sh2
-rwxr-xr-xt/t9132-git-svn-broken-symlink.sh4
-rwxr-xr-xt/t9135-git-svn-moved-branch-empty-file.sh2
-rwxr-xr-xt/t9137-git-svn-dcommit-clobber-series.sh36
-rwxr-xr-xt/t9138-git-svn-authors-prog.sh37
-rwxr-xr-xt/t9139-git-svn-non-utf8-commitencoding.sh2
-rwxr-xr-xt/t9140-git-svn-reset.sh2
-rwxr-xr-xt/t9141-git-svn-multiple-branches.sh28
-rwxr-xr-xt/t9142-git-svn-shallow-clone.sh5
-rwxr-xr-xt/t9143-git-svn-gc.sh4
-rwxr-xr-xt/t9145-git-svn-master-branch.sh4
-rwxr-xr-xt/t9146-git-svn-empty-dirs.sh19
-rwxr-xr-xt/t9147-git-svn-include-paths.sh149
-rwxr-xr-xt/t9148-git-svn-propset.sh95
-rwxr-xr-xt/t9150-svk-mergetickets.sh2
-rwxr-xr-x[-rw-r--r--]t/t9150/make-svk-dump0
-rwxr-xr-xt/t9151-svn-mergeinfo.sh22
-rwxr-xr-x[-rw-r--r--]t/t9151/make-svnmerge-dump0
-rwxr-xr-xt/t9153-git-svn-rewrite-uuid.sh4
-rwxr-xr-xt/t9154-git-svn-fancy-glob.sh13
-rwxr-xr-xt/t9155-git-svn-fetch-deleted-tag.sh42
-rwxr-xr-xt/t9156-git-svn-fetch-deleted-tag-2.sh44
-rwxr-xr-xt/t9157-git-svn-fetch-merge.sh58
-rwxr-xr-xt/t9158-git-svn-mergeinfo.sh54
-rwxr-xr-xt/t9159-git-svn-no-parent-mergeinfo.sh41
-rwxr-xr-xt/t9160-git-svn-preserve-empty-dirs.sh153
-rwxr-xr-xt/t9161-git-svn-mergeinfo-push.sh103
-rw-r--r--t/t9161/branches.dump374
-rwxr-xr-xt/t9162-git-svn-dcommit-interactive.sh64
-rwxr-xr-xt/t9163-git-svn-reset-clears-caches.sh78
-rwxr-xr-xt/t9164-git-svn-dcommit-concurrent.sh216
-rwxr-xr-xt/t9165-git-svn-fetch-merge-branch-of-branch.sh60
-rwxr-xr-xt/t9166-git-svn-fetch-merge-branch-of-branch2.sh53
-rwxr-xr-xt/t9167-git-svn-cmd-branch-subproject.sh48
-rwxr-xr-xt/t9168-git-svn-partially-globbed-names.sh223
-rwxr-xr-xt/t9200-git-cvsexportcommit.sh34
-rwxr-xr-xt/t9300-fast-import.sh4299
-rwxr-xr-xt/t9301-fast-import-notes.sh69
-rwxr-xr-xt/t9350-fast-export.sh164
-rwxr-xr-xt/t9351-fast-export-anonymize.sh112
-rwxr-xr-xt/t9400-git-cvsserver-server.sh95
-rwxr-xr-xt/t9401-git-cvsserver-crlf.sh99
-rwxr-xr-xt/t9402-git-cvsserver-refs.sh551
-rwxr-xr-xt/t9500-gitweb-standalone-no-errors.sh322
-rwxr-xr-xt/t9501-gitweb-standalone-http-status.sh89
-rwxr-xr-xt/t9502-gitweb-standalone-parse-output.sh93
-rwxr-xr-xt/t9600-cvsimport.sh82
-rwxr-xr-xt/t9601-cvsimport-vendor-branch.sh21
-rwxr-xr-xt/t9602-cvsimport-branches-tags.sh25
-rwxr-xr-xt/t9603-cvsimport-patchsets.sh11
-rwxr-xr-xt/t9604-cvsimport-timestamps.sh71
-rw-r--r--t/t9604/cvsroot/.gitattributes1
-rw-r--r--t/t9604/cvsroot/CVSROOT/.gitignore2
-rw-r--r--t/t9604/cvsroot/module/a,v264
-rwxr-xr-xt/t9700-perl-git.sh10
-rwxr-xr-xt/t9700/test.pl28
-rwxr-xr-xt/t9800-git-p4-basic.sh264
-rwxr-xr-xt/t9801-git-p4-branch.sh617
-rwxr-xr-xt/t9802-git-p4-filetype.sh340
-rwxr-xr-xt/t9803-git-p4-shell-metachars.sh112
-rwxr-xr-xt/t9804-git-p4-label.sh115
-rwxr-xr-xt/t9805-git-p4-skip-submit-edit.sh105
-rwxr-xr-xt/t9806-git-p4-options.sh276
-rwxr-xr-xt/t9807-git-p4-submit.sh420
-rwxr-xr-xt/t9808-git-p4-chdir.sh90
-rwxr-xr-xt/t9809-git-p4-client-view.sh843
-rwxr-xr-xt/t9810-git-p4-rcs.sh367
-rwxr-xr-xt/t9811-git-p4-label-import.sh267
-rwxr-xr-xt/t9812-git-p4-wildcards.sh218
-rwxr-xr-xt/t9813-git-p4-preserve-users.sh145
-rwxr-xr-xt/t9814-git-p4-rename.sh261
-rwxr-xr-xt/t9815-git-p4-submit-fail.sh429
-rwxr-xr-xt/t9816-git-p4-locked.sh145
-rwxr-xr-xt/t9817-git-p4-exclude.sh71
-rwxr-xr-xt/t9818-git-p4-block.sh145
-rwxr-xr-xt/t9819-git-p4-case-folding.sh60
-rwxr-xr-xt/t9820-git-p4-editor-handling.sh38
-rwxr-xr-xt/t9821-git-p4-path-variations.sh200
-rwxr-xr-xt/t9822-git-p4-path-encoding.sh58
-rwxr-xr-xt/t9823-git-p4-mock-lfs.sh192
-rwxr-xr-xt/t9824-git-p4-git-lfs.sh292
-rwxr-xr-xt/t9825-git-p4-handle-utf16-without-bom.sh50
-rwxr-xr-xt/t9826-git-p4-keep-empty-commits.sh134
-rwxr-xr-xt/t9827-git-p4-change-filetype.sh66
-rwxr-xr-xt/t9828-git-p4-map-user.sh61
-rwxr-xr-xt/t9829-git-p4-jobs.sh99
-rwxr-xr-xt/t9901-git-web--browse.sh63
-rwxr-xr-xt/t9902-completion.sh651
-rwxr-xr-xt/t9903-bash-prompt.sh758
-rw-r--r--t/test-binary-1.png (renamed from t/test9200a.png)bin5660 -> 5660 bytes
-rw-r--r--t/test-binary-2.png (renamed from t/test9200b.png)bin275 -> 275 bytes
-rw-r--r--t/test-lib-functions.sh963
-rw-r--r--t/test-lib.sh1203
-rwxr-xr-xt/test-terminal.perl104
-rw-r--r--t/test4012.pngbin5660 -> 0 bytes
-rwxr-xr-xt/valgrind/analyze.sh8
-rw-r--r--t/valgrind/default.supp6
-rwxr-xr-xt/valgrind/valgrind.sh30
1095 files changed, 105241 insertions, 15884 deletions
diff --git a/t/.gitattributes b/t/.gitattributes
index 1b97c54..2d44088 100644
--- a/t/.gitattributes
+++ b/t/.gitattributes
@@ -1 +1,2 @@
t[0-9][0-9][0-9][0-9]/* -whitespace
+t0110/url-* binary
diff --git a/t/.gitignore b/t/.gitignore
index 7dcbb23..4e731dc 100644
--- a/t/.gitignore
+++ b/t/.gitignore
@@ -1,2 +1,3 @@
/trash directory*
/test-results
+/.prove
diff --git a/t/Git-SVN/00compile.t b/t/Git-SVN/00compile.t
new file mode 100755
index 0000000..c92fee4
--- /dev/null
+++ b/t/Git-SVN/00compile.t
@@ -0,0 +1,14 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 7;
+
+require_ok 'Git::SVN';
+require_ok 'Git::SVN::Utils';
+require_ok 'Git::SVN::Ra';
+require_ok 'Git::SVN::Log';
+require_ok 'Git::SVN::Migration';
+require_ok 'Git::IndexInfo';
+require_ok 'Git::SVN::GlobSpec';
diff --git a/t/Git-SVN/Utils/add_path_to_url.t b/t/Git-SVN/Utils/add_path_to_url.t
new file mode 100755
index 0000000..bfbd878
--- /dev/null
+++ b/t/Git-SVN/Utils/add_path_to_url.t
@@ -0,0 +1,27 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More 'no_plan';
+
+use Git::SVN::Utils qw(
+ add_path_to_url
+);
+
+# A reference cannot be a hash key, so we use an array.
+my @tests = (
+ ["http://x.com", "bar"] => 'http://x.com/bar',
+ ["http://x.com", ""] => 'http://x.com',
+ ["http://x.com/foo/", undef] => 'http://x.com/foo/',
+ ["http://x.com/foo/", "/bar/baz/"] => 'http://x.com/foo/bar/baz/',
+ ["http://x.com", 'per%cent'] => 'http://x.com/per%25cent',
+);
+
+while(@tests) {
+ my($have, $want) = splice @tests, 0, 2;
+
+ my $args = join ", ", map { qq['$_'] } map { defined($_) ? $_ : 'undef' } @$have;
+ my $name = "add_path_to_url($args) eq $want";
+ is add_path_to_url(@$have), $want, $name;
+}
diff --git a/t/Git-SVN/Utils/can_compress.t b/t/Git-SVN/Utils/can_compress.t
new file mode 100755
index 0000000..d7b49b8
--- /dev/null
+++ b/t/Git-SVN/Utils/can_compress.t
@@ -0,0 +1,11 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More 'no_plan';
+
+use Git::SVN::Utils qw(can_compress);
+
+# !! is the "convert this to boolean" operator.
+is !!can_compress(), !!eval { require Compress::Zlib };
diff --git a/t/Git-SVN/Utils/canonicalize_url.t b/t/Git-SVN/Utils/canonicalize_url.t
new file mode 100755
index 0000000..05795ab
--- /dev/null
+++ b/t/Git-SVN/Utils/canonicalize_url.t
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+
+# Test our own home rolled URL canonicalizer. Test the private one
+# directly because we can't predict what the SVN API is doing to do.
+
+use strict;
+use warnings;
+
+use Test::More 'no_plan';
+
+use Git::SVN::Utils;
+my $canonicalize_url = \&Git::SVN::Utils::_canonicalize_url_ourselves;
+
+my %tests = (
+ "http://x.com" => "http://x.com",
+ "http://x.com/" => "http://x.com",
+ "http://x.com/foo/bar" => "http://x.com/foo/bar",
+ "http://x.com//foo//bar//" => "http://x.com/foo/bar",
+ "http://x.com/ /%/" => "http://x.com/%20%20/%25",
+);
+
+for my $arg (keys %tests) {
+ my $want = $tests{$arg};
+
+ is $canonicalize_url->($arg), $want, "canonicalize_url('$arg') => $want";
+}
diff --git a/t/Git-SVN/Utils/collapse_dotdot.t b/t/Git-SVN/Utils/collapse_dotdot.t
new file mode 100755
index 0000000..1da1cce
--- /dev/null
+++ b/t/Git-SVN/Utils/collapse_dotdot.t
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More 'no_plan';
+
+use Git::SVN::Utils;
+my $collapse_dotdot = \&Git::SVN::Utils::_collapse_dotdot;
+
+my %tests = (
+ "foo/bar/baz" => "foo/bar/baz",
+ ".." => "..",
+ "foo/.." => "",
+ "/foo/bar/../../baz" => "/baz",
+ "deeply/.././deeply/nested" => "./deeply/nested",
+);
+
+for my $arg (keys %tests) {
+ my $want = $tests{$arg};
+
+ is $collapse_dotdot->($arg), $want, "_collapse_dotdot('$arg') => $want";
+}
diff --git a/t/Git-SVN/Utils/fatal.t b/t/Git-SVN/Utils/fatal.t
new file mode 100755
index 0000000..49e1438
--- /dev/null
+++ b/t/Git-SVN/Utils/fatal.t
@@ -0,0 +1,34 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More 'no_plan';
+
+BEGIN {
+ # Override exit at BEGIN time before Git::SVN::Utils is loaded
+ # so it will see our local exit later.
+ *CORE::GLOBAL::exit = sub(;$) {
+ return @_ ? CORE::exit($_[0]) : CORE::exit();
+ };
+}
+
+use Git::SVN::Utils qw(fatal);
+
+# fatal()
+{
+ # Capture the exit code and prevent exit.
+ my $exit_status;
+ no warnings 'redefine';
+ local *CORE::GLOBAL::exit = sub { $exit_status = $_[0] || 0 };
+
+ # Trap fatal's message to STDERR
+ my $stderr;
+ close STDERR;
+ ok open STDERR, ">", \$stderr;
+
+ fatal "Some", "Stuff", "Happened";
+
+ is $stderr, "Some Stuff Happened\n";
+ is $exit_status, 1;
+}
diff --git a/t/Git-SVN/Utils/join_paths.t b/t/Git-SVN/Utils/join_paths.t
new file mode 100755
index 0000000..d4488e7
--- /dev/null
+++ b/t/Git-SVN/Utils/join_paths.t
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More 'no_plan';
+
+use Git::SVN::Utils qw(
+ join_paths
+);
+
+# A reference cannot be a hash key, so we use an array.
+my @tests = (
+ [] => '',
+ ["/x.com", "bar"] => '/x.com/bar',
+ ["x.com", ""] => 'x.com',
+ ["/x.com/foo/", undef, "bar"] => '/x.com/foo/bar',
+ ["x.com/foo/", "/bar/baz/"] => 'x.com/foo/bar/baz/',
+ ["foo", "bar"] => 'foo/bar',
+ ["/foo/bar", "baz", "/biff"] => '/foo/bar/baz/biff',
+ ["", undef, "."] => '.',
+ [] => '',
+
+);
+
+while(@tests) {
+ my($have, $want) = splice @tests, 0, 2;
+
+ my $args = join ", ", map { qq['$_'] } map { defined($_) ? $_ : 'undef' } @$have;
+ my $name = "join_paths($args) eq '$want'";
+ is join_paths(@$have), $want, $name;
+}
diff --git a/t/Makefile b/t/Makefile
index cf5f9e2..18e2b28 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -6,46 +6,83 @@
-include ../config.mak.autogen
-include ../config.mak
-#GIT_TEST_OPTS=--verbose --debug
+#GIT_TEST_OPTS = --verbose --debug
SHELL_PATH ?= $(SHELL)
+PERL_PATH ?= /usr/bin/perl
TAR ?= $(TAR)
RM ?= rm -f
+PROVE ?= prove
+DEFAULT_TEST_TARGET ?= test
+TEST_LINT ?= test-lint
+
+ifdef TEST_OUTPUT_DIRECTORY
+TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results
+else
+TEST_RESULTS_DIRECTORY = test-results
+endif
# Shell quote;
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY))
+
+T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
+TGITWEB = $(sort $(wildcard t95[0-9][0-9]-*.sh))
+THELPERS = $(sort $(filter-out $(T),$(wildcard *.sh)))
-T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
-TSVN = $(wildcard t91[0-9][0-9]-*.sh)
+all: $(DEFAULT_TEST_TARGET)
-all: pre-clean
+test: pre-clean $(TEST_LINT)
$(MAKE) aggregate-results-and-cleanup
+prove: pre-clean $(TEST_LINT)
+ @echo "*** prove ***"; $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
+ $(MAKE) clean-except-prove-cache
+
$(T):
- @echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+ @echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
pre-clean:
- $(RM) -r test-results
+ $(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
-clean:
- $(RM) -r 'trash directory'.* test-results
- $(RM) t????/cvsroot/CVSROOT/?*
+clean-except-prove-cache:
+ $(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'
$(RM) -r valgrind/bin
+clean: clean-except-prove-cache
+ $(RM) .prove
+
+test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax
+
+test-lint-duplicates:
+ @dups=`echo $(T) | tr ' ' '\n' | sed 's/-.*//' | sort | uniq -d` && \
+ test -z "$$dups" || { \
+ echo >&2 "duplicate test numbers:" $$dups; exit 1; }
+
+test-lint-executable:
+ @bad=`for i in $(T); do test -x "$$i" || echo $$i; done` && \
+ test -z "$$bad" || { \
+ echo >&2 "non-executable tests:" $$bad; exit 1; }
+
+test-lint-shell-syntax:
+ @'$(PERL_PATH_SQ)' check-non-portable-shell.pl $(T) $(THELPERS)
+
aggregate-results-and-cleanup: $(T)
$(MAKE) aggregate-results
$(MAKE) clean
aggregate-results:
- for f in test-results/t*-*.counts; do \
+ for f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \
echo "$$f"; \
done | '$(SHELL_PATH_SQ)' ./aggregate-results.sh
-# 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
+gitweb-test:
+ $(MAKE) $(TGITWEB)
valgrind:
- GIT_TEST_OPTS=--valgrind $(MAKE)
+ $(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
+
+perf:
+ $(MAKE) -C perf/ all
-.PHONY: pre-clean $(T) aggregate-results clean valgrind
+.PHONY: pre-clean $(T) aggregate-results clean valgrind perf
diff --git a/t/README b/t/README
index 0d1183c..76a0daa 100644
--- a/t/README
+++ b/t/README
@@ -50,6 +50,12 @@ prove and other harnesses come with a lot of useful options. The
# Repeat until no more failures
$ prove -j 15 --state=failed,save ./t[0-9]*.sh
+You can give DEFAULT_TEST_TARGET=prove on the make command (or define it
+in config.mak) to cause "make test" to run tests under prove.
+GIT_PROVE_OPTS can be used to pass additional options, e.g.
+
+ $ make DEFAULT_TEST_TARGET=prove GIT_PROVE_OPTS='--timer --jobs 16' test
+
You can also run each test individually from command line, like this:
$ sh ./t3010-ls-files-killed-modified.sh
@@ -65,33 +71,82 @@ 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".
+-v::
--verbose::
This makes the test more verbose. Specifically, the
command being run and their output if any are also
output.
+--verbose-only=<pattern>::
+ Like --verbose, but the effect is limited to tests with
+ numbers matching <pattern>. The number matched against is
+ simply the running count of the test within the file.
+
+-x::
+ Turn on shell tracing (i.e., `set -x`) during the tests
+ themselves. Implies `--verbose`. Note that in non-bash shells,
+ this can cause failures in some tests which redirect and test
+ the output of shell functions. Use with caution.
+
+-d::
--debug::
This may help the person who is developing a new test.
It causes the command defined with test_debug to run.
+ The "trash" directory (used to store all temporary data
+ during testing) is not deleted even if there are no
+ failed tests so that you can inspect its contents after
+ the test finished.
+-i::
--immediate::
This causes the test to immediately exit upon the first
- failed test.
+ failed test. Cleanup commands requested with
+ test_when_finished are not executed if the test failed,
+ in order to keep the state for inspection by the tester
+ to diagnose the bug.
+-l::
--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.
+-r::
+--run=<test-selector>::
+ Run only the subset of tests indicated by
+ <test-selector>. See section "Skipping Tests" below for
+ <test-selector> syntax.
+
+--valgrind=<tool>::
+ Execute all Git binaries under valgrind tool <tool> and exit
+ with status 126 on errors (just like regular tests, this will
+ only stop the test script when running under -i).
Since it makes no sense to run the tests with --valgrind and
not see any output, this option implies --verbose. For
convenience, it also implies --tee.
+ <tool> defaults to 'memcheck', just like valgrind itself.
+ Other particularly useful choices include 'helgrind' and
+ 'drd', but you may use any tool recognized by your valgrind
+ installation.
+
+ As a special case, <tool> can be 'memcheck-fast', which uses
+ memcheck but disables --track-origins. Use this if you are
+ running tests in bulk, to see if there are _any_ memory
+ issues.
+
+ Note that memcheck is run with the option --leak-check=no,
+ as the git process is short-lived and some errors are not
+ interesting. In order to run a single command under the same
+ conditions manually, you should set GIT_VALGRIND to point to
+ the 't/valgrind/' directory and use the commands under
+ 't/valgrind/bin/'.
+
+--valgrind-only=<pattern>::
+ Like --valgrind, but the effect is limited to tests with
+ numbers matching <pattern>. The number matched against is
+ simply the running count of the test within the file.
+
--tee::
In addition to printing the test output to the terminal,
write it to files named 't/test-results/$TEST_NAME.out'.
@@ -113,6 +168,16 @@ appropriately before running "make".
Using this option with a RAM-based filesystem (such as tmpfs)
can massively speed up the test suite.
+--chain-lint::
+--no-chain-lint::
+ If --chain-lint is enabled, the test harness will check each
+ test to make sure that it properly "&&-chains" all commands (so
+ that a failure in the middle does not go unnoticed by the final
+ exit code of the test). This check is performed in addition to
+ running the tests themselves. You may also enable or disable
+ this feature by setting the GIT_TEST_CHAIN_LINT environment
+ variable to "1" or "0", respectively.
+
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
@@ -148,10 +213,77 @@ 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.
+For an individual test suite --run could be used to specify that
+only some tests should be run or that some tests should be
+excluded from a run.
+
+The argument for --run is a list of individual test numbers or
+ranges with an optional negation prefix that define what tests in
+a test suite to include in the run. A range is two numbers
+separated with a dash and matches a range of tests with both ends
+been included. You may omit the first or the second number to
+mean "from the first test" or "up to the very last test"
+respectively.
+
+Optional prefix of '!' means that the test or a range of tests
+should be excluded from the run.
+
+If --run starts with an unprefixed number or range the initial
+set of tests to run is empty. If the first item starts with '!'
+all the tests are added to the initial set. After initial set is
+determined every test number or range is added or excluded from
+the set one by one, from left to right.
+
+Individual numbers or ranges could be separated either by a space
+or a comma.
+
+For example, to run only tests up to a specific test (21), one
+could do this:
+
+ $ sh ./t9200-git-cvsexport-commit.sh --run='1-21'
+
+or this:
+
+ $ sh ./t9200-git-cvsexport-commit.sh --run='-21'
+
+Common case is to run several setup tests (1, 2, 3) and then a
+specific test (21) that relies on that setup:
+
+ $ sh ./t9200-git-cvsexport-commit.sh --run='1 2 3 21'
+
+or:
+
+ $ sh ./t9200-git-cvsexport-commit.sh --run=1,2,3,21
+
+or:
+
+ $ sh ./t9200-git-cvsexport-commit.sh --run='-3 21'
+
+As noted above, the test set is built going though items left to
+right, so this:
+
+ $ sh ./t9200-git-cvsexport-commit.sh --run='1-4 !3'
+
+will run tests 1, 2, and 4. Items that comes later have higher
+precendence. It means that this:
+
+ $ sh ./t9200-git-cvsexport-commit.sh --run='!3 1-4'
+
+would just run tests from 1 to 4, including 3.
+
+You may use negation with ranges. The following will run all
+test in the test suite except from 7 up to 11:
+
+ $ sh ./t9200-git-cvsexport-commit.sh --run='!7-11'
+
+Some tests in a test suite rely on the previous tests performing
+certain actions, specifically some tests are designated as
+"setup" test, so you cannot _arbitrarily_ disable one test and
+expect the rest to function correctly.
+
+--run is mostly useful when you want to focus on a specific test
+and know what setup is needed for it. Or when you want to run
+everything up to a certain test.
Naming Tests
@@ -184,7 +316,7 @@ 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
+top-level test script and tries to run all of them. 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.
@@ -259,14 +391,28 @@ Do:
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:
+ you must ignore the return value of something, consider using a
+ helper function (e.g. use sane_unset instead of unset, in order
+ to avoid unportable return value for unsetting a variable that was
+ already unset), or prepending the command with test_might_fail or
+ test_must_fail.
- unset HLAGH;
- git merge hla &&
- git push gh &&
- test ...
+ - Check the test coverage for your tests. See the "Test coverage"
+ below.
+
+ Don't blindly follow test coverage metrics; if a new function you added
+ doesn't have any coverage, then you're probably doing something wrong,
+ but having 100% coverage doesn't necessarily mean that you tested
+ everything.
+
+ Tests that are likely to smoke out future regressions are better
+ than tests that just inflate the coverage metrics.
+
+ - When a test checks for an absolute path that a git command generated,
+ construct the expected value using $(pwd) rather than $PWD,
+ $TEST_DIRECTORY, or $TRASH_DIRECTORY. It makes a difference on
+ Windows, where the shell (MSYS bash) mangles absolute path names.
+ For details, see the commit message of 4114156ae9.
Don't:
@@ -276,6 +422,33 @@ Don't:
Use test_done instead if you need to stop the tests early (see
"Skipping tests" below).
+ - use '! git cmd' when you want to make sure the git command exits
+ with failure in a controlled way by calling "die()". Instead,
+ use 'test_must_fail git cmd'. This will signal a failure if git
+ dies in an unexpected way (e.g. segfault).
+
+ On the other hand, don't use test_must_fail for running regular
+ platform commands; just use '! cmd'. We are not in the business
+ of verifying that the world given to us sanely works.
+
+ - use perl without spelling it as "$PERL_PATH". This is to help our
+ friends on Windows where the platform Perl often adds CR before
+ the end of line, and they bundle Git with a version of Perl that
+ does not do so, whose path is specified with $PERL_PATH. Note that we
+ provide a "perl" function which uses $PERL_PATH under the hood, so
+ you do not need to worry when simply running perl in the test scripts
+ (but you do, for example, on a shebang line or in a sub script
+ created via "write_script").
+
+ - use sh without spelling it as "$SHELL_PATH", when the script can
+ be misinterpreted by broken platform shell (e.g. Solaris).
+
+ - chdir around in tests. It is not sufficient to chdir to
+ somewhere and then chdir back to the original location later in
+ the test, as any intermediate step can fail and abort the test,
+ causing the next test to start in an unexpected directory. Do so
+ inside a subshell if necessary.
+
- Break the TAP output
The raw output from your test may be interpreted by a TAP harness. TAP
@@ -307,9 +480,21 @@ Keep in mind:
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 you need to skip tests you should do so by using the three-arg form
+of the test_* functions (see the "Test harness library" section
+below), e.g.:
+
+ test_expect_success PERL 'I need Perl' '
+ perl -e "hlagh() if unf_unf()"
+ '
+
+The advantage of skipping tests like this is that platforms that don't
+have the PERL and other optional dependencies get an indication of how
+many tests they're missing.
+
+If the test code is too hairy for that (i.e. does a lot of setup work
+outside test assertions) you can also skip all remaining tests by
+setting skip_all and immediately call test_done:
if ! test_have_prereq PERL
then
@@ -317,6 +502,9 @@ be used as an explanation for why the test was skipped. for instance:
test_done
fi
+The string you give to skip_all will be used as an explanation for why
+the test was skipped.
+
End with test_done
------------------
@@ -333,7 +521,7 @@ library for your script to use.
- test_expect_success [<prereq>] <message> <script>
- Usually takes two strings as parameter, and evaluates the
+ Usually takes two strings as parameters, and evaluates the
<script>. If it yields success, test is considered
successful. <message> should state what it is testing.
@@ -344,12 +532,18 @@ library for your script to use.
'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
+ prerequisite; see the test_set_prereq and test_have_prereq
documentation below:
test_expect_success TTY 'git --paginate rev-list uses a pager' \
' ... '
+ You can also supply a comma-separated list of prerequisites, in the
+ rare case where your test depends on more than one:
+
+ test_expect_success PERL,PYTHON 'yo dawg' \
+ ' test $(perl -E 'print eval "1 +" . qx[python -c "print 2"]') == "4" '
+
- test_expect_failure [<prereq>] <message> <script>
This is NOT the opposite of test_expect_success, but is used
@@ -362,13 +556,6 @@ library for your script to use.
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
@@ -376,6 +563,11 @@ library for your script to use.
argument. This is primarily meant for use during the
development of a new test script.
+ - debug <git-command>
+
+ Run a git command inside a debugger. This is primarily meant for
+ use when debugging a failing test script.
+
- test_done
Your test script must have test_done at the end. Its purpose
@@ -385,7 +577,7 @@ library for your script to use.
- test_tick
Make commit and tag names consistent by setting the author and
- committer times to defined stated. Subsequent calls will
+ committer times to defined state. Subsequent calls will
advance the times by a fixed amount.
- test_commit <message> [<filename> [<contents>]]
@@ -401,16 +593,17 @@ library for your script to use.
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
+ - test_set_prereq <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-lib will set some prerequisites for you, see the
+ "Prerequisites" section below for a full list of these.
+
+ 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
+ - test_have_prereq <prereq>
Check if we have a prerequisite previously set with
test_set_prereq. The most common use of this directly is to skip
@@ -430,7 +623,7 @@ library for your script to use.
test_external \
'GitwebCache::*FileCache*' \
- "$PERL_PATH" "$TEST_DIRECTORY"/t9503/test_cache_interface.pl
+ perl "$TEST_DIRECTORY"/t9503/test_cache_interface.pl
If the test is outputting its own TAP you should set the
test_external_has_tap variable somewhere before calling the first
@@ -446,7 +639,16 @@ library for your script to use.
test_external_without_stderr \
'Perl API' \
- "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl
+ perl "$TEST_DIRECTORY"/t9700/test.pl
+
+ - test_expect_code <exit-code> <command>
+
+ Run a command and ensure that it exits with the given exit code.
+ For example:
+
+ test_expect_success 'Merge with d/f conflicts' '
+ test_expect_code 1 git merge "merge msg" B master
+ '
- test_must_fail <git-command>
@@ -467,6 +669,18 @@ library for your script to use.
<expected> file. This behaves like "cmp" but produces more
helpful output when the test is run with "-v" option.
+ - test_line_count (= | -lt | -ge | ...) <length> <file>
+
+ Check whether a file has the length it is expected to.
+
+ - test_path_is_file <path> [<diagnosis>]
+ test_path_is_dir <path> [<diagnosis>]
+ test_path_is_missing <path> [<diagnosis>]
+
+ Check if the named path is a file, if the named path is a
+ directory, or if the named path does not exist, respectively,
+ and fail otherwise, showing the <diagnosis> text.
+
- test_when_finished <script>
Prepend <script> to a list of commands to run to clean up
@@ -481,6 +695,119 @@ library for your script to use.
...
'
+ - test_write_lines <lines>
+
+ Write <lines> on standard output, one line per argument.
+ Useful to prepare multi-line files in a compact form.
+
+ Example:
+
+ test_write_lines a b c d e f g >foo
+
+ Is a more compact equivalent of:
+ cat >foo <<-EOF
+ a
+ b
+ c
+ d
+ e
+ f
+ g
+ EOF
+
+
+ - test_pause
+
+ This command is useful for writing and debugging tests and must be
+ removed before submitting. It halts the execution of the test and
+ spawns a shell in the trash directory. Exit the shell to continue
+ the test. Example:
+
+ test_expect_success 'test' '
+ git do-something >actual &&
+ test_pause &&
+ test_cmp expected actual
+ '
+
+ - test_ln_s_add <path1> <path2>
+
+ This function helps systems whose filesystem does not support symbolic
+ links. Use it to add a symbolic link entry to the index when it is not
+ important that the file system entry is a symbolic link, i.e., instead
+ of the sequence
+
+ ln -s foo bar &&
+ git add bar
+
+ Sometimes it is possible to split a test in a part that does not need
+ the symbolic link in the file system and a part that does; then only
+ the latter part need be protected by a SYMLINKS prerequisite (see below).
+
+Prerequisites
+-------------
+
+These are the prerequisites that the test library predefines with
+test_have_prereq.
+
+See the prereq argument to the test_* functions in the "Test harness
+library" section above and the "test_have_prereq" function for how to
+use these, and "test_set_prereq" for how to define your own.
+
+ - PYTHON
+
+ Git wasn't compiled with NO_PYTHON=YesPlease. Wrap any tests that
+ need Python with this.
+
+ - PERL
+
+ Git wasn't compiled with NO_PERL=YesPlease.
+
+ Even without the PERL prerequisite, tests can assume there is a
+ usable perl interpreter at $PERL_PATH, though it need not be
+ particularly modern.
+
+ - POSIXPERM
+
+ The filesystem supports POSIX style permission bits.
+
+ - BSLASHPSPEC
+
+ Backslashes in pathspec are not directory separators. This is not
+ set on Windows. See 6fd1106a for details.
+
+ - EXECKEEPSPID
+
+ The process retains the same pid across exec(2). See fb9a2bea for
+ details.
+
+ - PIPE
+
+ The filesystem we're on supports creation of FIFOs (named pipes)
+ via mkfifo(1).
+
+ - SYMLINKS
+
+ The filesystem we're on supports symbolic links. E.g. a FAT
+ filesystem doesn't support these. See 704a3143 for details.
+
+ - SANITY
+
+ Test is not run by root user, and an attempt to write to an
+ unwritable file is expected to fail correctly.
+
+ - LIBPCRE
+
+ Git was compiled with USE_LIBPCRE=YesPlease. Wrap any tests
+ that use git-grep --perl-regexp or git-grep -P in these.
+
+ - CASE_INSENSITIVE_FS
+
+ Test is run on a case insensitive file system.
+
+ - UTF8_NFD_TO_NFC
+
+ Test is run on a filesystem which converts decomposed utf-8 (nfd)
+ to precomposed utf-8 (nfc).
Tips for Writing Tests
----------------------
@@ -508,3 +835,42 @@ 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.
+
+Test coverage
+-------------
+
+You can use the coverage tests to find code paths that are not being
+used or properly exercised yet.
+
+To do that, run the coverage target at the top-level (not in the t/
+directory):
+
+ make coverage
+
+That'll compile Git with GCC's coverage arguments, and generate a test
+report with gcov after the tests finish. Running the coverage tests
+can take a while, since running the tests in parallel is incompatible
+with GCC's coverage mode.
+
+After the tests have run you can generate a list of untested
+functions:
+
+ make coverage-untested-functions
+
+You can also generate a detailed per-file HTML report using the
+Devel::Cover module. To install it do:
+
+ # On Debian or Ubuntu:
+ sudo aptitude install libdevel-cover-perl
+
+ # From the CPAN with cpanminus
+ curl -L http://cpanmin.us | perl - --sudo --self-upgrade
+ cpanm --sudo Devel::Cover
+
+Then, at the top-level:
+
+ make cover_db_html
+
+That'll generate a detailed cover report in the "cover_db_html"
+directory, which you can then copy to a webserver, or inspect locally
+in a browser.
diff --git a/t/aggregate-results.sh b/t/aggregate-results.sh
index d206b7c..7913e20 100755
--- a/t/aggregate-results.sh
+++ b/t/aggregate-results.sh
@@ -1,5 +1,6 @@
#!/bin/sh
+failed_tests=
fixed=0
success=0
failed=0
@@ -18,7 +19,13 @@ do
success)
success=$(($success + $value)) ;;
failed)
- failed=$(($failed + $value)) ;;
+ failed=$(($failed + $value))
+ if test $value != 0
+ then
+ testnum=$(expr "$file" : 'test-results/\(t[0-9]*\)-')
+ failed_tests="$failed_tests $testnum"
+ fi
+ ;;
broken)
broken=$(($broken + $value)) ;;
total)
@@ -27,6 +34,11 @@ do
done <"$file"
done
+if test -n "$failed_tests"
+then
+ printf "\nfailed test(s):$failed_tests\n\n"
+fi
+
printf "%-8s%d\n" fixed $fixed
printf "%-8s%d\n" success $success
printf "%-8s%d\n" failed $failed
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 396b965..093832f 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -1,126 +1,576 @@
# This file isn't used as a test script directly, instead it is
-# sourced from t8001-annotate.sh and t8001-blame.sh.
+# sourced from t8001-annotate.sh and t8002-blame.sh.
+
+if test_have_prereq MINGW
+then
+ sanitize_L () {
+ echo "$1" | sed 'sX\(^-L\|,\)\^\?/X&\\;*Xg'
+ }
+else
+ sanitize_L () {
+ echo "$1"
+ }
+fi
check_count () {
- head=
- case "$1" in -h) head="$2"; shift; shift ;; esac
- echo "$PROG file $head" >&4
- $PROG file $head >.result || return 1
- cat .result | perl -e '
+ head= &&
+ file='file' &&
+ options= &&
+ while :
+ do
+ case "$1" in
+ -h) head="$2"; shift; shift ;;
+ -f) file="$2"; shift; shift ;;
+ -L*) options="$options $(sanitize_L "$1")"; shift ;;
+ -*) options="$options $1"; shift ;;
+ *) break ;;
+ esac
+ done &&
+ echo "$PROG $options $file $head" >&4 &&
+ $PROG $options $file $head >actual &&
+ perl -e '
my %expect = (@ARGV);
- my %count = ();
+ my %count = map { $_ => 0 } keys %expect;
while (<STDIN>) {
if (/^[0-9a-f]+\t\(([^\t]+)\t/) {
my $author = $1;
for ($author) { s/^\s*//; s/\s*$//; }
- if (exists $expect{$author}) {
- $count{$author}++;
- }
+ $count{$author}++;
}
}
my $bad = 0;
while (my ($author, $count) = each %count) {
my $ok;
- if ($expect{$author} != $count) {
+ my $value = 0;
+ $value = $expect{$author} if defined $expect{$author};
+ if ($value != $count) {
$bad = 1;
$ok = "bad";
}
else {
$ok = "good";
}
- print STDERR "Author $author (expected $expect{$author}, attributed $count) $ok\n";
+ print STDERR "Author $author (expected $value, attributed $count) $ok\n";
}
exit($bad);
- ' "$@"
+ ' "$@" <actual
}
-test_expect_success \
- 'prepare reference tree' \
- 'echo "1A quick brown fox jumps over the" >file &&
- echo "lazy dog" >>file &&
- git add file
- GIT_AUTHOR_NAME="A" git 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'
+test_expect_success 'setup A lines' '
+ echo "1A quick brown fox jumps over the" >file &&
+ echo "lazy dog" >>file &&
+ git add file &&
+ GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" \
+ git commit -a -m "Initial."
+'
+
+test_expect_success 'blame 1 author' '
+ check_count A 2
+'
+
+test_expect_success 'blame by tag objects' '
+ git tag -m "test tag" testTag &&
+ git tag -m "test tag #2" testTag2 testTag &&
+ check_count -h testTag A 2 &&
+ check_count -h testTag2 A 2
+'
+
+test_expect_success 'setup B lines' '
+ echo "2A quick brown fox jumps over the" >>file &&
+ echo "lazy dog" >>file &&
+ GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" \
+ git commit -a -m "Second."
+'
+
+test_expect_success 'blame 2 authors' '
+ check_count A 2 B 2
+'
+
+test_expect_success 'setup B1 lines (branch1)' '
+ git checkout -b branch1 master &&
+ echo "3A slow green fox jumps into the" >>file &&
+ echo "well." >>file &&
+ GIT_AUTHOR_NAME="B1" GIT_AUTHOR_EMAIL="B1@test.git" \
+ git commit -a -m "Branch1-1"
+'
+
+test_expect_success 'blame 2 authors + 1 branch1 author' '
+ check_count A 2 B 2 B1 2
+'
+
+test_expect_success 'setup B2 lines (branch2)' '
+ git checkout -b branch2 master &&
+ sed -e "s/2A quick brown/4A quick brown lazy dog/" <file >file.new &&
+ mv file.new file &&
+ GIT_AUTHOR_NAME="B2" GIT_AUTHOR_EMAIL="B2@test.git" \
+ git commit -a -m "Branch2-1"
+'
+
+test_expect_success 'blame 2 authors + 1 branch2 author' '
+ check_count A 2 B 1 B2 1
+'
+
+test_expect_success 'merge branch1 & branch2' '
+ git merge branch1
+'
+
+test_expect_success 'blame 2 authors + 2 merged-in authors' '
+ check_count A 2 B 1 B1 2 B2 1
+'
+
+test_expect_success 'blame --first-parent blames merge for branch1' '
+ check_count --first-parent A 2 B 1 "A U Thor" 2 B2 1
+'
+
+test_expect_success 'blame ancestor' '
+ check_count -h master A 2 B 2
+'
+
+test_expect_success 'blame great-ancestor' '
+ check_count -h master^ A 2
+'
+
+test_expect_success 'setup evil merge' '
+ echo "evil merge." >>file &&
+ git commit -a --amend
+'
+
+test_expect_success 'blame evil merge' '
+ check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1
+'
+
+test_expect_success 'blame huge graft' '
+ test_when_finished "git checkout branch2" &&
+ test_when_finished "rm -f .git/info/grafts" &&
+ graft= &&
+ for i in 0 1 2
+ do
+ for j in 0 1 2 3 4 5 6 7 8 9
+ do
+ git checkout --orphan "$i$j" &&
+ printf "%s\n" "$i" "$j" >file &&
+ test_tick &&
+ GIT_AUTHOR_NAME=$i$j GIT_AUTHOR_EMAIL=$i$j@test.git \
+ git commit -a -m "$i$j" &&
+ commit=$(git rev-parse --verify HEAD) &&
+ graft="$graft$commit "
+ done
+ done &&
+ printf "%s " $graft >.git/info/grafts &&
+ check_count -h 00 01 1 10 1
+'
+
+test_expect_success 'setup incomplete line' '
+ echo "incomplete" | tr -d "\\012" >>file &&
+ GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" \
+ git commit -a -m "Incomplete"
+'
+
+test_expect_success 'blame incomplete line' '
+ check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1 C 1
+'
+
+test_expect_success 'setup edits' '
+ mv file file.orig &&
+ {
+ cat file.orig &&
+ echo
+ } | sed -e "s/^3A/99/" -e "/^1A/d" -e "/^incomplete/d" >file &&
+ echo "incomplete" | tr -d "\\012" >>file &&
+ GIT_AUTHOR_NAME="D" GIT_AUTHOR_EMAIL="D@test.git" \
+ git commit -a -m "edit"
+'
+
+test_expect_success 'blame edits' '
+ check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1
+'
+
+test_expect_success 'setup obfuscated email' '
+ echo "No robots allowed" >file.new &&
+ cat file >>file.new &&
+ mv file.new file &&
+ GIT_AUTHOR_NAME="E" GIT_AUTHOR_EMAIL="E at test dot git" \
+ git commit -a -m "norobots"
+'
+
+test_expect_success 'blame obfuscated email' '
+ check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
+'
+
+test_expect_success 'blame -L 1 (all)' '
+ check_count -L1 A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
+'
+
+test_expect_success 'blame -L , (all)' '
+ check_count -L, A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
+'
+
+test_expect_success 'blame -L X (X to end)' '
+ check_count -L5 B1 1 C 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L X, (X to end)' '
+ check_count -L5, B1 1 C 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L ,Y (up to Y)' '
+ check_count -L,3 A 1 B2 1 E 1
+'
+
+test_expect_success 'blame -L X,X' '
+ check_count -L3,3 B2 1
+'
+
+test_expect_success 'blame -L X,Y' '
+ check_count -L3,6 B 1 B1 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L Y,X (undocumented)' '
+ check_count -L6,3 B 1 B1 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L -X' '
+ test_must_fail $PROG -L-1 file
+'
+
+test_expect_success 'blame -L 0' '
+ test_must_fail $PROG -L0 file
+'
+
+test_expect_success 'blame -L ,0' '
+ test_must_fail $PROG -L,0 file
+'
+
+test_expect_success 'blame -L ,+0' '
+ test_must_fail $PROG -L,+0 file
+'
+
+test_expect_success 'blame -L X,+0' '
+ test_must_fail $PROG -L1,+0 file
+'
+
+test_expect_success 'blame -L X,+1' '
+ check_count -L3,+1 B2 1
+'
+
+test_expect_success 'blame -L X,+N' '
+ check_count -L3,+4 B 1 B1 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L ,-0' '
+ test_must_fail $PROG -L,-0 file
+'
+
+test_expect_success 'blame -L X,-0' '
+ test_must_fail $PROG -L1,-0 file
+'
+
+test_expect_success 'blame -L X,-1' '
+ check_count -L3,-1 B2 1
+'
+
+test_expect_success 'blame -L X,-N' '
+ check_count -L6,-4 B 1 B1 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L /RE/ (RE to end)' '
+ check_count -L/evil/ C 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/,/RE2/' '
+ check_count -L/robot/,/green/ A 1 B 1 B2 1 D 1 E 1
+'
+
+test_expect_success 'blame -L X,/RE/' '
+ check_count -L5,/evil/ B1 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/,Y' '
+ check_count -L/99/,7 B1 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/,+N' '
+ check_count -L/99/,+3 B1 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/,-N' '
+ check_count -L/99/,-3 B 1 B2 1 D 1
+'
+
+# 'file' ends with an incomplete line, so 'wc' reports one fewer lines than
+# git-blame sees, hence the last line is actually $(wc...)+1.
+test_expect_success 'blame -L X (X == nlines)' '
+ n=$(expr $(wc -l <file) + 1) &&
+ check_count -L$n C 1
+'
+
+test_expect_success 'blame -L X (X == nlines + 1)' '
+ n=$(expr $(wc -l <file) + 2) &&
+ test_must_fail $PROG -L$n file
+'
+
+test_expect_success 'blame -L X (X > nlines)' '
+ test_must_fail $PROG -L12345 file
+'
+
+test_expect_success 'blame -L ,Y (Y == nlines)' '
+ n=$(expr $(wc -l <file) + 1) &&
+ check_count -L,$n A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
+'
+
+test_expect_success 'blame -L ,Y (Y == nlines + 1)' '
+ n=$(expr $(wc -l <file) + 2) &&
+ test_must_fail $PROG -L,$n file
+'
+
+test_expect_success 'blame -L ,Y (Y > nlines)' '
+ test_must_fail $PROG -L,12345 file
+'
+
+test_expect_success 'blame -L multiple (disjoint)' '
+ check_count -L2,3 -L6,7 A 1 B1 1 B2 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L multiple (disjoint: unordered)' '
+ check_count -L6,7 -L2,3 A 1 B1 1 B2 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L multiple (adjacent)' '
+ check_count -L2,3 -L4,5 A 1 B 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L multiple (adjacent: unordered)' '
+ check_count -L4,5 -L2,3 A 1 B 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L multiple (overlapping)' '
+ check_count -L2,4 -L3,5 A 1 B 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L multiple (overlapping: unordered)' '
+ check_count -L3,5 -L2,4 A 1 B 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L multiple (superset/subset)' '
+ check_count -L2,8 -L3,5 A 1 B 1 B1 1 B2 1 C 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L multiple (superset/subset: unordered)' '
+ check_count -L3,5 -L2,8 A 1 B 1 B1 1 B2 1 C 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/ (relative)' '
+ check_count -L3,3 -L/fox/ B1 1 B2 1 C 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/ (relative: no preceding range)' '
+ check_count -L/dog/ A 1 B 1 B1 1 B2 1 C 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/ (relative: adjacent)' '
+ check_count -L1,1 -L/dog/,+1 A 1 E 1
+'
+
+test_expect_success 'blame -L /RE/ (relative: not found)' '
+ test_must_fail $PROG -L4,4 -L/dog/ file
+'
+
+test_expect_success 'blame -L /RE/ (relative: end-of-file)' '
+ test_must_fail $PROG -L, -L/$/ file
+'
+
+test_expect_success 'blame -L ^/RE/ (absolute)' '
+ check_count -L3,3 -L^/dog/,+2 A 1 B2 1
+'
+
+test_expect_success 'blame -L ^/RE/ (absolute: no preceding range)' '
+ check_count -L^/dog/,+2 A 1 B2 1
+'
+
+test_expect_success 'blame -L ^/RE/ (absolute: not found)' '
+ test_must_fail $PROG -L4,4 -L^/tambourine/ file
+'
+
+test_expect_success 'blame -L ^/RE/ (absolute: end-of-file)' '
+ n=$(expr $(wc -l <file) + 1) &&
+ check_count -L$n -L^/$/,+2 A 1 C 1 E 1
+'
+
+test_expect_success 'setup -L :regex' '
+ tr Q "\\t" >hello.c <<-\EOF &&
+ int main(int argc, const char *argv[])
+ {
+ Qputs("hello");
+ }
+ EOF
+ git add hello.c &&
+ GIT_AUTHOR_NAME="F" GIT_AUTHOR_EMAIL="F@test.git" \
+ git commit -m "hello" &&
+
+ mv hello.c hello.orig &&
+ sed -e "/}/ {x; s/$/Qputs(\"goodbye\");/; G;}" <hello.orig |
+ tr Q "\\t" >hello.c &&
+ GIT_AUTHOR_NAME="G" GIT_AUTHOR_EMAIL="G@test.git" \
+ git commit -a -m "goodbye" &&
+
+ mv hello.c hello.orig &&
+ echo "#include <stdio.h>" >hello.c &&
+ cat hello.orig >>hello.c &&
+ tr Q "\\t" >>hello.c <<-\EOF &&
+ void mail()
+ {
+ Qputs("mail");
+ }
+ EOF
+ GIT_AUTHOR_NAME="H" GIT_AUTHOR_EMAIL="H@test.git" \
+ git commit -a -m "mail"
+'
+
+test_expect_success 'blame -L :literal' '
+ check_count -f hello.c -L:main F 4 G 1
+'
+
+test_expect_success 'blame -L :regex' '
+ check_count -f hello.c "-L:m[a-z][a-z]l" H 4
+'
+
+test_expect_success 'blame -L :nomatch' '
+ test_must_fail $PROG -L:nomatch hello.c
+'
+
+test_expect_success 'blame -L :RE (relative)' '
+ check_count -f hello.c -L3,3 -L:ma.. F 1 H 4
+'
+
+test_expect_success 'blame -L :RE (relative: no preceding range)' '
+ check_count -f hello.c -L:ma.. F 4 G 1
+'
+
+test_expect_success 'blame -L :RE (relative: not found)' '
+ test_must_fail $PROG -L3,3 -L:tambourine hello.c
+'
+
+test_expect_success 'blame -L :RE (relative: end-of-file)' '
+ test_must_fail $PROG -L, -L:main hello.c
+'
+
+test_expect_success 'blame -L ^:RE (absolute)' '
+ check_count -f hello.c -L3,3 -L^:ma.. F 4 G 1
+'
+
+test_expect_success 'blame -L ^:RE (absolute: no preceding range)' '
+ check_count -f hello.c -L^:ma.. F 4 G 1
+'
+
+test_expect_success 'blame -L ^:RE (absolute: not found)' '
+ test_must_fail $PROG -L4,4 -L^:tambourine hello.c
+'
+
+test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '
+ n=$(printf "%d" $(wc -l <hello.c)) &&
+ check_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1
+'
+
+test_expect_success 'setup incremental' '
+ (
+ GIT_AUTHOR_NAME=I &&
+ export GIT_AUTHOR_NAME &&
+ GIT_AUTHOR_EMAIL=I@test.git &&
+ export GIT_AUTHOR_EMAIL &&
+ >incremental &&
+ git add incremental &&
+ git commit -m "step 0" &&
+ printf "partial" >>incremental &&
+ git commit -a -m "step 0.5" &&
+ echo >>incremental &&
+ git commit -a -m "step 1"
+ )
+'
+
+test_expect_success 'blame empty' '
+ check_count -h HEAD^^ -f incremental
+'
+
+test_expect_success 'blame -L 0 empty' '
+ test_must_fail $PROG -L0 incremental HEAD^^
+'
+
+test_expect_success 'blame -L 1 empty' '
+ test_must_fail $PROG -L1 incremental HEAD^^
+'
+
+test_expect_success 'blame -L 2 empty' '
+ test_must_fail $PROG -L2 incremental HEAD^^
+'
+
+test_expect_success 'blame half' '
+ check_count -h HEAD^ -f incremental I 1
+'
+
+test_expect_success 'blame -L 0 half' '
+ test_must_fail $PROG -L0 incremental HEAD^
+'
+
+test_expect_success 'blame -L 1 half' '
+ check_count -h HEAD^ -f incremental -L1 I 1
+'
+
+test_expect_success 'blame -L 2 half' '
+ test_must_fail $PROG -L2 incremental HEAD^
+'
+
+test_expect_success 'blame -L 3 half' '
+ test_must_fail $PROG -L3 incremental HEAD^
+'
+
+test_expect_success 'blame full' '
+ check_count -f incremental I 1
+'
+
+test_expect_success 'blame -L 0 full' '
+ test_must_fail $PROG -L0 incremental
+'
+
+test_expect_success 'blame -L 1 full' '
+ check_count -f incremental -L1 I 1
+'
+
+test_expect_success 'blame -L 2 full' '
+ test_must_fail $PROG -L2 incremental
+'
+
+test_expect_success 'blame -L 3 full' '
+ test_must_fail $PROG -L3 incremental
+'
+
+test_expect_success 'blame -L' '
+ test_must_fail $PROG -L file
+'
+
+test_expect_success 'blame -L X,+' '
+ test_must_fail $PROG -L1,+ file
+'
+
+test_expect_success 'blame -L X,-' '
+ test_must_fail $PROG -L1,- file
+'
+
+test_expect_success 'blame -L X (non-numeric X)' '
+ test_must_fail $PROG -LX file
+'
+
+test_expect_success 'blame -L X,Y (non-numeric Y)' '
+ test_must_fail $PROG -L1,Y file
+'
+
+test_expect_success 'blame -L X,+N (non-numeric N)' '
+ test_must_fail $PROG -L1,+N file
+'
+
+test_expect_success 'blame -L X,-N (non-numeric N)' '
+ test_must_fail $PROG -L1,-N file
+'
+
+test_expect_success 'blame -L ,^/RE/' '
+ test_must_fail $PROG -L1,^/99/ file
+'
diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
new file mode 100755
index 0000000..b170cbc
--- /dev/null
+++ b/t/check-non-portable-shell.pl
@@ -0,0 +1,28 @@
+#!/usr/bin/perl
+
+# Test t0000..t9999.sh for non portable shell scripts
+# This script can be called with one or more filenames as parameters
+
+use strict;
+use warnings;
+
+my $exit_code=0;
+
+sub err {
+ my $msg = shift;
+ print "$ARGV:$.: error: $msg: $_\n";
+ $exit_code = 1;
+}
+
+while (<>) {
+ chomp;
+ /\bsed\s+-i/ and err 'sed -i is not portable';
+ /\becho\s+-n/ and err 'echo -n is not portable (please use printf)';
+ /^\s*declare\s+/ and err 'arrays/declare not portable';
+ /^\s*[^#]\s*which\s/ and err 'which is not portable (please use type)';
+ /\btest\s+[^=]*==/ and err '"test a == b" is not portable (please use =)';
+ /\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (please use FOO=bar && export FOO)';
+ # this resets our $. for each file
+ close ARGV if eof;
+}
+exit $exit_code;
diff --git a/t/diff-lib.sh b/t/diff-lib.sh
index 75a35fc..c211dc4 100644
--- a/t/diff-lib.sh
+++ b/t/diff-lib.sh
@@ -1,6 +1,6 @@
:
-sanitize_diff_raw='/^:/s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]* / X X \1# /'
+sanitize_diff_raw='/^:/s/ '"\($_x40\)"' '"\($_x40\)"' \([A-Z]\)[0-9]* / \1 \2 \3# /'
compare_diff_raw () {
# When heuristics are improved, the score numbers would change.
# Ignore them while comparing.
diff --git a/t/diff-lib/COPYING b/t/diff-lib/COPYING
new file mode 100644
index 0000000..6ff87c4
--- /dev/null
+++ b/t/diff-lib/COPYING
@@ -0,0 +1,361 @@
+
+ Note that the only valid version of the GPL as far as this project
+ is concerned is _this_ particular version of the license (ie v2, not
+ v2.2 or v3.x or whatever), unless explicitly otherwise stated.
+
+ HOWEVER, in order to allow a migration to GPLv3 if that seems like
+ a good idea, I also ask that people involved with the project make
+ their preferences known. In particular, if you trust me to make that
+ decision, you might note so in your copyright message, ie something
+ like
+
+ This file is licensed under the GPL v2, or a later version
+ at the discretion of Linus.
+
+ might avoid issues. But we can also just decide to synchronize and
+ contact all copyright holders on record if/when the occasion arises.
+
+ Linus Torvalds
+
+----------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/t/diff-lib/README b/t/diff-lib/README
new file mode 100644
index 0000000..548142c
--- /dev/null
+++ b/t/diff-lib/README
@@ -0,0 +1,46 @@
+////////////////////////////////////////////////////////////////
+
+ GIT - the stupid content tracker
+
+////////////////////////////////////////////////////////////////
+
+"git" can mean anything, depending on your mood.
+
+ - random three-letter combination that is pronounceable, and not
+ actually used by any common UNIX command. The fact that it is a
+ mispronunciation of "get" may or may not be relevant.
+ - stupid. contemptible and despicable. simple. Take your pick from the
+ dictionary of slang.
+ - "global information tracker": you're in a good mood, and it actually
+ works for you. Angels sing, and a light suddenly fills the room.
+ - "goddamn idiotic truckload of sh*t": when it breaks
+
+Git is a fast, scalable, distributed revision control system with an
+unusually rich command set that provides both high-level operations
+and full access to internals.
+
+Git is an Open Source project covered by the GNU General Public License.
+It was originally written by Linus Torvalds with help of a group of
+hackers around the net. It is currently maintained by Junio C Hamano.
+
+Please read the file INSTALL for installation instructions.
+See Documentation/tutorial.txt to get started, then see
+Documentation/everyday.txt for a useful minimum set of commands,
+and "man git-commandname" for documentation of each command.
+CVS users may also want to read Documentation/cvs-migration.txt.
+
+Many Git online resources are accessible from http://git.or.cz/
+including full documentation and Git related tools.
+
+The user discussion and development of Git take place on the Git
+mailing list -- everyone is welcome to post bug reports, feature
+requests, comments and patches to git@vger.kernel.org. To subscribe
+to the list, send an email with just "subscribe git" in the body to
+majordomo@vger.kernel.org. The mailing list archives are available at
+http://marc.theaimsgroup.com/?l=git and other archival sites.
+
+The messages titled "A note from the maintainer", "What's in
+git.git (stable)" and "What's cooking in git.git (topics)" and
+the discussion following them on the mailing list give a good
+reference for project status, development direction and
+remaining tasks.
diff --git a/t/gitweb-lib.sh b/t/gitweb-lib.sh
index 81ef2a0..d5dab5a 100644
--- a/t/gitweb-lib.sh
+++ b/t/gitweb-lib.sh
@@ -1,4 +1,5 @@
-#!/bin/sh
+# Initialization and helpers for Gitweb tests, which source this
+# shell library instead of test-lib.sh.
#
# Copyright (c) 2007 Jakub Narebski
#
@@ -16,12 +17,13 @@ our \$projectroot = "$safe_pwd";
our \$project_maxdepth = 8;
our \$home_link_str = 'projects';
our \$site_name = '[localhost]';
+our \$site_html_head_string = '';
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 @stylesheets = ('file:///$GIT_BUILD_DIR/gitweb/static/gitweb.css');
+our \$logo = 'file:///$GIT_BUILD_DIR/gitweb/static/git-logo.png';
+our \$favicon = 'file:///$GIT_BUILD_DIR/gitweb/static/git-favicon.png';
our \$projects_list = '';
our \$export_ok = '';
our \$strict_export = '';
@@ -32,17 +34,34 @@ EOF
cat >.git/description <<EOF
$0 test repository
EOF
+
+ # You can set the GITWEB_TEST_INSTALLED environment variable to
+ # the gitwebdir (the directory where gitweb is installed / deployed to)
+ # of an existing gitweb installation to test that installation,
+ # or simply to pathname of installed gitweb script.
+ if test -n "$GITWEB_TEST_INSTALLED" ; then
+ if test -d $GITWEB_TEST_INSTALLED; then
+ SCRIPT_NAME="$GITWEB_TEST_INSTALLED/gitweb.cgi"
+ else
+ SCRIPT_NAME="$GITWEB_TEST_INSTALLED"
+ fi
+ test -f "$SCRIPT_NAME" ||
+ error "Cannot find gitweb at $GITWEB_TEST_INSTALLED."
+ say "# Testing $SCRIPT_NAME"
+ else # normal case, use source version of gitweb
+ SCRIPT_NAME="$GIT_BUILD_DIR/gitweb/gitweb.perl"
+ fi
+ export SCRIPT_NAME
}
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
+ QUERY_STRING PATH_INFO
GITWEB_CONFIG=$(pwd)/gitweb_config.perl
export GITWEB_CONFIG
@@ -65,7 +84,12 @@ gitweb_run () {
}
close O;
' gitweb.output &&
- if grep '^[[]' gitweb.log >/dev/null 2>&1; then false; else true; fi
+ if grep '^[[]' gitweb.log >/dev/null 2>&1; then
+ test_debug 'cat gitweb.log >&2' &&
+ false
+ else
+ true
+ fi
# gitweb.log is left for debugging
# gitweb.output is used to parse HTTP output
@@ -80,9 +104,14 @@ if ! test_have_prereq PERL; then
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
+perl -MEncode -e '$e="";decode_utf8($e, Encode::FB_CROAK)' >/dev/null 2>&1 || {
+ skip_all='skipping gitweb tests, perl version is too old'
+ test_done
+}
+
+perl -MCGI -MCGI::Util -MCGI::Carp -e 0 >/dev/null 2>&1 || {
+ skip_all='skipping gitweb tests, CGI module unusable'
+ test_done
}
gitweb_init
diff --git a/t/helper/.gitignore b/t/helper/.gitignore
new file mode 100644
index 0000000..d6e8b36
--- /dev/null
+++ b/t/helper/.gitignore
@@ -0,0 +1,33 @@
+/test-chmtime
+/test-ctype
+/test-config
+/test-date
+/test-delta
+/test-dump-cache-tree
+/test-dump-split-index
+/test-dump-untracked-cache
+/test-fake-ssh
+/test-scrap-cache-tree
+/test-genrandom
+/test-hashmap
+/test-index-version
+/test-line-buffer
+/test-match-trees
+/test-mergesort
+/test-mktemp
+/test-parse-options
+/test-path-utils
+/test-prio-queue
+/test-read-cache
+/test-regex
+/test-revision-walking
+/test-run-command
+/test-sha1
+/test-sha1-array
+/test-sigchain
+/test-string-list
+/test-submodule-config
+/test-subprocess
+/test-svn-fe
+/test-urlmatch-normalization
+/test-wildmatch
diff --git a/t/helper/test-chmtime.c b/t/helper/test-chmtime.c
new file mode 100644
index 0000000..dfe8a83
--- /dev/null
+++ b/t/helper/test-chmtime.c
@@ -0,0 +1,119 @@
+/*
+ * This program can either change modification time of the given
+ * file(s) or just print it. The program does not change atime or
+ * ctime (their values are explicitly preserved).
+ *
+ * The mtime can be changed to an absolute value:
+ *
+ * test-chmtime =<seconds> file...
+ *
+ * Relative to the current time as returned by time(3):
+ *
+ * test-chmtime =+<seconds> (or =-<seconds>) file...
+ *
+ * Or relative to the current mtime of the file:
+ *
+ * test-chmtime <seconds> file...
+ * test-chmtime +<seconds> (or -<seconds>) file...
+ *
+ * Examples:
+ *
+ * To just print the mtime use --verbose and set the file mtime offset to 0:
+ *
+ * test-chmtime -v +0 file
+ *
+ * To set the mtime to current time:
+ *
+ * test-chmtime =+0 file
+ *
+ */
+#include "git-compat-util.h"
+#include <utime.h>
+
+static const char usage_str[] = "-v|--verbose (+|=|=+|=-|-)<seconds> <file>...";
+
+static int timespec_arg(const char *arg, long int *set_time, int *set_eq)
+{
+ char *test;
+ const char *timespec = arg;
+ *set_eq = (*timespec == '=') ? 1 : 0;
+ if (*set_eq) {
+ timespec++;
+ if (*timespec == '+') {
+ *set_eq = 2; /* relative "in the future" */
+ timespec++;
+ }
+ }
+ *set_time = strtol(timespec, &test, 10);
+ if (*test) {
+ fprintf(stderr, "Not a base-10 integer: %s\n", arg + 1);
+ return 0;
+ }
+ if ((*set_eq && *set_time < 0) || *set_eq == 2) {
+ time_t now = time(NULL);
+ *set_time += now;
+ }
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ static int verbose;
+
+ int i = 1;
+ /* no mtime change by default */
+ int set_eq = 0;
+ long int set_time = 0;
+
+ if (argc < 3)
+ goto usage;
+
+ if (strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0) {
+ verbose = 1;
+ ++i;
+ }
+ if (timespec_arg(argv[i], &set_time, &set_eq))
+ ++i;
+ else
+ goto usage;
+
+ for (; i < argc; i++) {
+ struct stat sb;
+ struct utimbuf utb;
+
+ if (stat(argv[i], &sb) < 0) {
+ fprintf(stderr, "Failed to stat %s: %s\n",
+ argv[i], strerror(errno));
+ return 1;
+ }
+
+#ifdef GIT_WINDOWS_NATIVE
+ if (!(sb.st_mode & S_IWUSR) &&
+ chmod(argv[i], sb.st_mode | S_IWUSR)) {
+ fprintf(stderr, "Could not make user-writable %s: %s",
+ argv[i], strerror(errno));
+ return 1;
+ }
+#endif
+
+ utb.actime = sb.st_atime;
+ utb.modtime = set_eq ? set_time : sb.st_mtime + set_time;
+
+ if (verbose) {
+ uintmax_t mtime = utb.modtime < 0 ? 0: utb.modtime;
+ printf("%"PRIuMAX"\t%s\n", mtime, argv[i]);
+ }
+
+ if (utb.modtime != sb.st_mtime && utime(argv[i], &utb) < 0) {
+ fprintf(stderr, "Failed to modify time on %s: %s\n",
+ argv[i], strerror(errno));
+ return 1;
+ }
+ }
+
+ return 0;
+
+usage:
+ fprintf(stderr, "usage: %s %s\n", argv[0], usage_str);
+ return 1;
+}
diff --git a/t/helper/test-config.c b/t/helper/test-config.c
new file mode 100644
index 0000000..6a77552
--- /dev/null
+++ b/t/helper/test-config.c
@@ -0,0 +1,152 @@
+#include "cache.h"
+#include "string-list.h"
+
+/*
+ * This program exposes the C API of the configuration mechanism
+ * as a set of simple commands in order to facilitate testing.
+ *
+ * Reads stdin and prints result of command to stdout:
+ *
+ * get_value -> prints the value with highest priority for the entered key
+ *
+ * get_value_multi -> prints all values for the entered key in increasing order
+ * of priority
+ *
+ * get_int -> print integer value for the entered key or die
+ *
+ * get_bool -> print bool value for the entered key or die
+ *
+ * get_string -> print string value for the entered key or die
+ *
+ * configset_get_value -> returns value with the highest priority for the entered key
+ * from a config_set constructed from files entered as arguments.
+ *
+ * configset_get_value_multi -> returns value_list for the entered key sorted in
+ * ascending order of priority from a config_set
+ * constructed from files entered as arguments.
+ *
+ * Examples:
+ *
+ * To print the value with highest priority for key "foo.bAr Baz.rock":
+ * test-config get_value "foo.bAr Baz.rock"
+ *
+ */
+
+
+int main(int argc, char **argv)
+{
+ int i, val;
+ const char *v;
+ const struct string_list *strptr;
+ struct config_set cs;
+ git_configset_init(&cs);
+
+ if (argc < 2) {
+ fprintf(stderr, "Please, provide a command name on the command-line\n");
+ goto exit1;
+ } else if (argc == 3 && !strcmp(argv[1], "get_value")) {
+ if (!git_config_get_value(argv[2], &v)) {
+ if (!v)
+ printf("(NULL)\n");
+ else
+ printf("%s\n", v);
+ goto exit0;
+ } else {
+ printf("Value not found for \"%s\"\n", argv[2]);
+ goto exit1;
+ }
+ } else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) {
+ strptr = git_config_get_value_multi(argv[2]);
+ if (strptr) {
+ for (i = 0; i < strptr->nr; i++) {
+ v = strptr->items[i].string;
+ if (!v)
+ printf("(NULL)\n");
+ else
+ printf("%s\n", v);
+ }
+ goto exit0;
+ } else {
+ printf("Value not found for \"%s\"\n", argv[2]);
+ goto exit1;
+ }
+ } else if (argc == 3 && !strcmp(argv[1], "get_int")) {
+ if (!git_config_get_int(argv[2], &val)) {
+ printf("%d\n", val);
+ goto exit0;
+ } else {
+ printf("Value not found for \"%s\"\n", argv[2]);
+ goto exit1;
+ }
+ } else if (argc == 3 && !strcmp(argv[1], "get_bool")) {
+ if (!git_config_get_bool(argv[2], &val)) {
+ printf("%d\n", val);
+ goto exit0;
+ } else {
+ printf("Value not found for \"%s\"\n", argv[2]);
+ goto exit1;
+ }
+ } else if (argc == 3 && !strcmp(argv[1], "get_string")) {
+ if (!git_config_get_string_const(argv[2], &v)) {
+ printf("%s\n", v);
+ goto exit0;
+ } else {
+ printf("Value not found for \"%s\"\n", argv[2]);
+ goto exit1;
+ }
+ } else if (!strcmp(argv[1], "configset_get_value")) {
+ for (i = 3; i < argc; i++) {
+ int err;
+ if ((err = git_configset_add_file(&cs, argv[i]))) {
+ fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
+ goto exit2;
+ }
+ }
+ if (!git_configset_get_value(&cs, argv[2], &v)) {
+ if (!v)
+ printf("(NULL)\n");
+ else
+ printf("%s\n", v);
+ goto exit0;
+ } else {
+ printf("Value not found for \"%s\"\n", argv[2]);
+ goto exit1;
+ }
+ } else if (!strcmp(argv[1], "configset_get_value_multi")) {
+ for (i = 3; i < argc; i++) {
+ int err;
+ if ((err = git_configset_add_file(&cs, argv[i]))) {
+ fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
+ goto exit2;
+ }
+ }
+ strptr = git_configset_get_value_multi(&cs, argv[2]);
+ if (strptr) {
+ for (i = 0; i < strptr->nr; i++) {
+ v = strptr->items[i].string;
+ if (!v)
+ printf("(NULL)\n");
+ else
+ printf("%s\n", v);
+ }
+ goto exit0;
+ } else {
+ printf("Value not found for \"%s\"\n", argv[2]);
+ goto exit1;
+ }
+ }
+
+ die("%s: Please check the syntax and the function name", argv[0]);
+
+exit0:
+ git_configset_clear(&cs);
+ return 0;
+
+exit1:
+ git_configset_clear(&cs);
+ return 1;
+
+exit2:
+ git_configset_clear(&cs);
+ return 2;
+}
diff --git a/t/helper/test-ctype.c b/t/helper/test-ctype.c
new file mode 100644
index 0000000..707a821
--- /dev/null
+++ b/t/helper/test-ctype.c
@@ -0,0 +1,42 @@
+#include "cache.h"
+
+static int rc;
+
+static void report_error(const char *class, int ch)
+{
+ printf("%s classifies char %d (0x%02x) wrongly\n", class, ch, ch);
+ rc = 1;
+}
+
+static int is_in(const char *s, int ch)
+{
+ /* We can't find NUL using strchr. It's classless anyway. */
+ if (ch == '\0')
+ return 0;
+ return !!strchr(s, ch);
+}
+
+#define TEST_CLASS(t,s) { \
+ int i; \
+ for (i = 0; i < 256; i++) { \
+ if (is_in(s, i) != t(i)) \
+ report_error(#t, i); \
+ } \
+}
+
+#define DIGIT "0123456789"
+#define LOWER "abcdefghijklmnopqrstuvwxyz"
+#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+int main(int argc, char **argv)
+{
+ TEST_CLASS(isdigit, DIGIT);
+ TEST_CLASS(isspace, " \n\r\t");
+ TEST_CLASS(isalpha, LOWER UPPER);
+ TEST_CLASS(isalnum, LOWER UPPER DIGIT);
+ TEST_CLASS(is_glob_special, "*?[\\");
+ TEST_CLASS(is_regex_special, "$()*+.?[\\^{|");
+ TEST_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~");
+
+ return rc;
+}
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
new file mode 100644
index 0000000..d9ab360
--- /dev/null
+++ b/t/helper/test-date.c
@@ -0,0 +1,99 @@
+#include "cache.h"
+
+static const char *usage_msg = "\n"
+" test-date relative [time_t]...\n"
+" test-date show:<format> [time_t]...\n"
+" test-date parse [date]...\n"
+" test-date approxidate [date]...\n";
+
+static void show_relative_dates(char **argv, struct timeval *now)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ for (; *argv; argv++) {
+ time_t t = atoi(*argv);
+ show_date_relative(t, 0, now, &buf);
+ printf("%s -> %s\n", *argv, buf.buf);
+ }
+ strbuf_release(&buf);
+}
+
+static void show_dates(char **argv, const char *format)
+{
+ struct date_mode mode;
+
+ parse_date_format(format, &mode);
+ for (; *argv; argv++) {
+ char *arg = *argv;
+ time_t t;
+ int tz;
+
+ /*
+ * Do not use our normal timestamp parsing here, as the point
+ * is to test the formatting code in isolation.
+ */
+ t = strtol(arg, &arg, 10);
+ while (*arg == ' ')
+ arg++;
+ tz = atoi(arg);
+
+ printf("%s -> %s\n", *argv, show_date(t, tz, &mode));
+ }
+}
+
+static void parse_dates(char **argv, struct timeval *now)
+{
+ struct strbuf result = STRBUF_INIT;
+
+ for (; *argv; argv++) {
+ unsigned long t;
+ int tz;
+
+ strbuf_reset(&result);
+ parse_date(*argv, &result);
+ if (sscanf(result.buf, "%lu %d", &t, &tz) == 2)
+ printf("%s -> %s\n",
+ *argv, show_date(t, tz, DATE_MODE(ISO8601)));
+ else
+ printf("%s -> bad\n", *argv);
+ }
+ strbuf_release(&result);
+}
+
+static void parse_approxidate(char **argv, struct timeval *now)
+{
+ for (; *argv; argv++) {
+ time_t t;
+ t = approxidate_relative(*argv, now);
+ printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601)));
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct timeval now;
+ const char *x;
+
+ x = getenv("TEST_DATE_NOW");
+ if (x) {
+ now.tv_sec = atoi(x);
+ now.tv_usec = 0;
+ }
+ else
+ gettimeofday(&now, NULL);
+
+ argv++;
+ if (!*argv)
+ usage(usage_msg);
+ if (!strcmp(*argv, "relative"))
+ show_relative_dates(argv+1, &now);
+ else if (skip_prefix(*argv, "show:", &x))
+ show_dates(argv+1, x);
+ else if (!strcmp(*argv, "parse"))
+ parse_dates(argv+1, &now);
+ else if (!strcmp(*argv, "approxidate"))
+ parse_approxidate(argv+1, &now);
+ else
+ usage(usage_msg);
+ return 0;
+}
diff --git a/t/helper/test-delta.c b/t/helper/test-delta.c
new file mode 100644
index 0000000..4595cd6
--- /dev/null
+++ b/t/helper/test-delta.c
@@ -0,0 +1,78 @@
+/*
+ * test-delta.c: test code to exercise diff-delta.c and patch-delta.c
+ *
+ * (C) 2005 Nicolas Pitre <nico@fluxnic.net>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "git-compat-util.h"
+#include "delta.h"
+#include "cache.h"
+
+static const char usage_str[] =
+ "test-delta (-d|-p) <from_file> <data_file> <out_file>";
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ struct stat st;
+ void *from_buf, *data_buf, *out_buf;
+ unsigned long from_size, data_size, out_size;
+
+ if (argc != 5 || (strcmp(argv[1], "-d") && strcmp(argv[1], "-p"))) {
+ fprintf(stderr, "usage: %s\n", usage_str);
+ return 1;
+ }
+
+ fd = open(argv[2], O_RDONLY);
+ if (fd < 0 || fstat(fd, &st)) {
+ perror(argv[2]);
+ return 1;
+ }
+ from_size = st.st_size;
+ from_buf = mmap(NULL, from_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (from_buf == MAP_FAILED) {
+ perror(argv[2]);
+ close(fd);
+ return 1;
+ }
+ close(fd);
+
+ fd = open(argv[3], O_RDONLY);
+ if (fd < 0 || fstat(fd, &st)) {
+ perror(argv[3]);
+ return 1;
+ }
+ data_size = st.st_size;
+ data_buf = mmap(NULL, data_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (data_buf == MAP_FAILED) {
+ perror(argv[3]);
+ close(fd);
+ return 1;
+ }
+ close(fd);
+
+ if (argv[1][1] == 'd')
+ out_buf = diff_delta(from_buf, from_size,
+ data_buf, data_size,
+ &out_size, 0);
+ else
+ out_buf = patch_delta(from_buf, from_size,
+ data_buf, data_size,
+ &out_size);
+ if (!out_buf) {
+ fprintf(stderr, "delta operation failed (returned NULL)\n");
+ return 1;
+ }
+
+ fd = open (argv[4], O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (fd < 0 || write_in_full(fd, out_buf, out_size) != out_size) {
+ perror(argv[4]);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/t/helper/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c
new file mode 100644
index 0000000..bb53c0a
--- /dev/null
+++ b/t/helper/test-dump-cache-tree.c
@@ -0,0 +1,67 @@
+#include "cache.h"
+#include "tree.h"
+#include "cache-tree.h"
+
+
+static void dump_one(struct cache_tree *it, const char *pfx, const char *x)
+{
+ if (it->entry_count < 0)
+ printf("%-40s %s%s (%d subtrees)\n",
+ "invalid", x, pfx, it->subtree_nr);
+ else
+ printf("%s %s%s (%d entries, %d subtrees)\n",
+ sha1_to_hex(it->sha1), x, pfx,
+ it->entry_count, it->subtree_nr);
+}
+
+static int dump_cache_tree(struct cache_tree *it,
+ struct cache_tree *ref,
+ const char *pfx)
+{
+ int i;
+ int errs = 0;
+
+ if (!it || !ref)
+ /* missing in either */
+ return 0;
+
+ if (it->entry_count < 0) {
+ /* invalid */
+ dump_one(it, pfx, "");
+ dump_one(ref, pfx, "#(ref) ");
+ }
+ else {
+ dump_one(it, pfx, "");
+ if (hashcmp(it->sha1, ref->sha1) ||
+ ref->entry_count != it->entry_count ||
+ ref->subtree_nr != it->subtree_nr) {
+ /* claims to be valid but is lying */
+ dump_one(ref, pfx, "#(ref) ");
+ errs = 1;
+ }
+ }
+
+ for (i = 0; i < it->subtree_nr; i++) {
+ char path[PATH_MAX];
+ struct cache_tree_sub *down = it->down[i];
+ struct cache_tree_sub *rdwn;
+
+ rdwn = cache_tree_sub(ref, down->name);
+ xsnprintf(path, sizeof(path), "%s%.*s/", pfx, down->namelen, down->name);
+ if (dump_cache_tree(down->cache_tree, rdwn->cache_tree, path))
+ errs = 1;
+ }
+ return errs;
+}
+
+int main(int ac, char **av)
+{
+ struct index_state istate;
+ struct cache_tree *another = cache_tree();
+ if (read_cache() < 0)
+ die("unable to read index file");
+ istate = the_index;
+ istate.cache_tree = another;
+ cache_tree_update(&istate, WRITE_TREE_DRY_RUN);
+ return dump_cache_tree(active_cache_tree, another, "");
+}
diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c
new file mode 100644
index 0000000..861d28c
--- /dev/null
+++ b/t/helper/test-dump-split-index.c
@@ -0,0 +1,36 @@
+#include "cache.h"
+#include "split-index.h"
+#include "ewah/ewok.h"
+
+static void show_bit(size_t pos, void *data)
+{
+ printf(" %d", (int)pos);
+}
+
+int main(int ac, char **av)
+{
+ struct split_index *si;
+ int i;
+
+ do_read_index(&the_index, av[1], 1);
+ printf("own %s\n", sha1_to_hex(the_index.sha1));
+ si = the_index.split_index;
+ if (!si) {
+ printf("not a split index\n");
+ return 0;
+ }
+ printf("base %s\n", sha1_to_hex(si->base_sha1));
+ for (i = 0; i < the_index.cache_nr; i++) {
+ struct cache_entry *ce = the_index.cache[i];
+ printf("%06o %s %d\t%s\n", ce->ce_mode,
+ sha1_to_hex(ce->sha1), ce_stage(ce), ce->name);
+ }
+ printf("replacements:");
+ if (si->replace_bitmap)
+ ewah_each_bit(si->replace_bitmap, show_bit, NULL);
+ printf("\ndeletions:");
+ if (si->delete_bitmap)
+ ewah_each_bit(si->delete_bitmap, show_bit, NULL);
+ printf("\n");
+ return 0;
+}
diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c
new file mode 100644
index 0000000..0a1c285
--- /dev/null
+++ b/t/helper/test-dump-untracked-cache.c
@@ -0,0 +1,66 @@
+#include "cache.h"
+#include "dir.h"
+
+static int compare_untracked(const void *a_, const void *b_)
+{
+ const char *const *a = a_;
+ const char *const *b = b_;
+ return strcmp(*a, *b);
+}
+
+static int compare_dir(const void *a_, const void *b_)
+{
+ const struct untracked_cache_dir *const *a = a_;
+ const struct untracked_cache_dir *const *b = b_;
+ return strcmp((*a)->name, (*b)->name);
+}
+
+static void dump(struct untracked_cache_dir *ucd, struct strbuf *base)
+{
+ int i, len;
+ qsort(ucd->untracked, ucd->untracked_nr, sizeof(*ucd->untracked),
+ compare_untracked);
+ qsort(ucd->dirs, ucd->dirs_nr, sizeof(*ucd->dirs),
+ compare_dir);
+ len = base->len;
+ strbuf_addf(base, "%s/", ucd->name);
+ printf("%s %s", base->buf,
+ sha1_to_hex(ucd->exclude_sha1));
+ if (ucd->recurse)
+ fputs(" recurse", stdout);
+ if (ucd->check_only)
+ fputs(" check_only", stdout);
+ if (ucd->valid)
+ fputs(" valid", stdout);
+ printf("\n");
+ for (i = 0; i < ucd->untracked_nr; i++)
+ printf("%s\n", ucd->untracked[i]);
+ for (i = 0; i < ucd->dirs_nr; i++)
+ dump(ucd->dirs[i], base);
+ strbuf_setlen(base, len);
+}
+
+int main(int ac, char **av)
+{
+ struct untracked_cache *uc;
+ struct strbuf base = STRBUF_INIT;
+
+ /* Hack to avoid modifying the untracked cache when we read it */
+ ignore_untracked_cache_config = 1;
+
+ setup_git_directory();
+ if (read_cache() < 0)
+ die("unable to read index file");
+ uc = the_index.untracked;
+ if (!uc) {
+ printf("no untracked cache\n");
+ return 0;
+ }
+ printf("info/exclude %s\n", sha1_to_hex(uc->ss_info_exclude.sha1));
+ printf("core.excludesfile %s\n", sha1_to_hex(uc->ss_excludes_file.sha1));
+ printf("exclude_per_dir %s\n", uc->exclude_per_dir);
+ printf("flags %08x\n", uc->dir_flags);
+ if (uc->root)
+ dump(uc->root, &base);
+ return 0;
+}
diff --git a/t/helper/test-fake-ssh.c b/t/helper/test-fake-ssh.c
new file mode 100644
index 0000000..980de21
--- /dev/null
+++ b/t/helper/test-fake-ssh.c
@@ -0,0 +1,30 @@
+#include "git-compat-util.h"
+#include "run-command.h"
+#include "strbuf.h"
+
+int main(int argc, char **argv)
+{
+ const char *trash_directory = getenv("TRASH_DIRECTORY");
+ struct strbuf buf = STRBUF_INIT;
+ FILE *f;
+ int i;
+ const char *child_argv[] = { NULL, NULL };
+
+ /* First, print all parameters into $TRASH_DIRECTORY/ssh-output */
+ if (!trash_directory)
+ die("Need a TRASH_DIRECTORY!");
+ strbuf_addf(&buf, "%s/ssh-output", trash_directory);
+ f = fopen(buf.buf, "w");
+ if (!f)
+ die("Could not write to %s", buf.buf);
+ for (i = 0; i < argc; i++)
+ fprintf(f, "%s%s", i > 0 ? " " : "", i > 0 ? argv[i] : "ssh:");
+ fprintf(f, "\n");
+ fclose(f);
+
+ /* Now, evaluate the *last* parameter */
+ if (argc < 2)
+ return 0;
+ child_argv[0] = argv[argc - 1];
+ return run_command_v_opt(child_argv, RUN_USING_SHELL);
+}
diff --git a/t/helper/test-genrandom.c b/t/helper/test-genrandom.c
new file mode 100644
index 0000000..54824d0
--- /dev/null
+++ b/t/helper/test-genrandom.c
@@ -0,0 +1,33 @@
+/*
+ * Simple random data generator used to create reproducible test files.
+ * This is inspired from POSIX.1-2001 implementation example for rand().
+ * Copyright (C) 2007 by Nicolas Pitre, licensed under the GPL version 2.
+ */
+
+#include "git-compat-util.h"
+
+int main(int argc, char *argv[])
+{
+ unsigned long count, next = 0;
+ unsigned char *c;
+
+ if (argc < 2 || argc > 3) {
+ fprintf(stderr, "usage: %s <seed_string> [<size>]\n", argv[0]);
+ return 1;
+ }
+
+ c = (unsigned char *) argv[1];
+ do {
+ next = next * 11 + *c;
+ } while (*c++);
+
+ count = (argc == 3) ? strtoul(argv[2], NULL, 0) : -1L;
+
+ while (count--) {
+ next = next * 1103515245 + 12345;
+ if (putchar((next >> 16) & 0xff) == EOF)
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
new file mode 100644
index 0000000..cc2891d
--- /dev/null
+++ b/t/helper/test-hashmap.c
@@ -0,0 +1,264 @@
+#include "git-compat-util.h"
+#include "hashmap.h"
+
+struct test_entry
+{
+ struct hashmap_entry ent;
+ /* key and value as two \0-terminated strings */
+ char key[FLEX_ARRAY];
+};
+
+static const char *get_value(const struct test_entry *e)
+{
+ return e->key + strlen(e->key) + 1;
+}
+
+static int test_entry_cmp(const struct test_entry *e1,
+ const struct test_entry *e2, const char* key)
+{
+ return strcmp(e1->key, key ? key : e2->key);
+}
+
+static int test_entry_cmp_icase(const struct test_entry *e1,
+ const struct test_entry *e2, const char* key)
+{
+ return strcasecmp(e1->key, key ? key : e2->key);
+}
+
+static struct test_entry *alloc_test_entry(int hash, char *key, int klen,
+ char *value, int vlen)
+{
+ struct test_entry *entry = malloc(sizeof(struct test_entry) + klen
+ + vlen + 2);
+ hashmap_entry_init(entry, hash);
+ memcpy(entry->key, key, klen + 1);
+ memcpy(entry->key + klen + 1, value, vlen + 1);
+ return entry;
+}
+
+#define HASH_METHOD_FNV 0
+#define HASH_METHOD_I 1
+#define HASH_METHOD_IDIV10 2
+#define HASH_METHOD_0 3
+#define HASH_METHOD_X2 4
+#define TEST_SPARSE 8
+#define TEST_ADD 16
+#define TEST_SIZE 100000
+
+static unsigned int hash(unsigned int method, unsigned int i, const char *key)
+{
+ unsigned int hash = 0;
+ switch (method & 3)
+ {
+ case HASH_METHOD_FNV:
+ hash = strhash(key);
+ break;
+ case HASH_METHOD_I:
+ hash = i;
+ break;
+ case HASH_METHOD_IDIV10:
+ hash = i / 10;
+ break;
+ case HASH_METHOD_0:
+ hash = 0;
+ break;
+ }
+
+ if (method & HASH_METHOD_X2)
+ hash = 2 * hash;
+ return hash;
+}
+
+/*
+ * Test performance of hashmap.[ch]
+ * Usage: time echo "perfhashmap method rounds" | test-hashmap
+ */
+static void perf_hashmap(unsigned int method, unsigned int rounds)
+{
+ struct hashmap map;
+ char buf[16];
+ struct test_entry **entries;
+ unsigned int *hashes;
+ unsigned int i, j;
+
+ entries = malloc(TEST_SIZE * sizeof(struct test_entry *));
+ hashes = malloc(TEST_SIZE * sizeof(int));
+ for (i = 0; i < TEST_SIZE; i++) {
+ snprintf(buf, sizeof(buf), "%i", i);
+ entries[i] = alloc_test_entry(0, buf, strlen(buf), "", 0);
+ hashes[i] = hash(method, i, entries[i]->key);
+ }
+
+ if (method & TEST_ADD) {
+ /* test adding to the map */
+ for (j = 0; j < rounds; j++) {
+ hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, 0);
+
+ /* add entries */
+ for (i = 0; i < TEST_SIZE; i++) {
+ hashmap_entry_init(entries[i], hashes[i]);
+ hashmap_add(&map, entries[i]);
+ }
+
+ hashmap_free(&map, 0);
+ }
+ } else {
+ /* test map lookups */
+ hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, 0);
+
+ /* fill the map (sparsely if specified) */
+ j = (method & TEST_SPARSE) ? TEST_SIZE / 10 : TEST_SIZE;
+ for (i = 0; i < j; i++) {
+ hashmap_entry_init(entries[i], hashes[i]);
+ hashmap_add(&map, entries[i]);
+ }
+
+ for (j = 0; j < rounds; j++) {
+ for (i = 0; i < TEST_SIZE; i++) {
+ hashmap_get_from_hash(&map, hashes[i],
+ entries[i]->key);
+ }
+ }
+
+ hashmap_free(&map, 0);
+ }
+}
+
+#define DELIM " \t\r\n"
+
+/*
+ * Read stdin line by line and print result of commands to stdout:
+ *
+ * hash key -> strhash(key) memhash(key) strihash(key) memihash(key)
+ * put key value -> NULL / old value
+ * get key -> NULL / value
+ * remove key -> NULL / old value
+ * iterate -> key1 value1\nkey2 value2\n...
+ * size -> tablesize numentries
+ *
+ * perfhashmap method rounds -> test hashmap.[ch] performance
+ */
+int main(int argc, char *argv[])
+{
+ char line[1024];
+ struct hashmap map;
+ int icase;
+
+ /* init hash map */
+ icase = argc > 1 && !strcmp("ignorecase", argv[1]);
+ hashmap_init(&map, (hashmap_cmp_fn) (icase ? test_entry_cmp_icase
+ : test_entry_cmp), 0);
+
+ /* process commands from stdin */
+ while (fgets(line, sizeof(line), stdin)) {
+ char *cmd, *p1 = NULL, *p2 = NULL;
+ int l1 = 0, l2 = 0, hash = 0;
+ struct test_entry *entry;
+
+ /* break line into command and up to two parameters */
+ cmd = strtok(line, DELIM);
+ /* ignore empty lines */
+ if (!cmd || *cmd == '#')
+ continue;
+
+ p1 = strtok(NULL, DELIM);
+ if (p1) {
+ l1 = strlen(p1);
+ hash = icase ? strihash(p1) : strhash(p1);
+ p2 = strtok(NULL, DELIM);
+ if (p2)
+ l2 = strlen(p2);
+ }
+
+ if (!strcmp("hash", cmd) && l1) {
+
+ /* print results of different hash functions */
+ printf("%u %u %u %u\n", strhash(p1), memhash(p1, l1),
+ strihash(p1), memihash(p1, l1));
+
+ } else if (!strcmp("add", cmd) && l1 && l2) {
+
+ /* create entry with key = p1, value = p2 */
+ entry = alloc_test_entry(hash, p1, l1, p2, l2);
+
+ /* add to hashmap */
+ hashmap_add(&map, entry);
+
+ } else if (!strcmp("put", cmd) && l1 && l2) {
+
+ /* create entry with key = p1, value = p2 */
+ entry = alloc_test_entry(hash, p1, l1, p2, l2);
+
+ /* add / replace entry */
+ entry = hashmap_put(&map, entry);
+
+ /* print and free replaced entry, if any */
+ puts(entry ? get_value(entry) : "NULL");
+ free(entry);
+
+ } else if (!strcmp("get", cmd) && l1) {
+
+ /* lookup entry in hashmap */
+ entry = hashmap_get_from_hash(&map, hash, p1);
+
+ /* print result */
+ if (!entry)
+ puts("NULL");
+ while (entry) {
+ puts(get_value(entry));
+ entry = hashmap_get_next(&map, entry);
+ }
+
+ } else if (!strcmp("remove", cmd) && l1) {
+
+ /* setup static key */
+ struct hashmap_entry key;
+ hashmap_entry_init(&key, hash);
+
+ /* remove entry from hashmap */
+ entry = hashmap_remove(&map, &key, p1);
+
+ /* print result and free entry*/
+ puts(entry ? get_value(entry) : "NULL");
+ free(entry);
+
+ } else if (!strcmp("iterate", cmd)) {
+
+ struct hashmap_iter iter;
+ hashmap_iter_init(&map, &iter);
+ while ((entry = hashmap_iter_next(&iter)))
+ printf("%s %s\n", entry->key, get_value(entry));
+
+ } else if (!strcmp("size", cmd)) {
+
+ /* print table sizes */
+ printf("%u %u\n", map.tablesize, map.size);
+
+ } else if (!strcmp("intern", cmd) && l1) {
+
+ /* test that strintern works */
+ const char *i1 = strintern(p1);
+ const char *i2 = strintern(p1);
+ if (strcmp(i1, p1))
+ printf("strintern(%s) returns %s\n", p1, i1);
+ else if (i1 == p1)
+ printf("strintern(%s) returns input pointer\n", p1);
+ else if (i1 != i2)
+ printf("strintern(%s) != strintern(%s)", i1, i2);
+ else
+ printf("%s\n", i1);
+
+ } else if (!strcmp("perfhashmap", cmd) && l1 && l2) {
+
+ perf_hashmap(atoi(p1), atoi(p2));
+
+ } else {
+
+ printf("Unknown command %s\n", cmd);
+
+ }
+ }
+
+ hashmap_free(&map, 1);
+ return 0;
+}
diff --git a/t/helper/test-index-version.c b/t/helper/test-index-version.c
new file mode 100644
index 0000000..05d4699
--- /dev/null
+++ b/t/helper/test-index-version.c
@@ -0,0 +1,14 @@
+#include "cache.h"
+
+int main(int argc, char **argv)
+{
+ struct cache_header hdr;
+ int version;
+
+ memset(&hdr,0,sizeof(hdr));
+ if (read(0, &hdr, sizeof(hdr)) != sizeof(hdr))
+ return 0;
+ version = ntohl(hdr.hdr_version);
+ printf("%d\n", version);
+ return 0;
+}
diff --git a/t/helper/test-line-buffer.c b/t/helper/test-line-buffer.c
new file mode 100644
index 0000000..1e58f04
--- /dev/null
+++ b/t/helper/test-line-buffer.c
@@ -0,0 +1,91 @@
+/*
+ * test-line-buffer.c: code to exercise the svn importer's input helper
+ */
+
+#include "git-compat-util.h"
+#include "strbuf.h"
+#include "vcs-svn/line_buffer.h"
+
+static uint32_t strtouint32(const char *s)
+{
+ char *end;
+ uintmax_t n = strtoumax(s, &end, 10);
+ if (*s == '\0' || *end != '\0')
+ die("invalid count: %s", s);
+ return (uint32_t) n;
+}
+
+static void handle_command(const char *command, const char *arg, struct line_buffer *buf)
+{
+ switch (*command) {
+ case 'b':
+ if (starts_with(command, "binary ")) {
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addch(&sb, '>');
+ buffer_read_binary(buf, &sb, strtouint32(arg));
+ fwrite(sb.buf, 1, sb.len, stdout);
+ strbuf_release(&sb);
+ return;
+ }
+ case 'c':
+ if (starts_with(command, "copy ")) {
+ buffer_copy_bytes(buf, strtouint32(arg));
+ return;
+ }
+ case 's':
+ if (starts_with(command, "skip ")) {
+ buffer_skip_bytes(buf, strtouint32(arg));
+ return;
+ }
+ default:
+ die("unrecognized command: %s", command);
+ }
+}
+
+static void handle_line(const char *line, struct line_buffer *stdin_buf)
+{
+ const char *arg = strchr(line, ' ');
+ if (!arg)
+ die("no argument in line: %s", line);
+ handle_command(line, arg + 1, stdin_buf);
+}
+
+int main(int argc, char *argv[])
+{
+ struct line_buffer stdin_buf = LINE_BUFFER_INIT;
+ struct line_buffer file_buf = LINE_BUFFER_INIT;
+ struct line_buffer *input = &stdin_buf;
+ const char *filename;
+ char *s;
+
+ if (argc == 1)
+ filename = NULL;
+ else if (argc == 2)
+ filename = argv[1];
+ else
+ usage("test-line-buffer [file | &fd] < script");
+
+ if (buffer_init(&stdin_buf, NULL))
+ die_errno("open error");
+ if (filename) {
+ if (*filename == '&') {
+ if (buffer_fdinit(&file_buf, strtouint32(filename + 1)))
+ die_errno("error opening fd %s", filename + 1);
+ } else {
+ if (buffer_init(&file_buf, filename))
+ die_errno("error opening %s", filename);
+ }
+ input = &file_buf;
+ }
+
+ while ((s = buffer_read_line(&stdin_buf)))
+ handle_line(s, input);
+
+ if (filename && buffer_deinit(&file_buf))
+ die("error reading from %s", filename);
+ if (buffer_deinit(&stdin_buf))
+ die("input error");
+ if (ferror(stdout))
+ die("output error");
+ return 0;
+}
diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c
new file mode 100644
index 0000000..d446b8e
--- /dev/null
+++ b/t/helper/test-match-trees.c
@@ -0,0 +1,26 @@
+#include "cache.h"
+#include "tree.h"
+
+int main(int ac, char **av)
+{
+ struct object_id hash1, hash2, shifted;
+ struct tree *one, *two;
+
+ setup_git_directory();
+
+ if (get_oid(av[1], &hash1))
+ die("cannot parse %s as an object name", av[1]);
+ if (get_oid(av[2], &hash2))
+ die("cannot parse %s as an object name", av[2]);
+ one = parse_tree_indirect(hash1.hash);
+ if (!one)
+ die("not a tree-ish %s", av[1]);
+ two = parse_tree_indirect(hash2.hash);
+ if (!two)
+ die("not a tree-ish %s", av[2]);
+
+ shift_tree(&one->object.oid, &two->object.oid, &shifted, -1);
+ printf("shifted: %s\n", oid_to_hex(&shifted));
+
+ exit(0);
+}
diff --git a/t/helper/test-mergesort.c b/t/helper/test-mergesort.c
new file mode 100644
index 0000000..ea3b959
--- /dev/null
+++ b/t/helper/test-mergesort.c
@@ -0,0 +1,52 @@
+#include "cache.h"
+#include "mergesort.h"
+
+struct line {
+ char *text;
+ struct line *next;
+};
+
+static void *get_next(const void *a)
+{
+ return ((const struct line *)a)->next;
+}
+
+static void set_next(void *a, void *b)
+{
+ ((struct line *)a)->next = b;
+}
+
+static int compare_strings(const void *a, const void *b)
+{
+ const struct line *x = a, *y = b;
+ return strcmp(x->text, y->text);
+}
+
+int main(int argc, char **argv)
+{
+ struct line *line, *p = NULL, *lines = NULL;
+ struct strbuf sb = STRBUF_INIT;
+
+ for (;;) {
+ if (strbuf_getwholeline(&sb, stdin, '\n'))
+ break;
+ line = xmalloc(sizeof(struct line));
+ line->text = strbuf_detach(&sb, NULL);
+ if (p) {
+ line->next = p->next;
+ p->next = line;
+ } else {
+ line->next = NULL;
+ lines = line;
+ }
+ p = line;
+ }
+
+ lines = llist_mergesort(lines, get_next, set_next, compare_strings);
+
+ while (lines) {
+ printf("%s", lines->text);
+ lines = lines->next;
+ }
+ return 0;
+}
diff --git a/t/helper/test-mktemp.c b/t/helper/test-mktemp.c
new file mode 100644
index 0000000..c8c5421
--- /dev/null
+++ b/t/helper/test-mktemp.c
@@ -0,0 +1,14 @@
+/*
+ * test-mktemp.c: code to exercise the creation of temporary files
+ */
+#include "git-compat-util.h"
+
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ usage("Expected 1 parameter defining the temporary file template");
+
+ xmkstemp(xstrdup(argv[1]));
+
+ return 0;
+}
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
new file mode 100644
index 0000000..8a1235d
--- /dev/null
+++ b/t/helper/test-parse-options.c
@@ -0,0 +1,179 @@
+#include "cache.h"
+#include "parse-options.h"
+#include "string-list.h"
+
+static int boolean = 0;
+static int integer = 0;
+static unsigned long magnitude = 0;
+static unsigned long timestamp;
+static int abbrev = 7;
+static int verbose = -1; /* unspecified */
+static int dry_run = 0, quiet = 0;
+static char *string = NULL;
+static char *file = NULL;
+static int ambiguous;
+static struct string_list list;
+
+static struct {
+ int called;
+ const char *arg;
+ int unset;
+} length_cb;
+
+static int length_callback(const struct option *opt, const char *arg, int unset)
+{
+ length_cb.called = 1;
+ length_cb.arg = arg;
+ length_cb.unset = unset;
+
+ if (unset)
+ return 1; /* do not support unset */
+
+ *(int *)opt->value = strlen(arg);
+ return 0;
+}
+
+static int number_callback(const struct option *opt, const char *arg, int unset)
+{
+ *(int *)opt->value = strtol(arg, NULL, 10);
+ return 0;
+}
+
+static int collect_expect(const struct option *opt, const char *arg, int unset)
+{
+ struct string_list *expect;
+ struct string_list_item *item;
+ struct strbuf label = STRBUF_INIT;
+ const char *colon;
+
+ if (!arg || unset)
+ die("malformed --expect option");
+
+ expect = (struct string_list *)opt->value;
+ colon = strchr(arg, ':');
+ if (!colon)
+ die("malformed --expect option, lacking a colon");
+ strbuf_add(&label, arg, colon - arg);
+ item = string_list_insert(expect, strbuf_detach(&label, NULL));
+ if (item->util)
+ die("malformed --expect option, duplicate %s", label.buf);
+ item->util = (void *)arg;
+ return 0;
+}
+
+__attribute__((format (printf,3,4)))
+static void show(struct string_list *expect, int *status, const char *fmt, ...)
+{
+ struct string_list_item *item;
+ struct strbuf buf = STRBUF_INIT;
+ va_list args;
+
+ va_start(args, fmt);
+ strbuf_vaddf(&buf, fmt, args);
+ va_end(args);
+
+ if (!expect->nr)
+ printf("%s\n", buf.buf);
+ else {
+ char *colon = strchr(buf.buf, ':');
+ if (!colon)
+ die("malformed output format, output lacking colon: %s", fmt);
+ *colon = '\0';
+ item = string_list_lookup(expect, buf.buf);
+ *colon = ':';
+ if (!item)
+ ; /* not among entries being checked */
+ else {
+ if (strcmp((const char *)item->util, buf.buf)) {
+ printf("-%s\n", (char *)item->util);
+ printf("+%s\n", buf.buf);
+ *status = 1;
+ }
+ }
+ }
+ strbuf_release(&buf);
+}
+
+int main(int argc, char **argv)
+{
+ const char *prefix = "prefix/";
+ const char *usage[] = {
+ "test-parse-options <options>",
+ NULL
+ };
+ struct string_list expect = STRING_LIST_INIT_NODUP;
+ struct option options[] = {
+ OPT_BOOL(0, "yes", &boolean, "get a boolean"),
+ OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"),
+ { OPTION_SET_INT, 'B', "no-fear", &boolean, NULL,
+ "be brave", PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
+ OPT_COUNTUP('b', "boolean", &boolean, "increment by one"),
+ OPT_BIT('4', "or4", &boolean,
+ "bitwise-or boolean with ...0100", 4),
+ OPT_NEGBIT(0, "neg-or4", &boolean, "same as --no-or4", 4),
+ OPT_GROUP(""),
+ OPT_INTEGER('i', "integer", &integer, "get a integer"),
+ OPT_INTEGER('j', NULL, &integer, "get a integer, too"),
+ OPT_MAGNITUDE('m', "magnitude", &magnitude, "get a magnitude"),
+ OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23),
+ OPT_DATE('t', NULL, &timestamp, "get timestamp of <time>"),
+ OPT_CALLBACK('L', "length", &integer, "str",
+ "get length of <str>", length_callback),
+ OPT_FILENAME('F', "file", &file, "set file to <file>"),
+ OPT_GROUP("String options"),
+ OPT_STRING('s', "string", &string, "string", "get a string"),
+ OPT_STRING(0, "string2", &string, "str", "get another string"),
+ OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"),
+ OPT_STRING('o', NULL, &string, "str", "get another string"),
+ OPT_NOOP_NOARG(0, "obsolete"),
+ OPT_STRING_LIST(0, "list", &list, "str", "add str to list"),
+ OPT_GROUP("Magic arguments"),
+ OPT_ARGUMENT("quux", "means --quux"),
+ OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
+ number_callback),
+ { OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b",
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH },
+ { OPTION_COUNTUP, 0, "ambiguous", &ambiguous, NULL,
+ "positive ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG },
+ { OPTION_COUNTUP, 0, "no-ambiguous", &ambiguous, NULL,
+ "negative ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG },
+ OPT_GROUP("Standard options"),
+ OPT__ABBREV(&abbrev),
+ OPT__VERBOSE(&verbose, "be verbose"),
+ OPT__DRY_RUN(&dry_run, "dry run"),
+ OPT__QUIET(&quiet, "be quiet"),
+ OPT_CALLBACK(0, "expect", &expect, "string",
+ "expected output in the variable dump",
+ collect_expect),
+ OPT_END(),
+ };
+ int i;
+ int ret = 0;
+
+ argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0);
+
+ if (length_cb.called) {
+ const char *arg = length_cb.arg;
+ int unset = length_cb.unset;
+ show(&expect, &ret, "Callback: \"%s\", %d",
+ (arg ? arg : "not set"), unset);
+ }
+ show(&expect, &ret, "boolean: %d", boolean);
+ show(&expect, &ret, "integer: %d", integer);
+ show(&expect, &ret, "magnitude: %lu", magnitude);
+ show(&expect, &ret, "timestamp: %lu", timestamp);
+ show(&expect, &ret, "string: %s", string ? string : "(not set)");
+ show(&expect, &ret, "abbrev: %d", abbrev);
+ show(&expect, &ret, "verbose: %d", verbose);
+ show(&expect, &ret, "quiet: %d", quiet);
+ show(&expect, &ret, "dry run: %s", dry_run ? "yes" : "no");
+ show(&expect, &ret, "file: %s", file ? file : "(not set)");
+
+ for (i = 0; i < list.nr; i++)
+ show(&expect, &ret, "list: %s", list.items[i].string);
+
+ for (i = 0; i < argc; i++)
+ show(&expect, &ret, "arg %02d: %s", i, argv[i]);
+
+ return ret;
+}
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
new file mode 100644
index 0000000..ba805b3
--- /dev/null
+++ b/t/helper/test-path-utils.c
@@ -0,0 +1,262 @@
+#include "cache.h"
+#include "string-list.h"
+
+/*
+ * A "string_list_each_func_t" function that normalizes an entry from
+ * GIT_CEILING_DIRECTORIES. If the path is unusable for some reason,
+ * die with an explanation.
+ */
+static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
+{
+ char *ceil = item->string;
+
+ if (!*ceil)
+ die("Empty path is not supported");
+ if (!is_absolute_path(ceil))
+ die("Path \"%s\" is not absolute", ceil);
+ if (normalize_path_copy(ceil, ceil) < 0)
+ die("Path \"%s\" could not be normalized", ceil);
+ return 1;
+}
+
+static void normalize_argv_string(const char **var, const char *input)
+{
+ if (!strcmp(input, "<null>"))
+ *var = NULL;
+ else if (!strcmp(input, "<empty>"))
+ *var = "";
+ else
+ *var = input;
+
+ if (*var && (**var == '<' || **var == '('))
+ die("Bad value: %s\n", input);
+}
+
+struct test_data {
+ const char *from; /* input: transform from this ... */
+ const char *to; /* output: ... to this. */
+ const char *alternative; /* output: ... or this. */
+};
+
+static int test_function(struct test_data *data, char *(*func)(char *input),
+ const char *funcname)
+{
+ int failed = 0, i;
+ char buffer[1024];
+ char *to;
+
+ for (i = 0; data[i].to; i++) {
+ if (!data[i].from)
+ to = func(NULL);
+ else {
+ xsnprintf(buffer, sizeof(buffer), "%s", data[i].from);
+ to = func(buffer);
+ }
+ if (!strcmp(to, data[i].to))
+ continue;
+ if (!data[i].alternative)
+ error("FAIL: %s(%s) => '%s' != '%s'\n",
+ funcname, data[i].from, to, data[i].to);
+ else if (!strcmp(to, data[i].alternative))
+ continue;
+ else
+ error("FAIL: %s(%s) => '%s' != '%s', '%s'\n",
+ funcname, data[i].from, to, data[i].to,
+ data[i].alternative);
+ failed = 1;
+ }
+ return failed;
+}
+
+static struct test_data basename_data[] = {
+ /* --- POSIX type paths --- */
+ { NULL, "." },
+ { "", "." },
+ { ".", "." },
+ { "..", ".." },
+ { "/", "/" },
+ { "//", "/", "//" },
+ { "///", "/", "//" },
+ { "////", "/", "//" },
+ { "usr", "usr" },
+ { "/usr", "usr" },
+ { "/usr/", "usr" },
+ { "/usr//", "usr" },
+ { "/usr/lib", "lib" },
+ { "usr/lib", "lib" },
+ { "usr/lib///", "lib" },
+
+#if defined(__MINGW32__) || defined(_MSC_VER)
+ /* --- win32 type paths --- */
+ { "\\usr", "usr" },
+ { "\\usr\\", "usr" },
+ { "\\usr\\\\", "usr" },
+ { "\\usr\\lib", "lib" },
+ { "usr\\lib", "lib" },
+ { "usr\\lib\\\\\\", "lib" },
+ { "C:/usr", "usr" },
+ { "C:/usr", "usr" },
+ { "C:/usr/", "usr" },
+ { "C:/usr//", "usr" },
+ { "C:/usr/lib", "lib" },
+ { "C:usr/lib", "lib" },
+ { "C:usr/lib///", "lib" },
+ { "C:", "." },
+ { "C:a", "a" },
+ { "C:/", "/" },
+ { "C:///", "/" },
+ { "\\", "\\", "/" },
+ { "\\\\", "\\", "/" },
+ { "\\\\\\", "\\", "/" },
+#endif
+ { NULL, NULL }
+};
+
+static struct test_data dirname_data[] = {
+ /* --- POSIX type paths --- */
+ { NULL, "." },
+ { "", "." },
+ { ".", "." },
+ { "..", "." },
+ { "/", "/" },
+ { "//", "/", "//" },
+ { "///", "/", "//" },
+ { "////", "/", "//" },
+ { "usr", "." },
+ { "/usr", "/" },
+ { "/usr/", "/" },
+ { "/usr//", "/" },
+ { "/usr/lib", "/usr" },
+ { "usr/lib", "usr" },
+ { "usr/lib///", "usr" },
+
+#if defined(__MINGW32__) || defined(_MSC_VER)
+ /* --- win32 type paths --- */
+ { "\\", "\\" },
+ { "\\\\", "\\\\" },
+ { "\\usr", "\\" },
+ { "\\usr\\", "\\" },
+ { "\\usr\\\\", "\\" },
+ { "\\usr\\lib", "\\usr" },
+ { "usr\\lib", "usr" },
+ { "usr\\lib\\\\\\", "usr" },
+ { "C:a", "C:." },
+ { "C:/", "C:/" },
+ { "C:///", "C:/" },
+ { "C:/usr", "C:/" },
+ { "C:/usr/", "C:/" },
+ { "C:/usr//", "C:/" },
+ { "C:/usr/lib", "C:/usr" },
+ { "C:usr/lib", "C:usr" },
+ { "C:usr/lib///", "C:usr" },
+ { "\\\\\\", "\\" },
+ { "\\\\\\\\", "\\" },
+ { "C:", "C:.", "." },
+#endif
+ { NULL, NULL }
+};
+
+int main(int argc, char **argv)
+{
+ if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
+ char *buf = xmallocz(strlen(argv[2]));
+ int rv = normalize_path_copy(buf, argv[2]);
+ if (rv)
+ buf = "++failed++";
+ puts(buf);
+ return 0;
+ }
+
+ if (argc >= 2 && !strcmp(argv[1], "real_path")) {
+ while (argc > 2) {
+ puts(real_path(argv[2]));
+ argc--;
+ argv++;
+ }
+ return 0;
+ }
+
+ if (argc >= 2 && !strcmp(argv[1], "absolute_path")) {
+ while (argc > 2) {
+ puts(absolute_path(argv[2]));
+ argc--;
+ argv++;
+ }
+ return 0;
+ }
+
+ if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) {
+ int len;
+ struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
+ char *path = xstrdup(argv[2]);
+
+ /*
+ * We have to normalize the arguments because under
+ * Windows, bash mangles arguments that look like
+ * absolute POSIX paths or colon-separate lists of
+ * absolute POSIX paths into DOS paths (e.g.,
+ * "/foo:/foo/bar" might be converted to
+ * "D:\Src\msysgit\foo;D:\Src\msysgit\foo\bar"),
+ * whereas longest_ancestor_length() requires paths
+ * that use forward slashes.
+ */
+ if (normalize_path_copy(path, path))
+ die("Path \"%s\" could not be normalized", argv[2]);
+ string_list_split(&ceiling_dirs, argv[3], PATH_SEP, -1);
+ filter_string_list(&ceiling_dirs, 0,
+ normalize_ceiling_entry, NULL);
+ len = longest_ancestor_length(path, &ceiling_dirs);
+ string_list_clear(&ceiling_dirs, 0);
+ free(path);
+ printf("%d\n", len);
+ return 0;
+ }
+
+ if (argc >= 4 && !strcmp(argv[1], "prefix_path")) {
+ char *prefix = argv[2];
+ int prefix_len = strlen(prefix);
+ int nongit_ok;
+ setup_git_directory_gently(&nongit_ok);
+ while (argc > 3) {
+ puts(prefix_path(prefix, prefix_len, argv[3]));
+ argc--;
+ argv++;
+ }
+ return 0;
+ }
+
+ if (argc == 4 && !strcmp(argv[1], "strip_path_suffix")) {
+ char *prefix = strip_path_suffix(argv[2], argv[3]);
+ printf("%s\n", prefix ? prefix : "(null)");
+ return 0;
+ }
+
+ if (argc == 3 && !strcmp(argv[1], "print_path")) {
+ puts(argv[2]);
+ return 0;
+ }
+
+ if (argc == 4 && !strcmp(argv[1], "relative_path")) {
+ struct strbuf sb = STRBUF_INIT;
+ const char *in, *prefix, *rel;
+ normalize_argv_string(&in, argv[2]);
+ normalize_argv_string(&prefix, argv[3]);
+ rel = relative_path(in, prefix, &sb);
+ if (!rel)
+ puts("(null)");
+ else
+ puts(strlen(rel) > 0 ? rel : "(empty)");
+ strbuf_release(&sb);
+ return 0;
+ }
+
+ if (argc == 2 && !strcmp(argv[1], "basename"))
+ return test_function(basename_data, basename, argv[1]);
+
+ if (argc == 2 && !strcmp(argv[1], "dirname"))
+ return test_function(dirname_data, dirname, argv[1]);
+
+ fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
+ argv[1] ? argv[1] : "(there was none)");
+ return 1;
+}
diff --git a/t/helper/test-prio-queue.c b/t/helper/test-prio-queue.c
new file mode 100644
index 0000000..7be72f0
--- /dev/null
+++ b/t/helper/test-prio-queue.c
@@ -0,0 +1,39 @@
+#include "cache.h"
+#include "prio-queue.h"
+
+static int intcmp(const void *va, const void *vb, void *data)
+{
+ const int *a = va, *b = vb;
+ return *a - *b;
+}
+
+static void show(int *v)
+{
+ if (!v)
+ printf("NULL\n");
+ else
+ printf("%d\n", *v);
+ free(v);
+}
+
+int main(int argc, char **argv)
+{
+ struct prio_queue pq = { intcmp };
+
+ while (*++argv) {
+ if (!strcmp(*argv, "get"))
+ show(prio_queue_get(&pq));
+ else if (!strcmp(*argv, "dump")) {
+ int *v;
+ while ((v = prio_queue_get(&pq)))
+ show(v);
+ }
+ else {
+ int *v = malloc(sizeof(*v));
+ *v = atoi(*argv);
+ prio_queue_put(&pq, v);
+ }
+ }
+
+ return 0;
+}
diff --git a/t/helper/test-read-cache.c b/t/helper/test-read-cache.c
new file mode 100644
index 0000000..b25bcf1
--- /dev/null
+++ b/t/helper/test-read-cache.c
@@ -0,0 +1,13 @@
+#include "cache.h"
+
+int main (int argc, char **argv)
+{
+ int i, cnt = 1;
+ if (argc == 2)
+ cnt = strtol(argv[1], NULL, 0);
+ for (i = 0; i < cnt; i++) {
+ read_cache();
+ discard_cache();
+ }
+ return 0;
+}
diff --git a/t/helper/test-regex.c b/t/helper/test-regex.c
new file mode 100644
index 0000000..0dc598e
--- /dev/null
+++ b/t/helper/test-regex.c
@@ -0,0 +1,20 @@
+#include "git-compat-util.h"
+
+int main(int argc, char **argv)
+{
+ char *pat = "[^={} \t]+";
+ char *str = "={}\nfred";
+ regex_t r;
+ regmatch_t m[1];
+
+ if (regcomp(&r, pat, REG_EXTENDED | REG_NEWLINE))
+ die("failed regcomp() for pattern '%s'", pat);
+ if (regexec(&r, str, 1, m, 0))
+ die("no match of pattern '%s' to string '%s'", pat, str);
+
+ /* http://sourceware.org/bugzilla/show_bug.cgi?id=3957 */
+ if (m[0].rm_so == 3) /* matches '\n' when it should not */
+ die("regex bug confirmed: re-build git with NO_REGEX=1");
+
+ exit(0);
+}
diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c
new file mode 100644
index 0000000..3d03133
--- /dev/null
+++ b/t/helper/test-revision-walking.c
@@ -0,0 +1,68 @@
+/*
+ * test-revision-walking.c: test revision walking API.
+ *
+ * (C) 2012 Heiko Voigt <hvoigt@hvoigt.net>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+
+static void print_commit(struct commit *commit)
+{
+ struct strbuf sb = STRBUF_INIT;
+ struct pretty_print_context ctx = {0};
+ ctx.date_mode.type = DATE_NORMAL;
+ format_commit_message(commit, " %m %s", &sb, &ctx);
+ printf("%s\n", sb.buf);
+ strbuf_release(&sb);
+}
+
+static int run_revision_walk(void)
+{
+ struct rev_info rev;
+ struct commit *commit;
+ const char *argv[] = {NULL, "--all", NULL};
+ int argc = ARRAY_SIZE(argv) - 1;
+ int got_revision = 0;
+
+ init_revisions(&rev, NULL);
+ setup_revisions(argc, argv, &rev, NULL);
+ if (prepare_revision_walk(&rev))
+ die("revision walk setup failed");
+
+ while ((commit = get_revision(&rev)) != NULL) {
+ print_commit(commit);
+ got_revision = 1;
+ }
+
+ reset_revision_walk();
+ return got_revision;
+}
+
+int main(int argc, char **argv)
+{
+ if (argc < 2)
+ return 1;
+
+ setup_git_directory();
+
+ if (!strcmp(argv[1], "run-twice")) {
+ printf("1st\n");
+ if (!run_revision_walk())
+ return 1;
+ printf("2nd\n");
+ if (!run_revision_walk())
+ return 1;
+
+ return 0;
+ }
+
+ fprintf(stderr, "check usage\n");
+ return 1;
+}
diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c
new file mode 100644
index 0000000..30a64a9
--- /dev/null
+++ b/t/helper/test-run-command.c
@@ -0,0 +1,87 @@
+/*
+ * test-run-command.c: test run command API.
+ *
+ * (C) 2009 Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "git-compat-util.h"
+#include "run-command.h"
+#include "argv-array.h"
+#include "strbuf.h"
+#include <string.h>
+#include <errno.h>
+
+static int number_callbacks;
+static int parallel_next(struct child_process *cp,
+ struct strbuf *err,
+ void *cb,
+ void **task_cb)
+{
+ struct child_process *d = cb;
+ if (number_callbacks >= 4)
+ return 0;
+
+ argv_array_pushv(&cp->args, d->argv);
+ strbuf_addf(err, "preloaded output of a child\n");
+ number_callbacks++;
+ return 1;
+}
+
+static int no_job(struct child_process *cp,
+ struct strbuf *err,
+ void *cb,
+ void **task_cb)
+{
+ strbuf_addf(err, "no further jobs available\n");
+ return 0;
+}
+
+static int task_finished(int result,
+ struct strbuf *err,
+ void *pp_cb,
+ void *pp_task_cb)
+{
+ strbuf_addf(err, "asking for a quick stop\n");
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ struct child_process proc = CHILD_PROCESS_INIT;
+ int jobs;
+
+ if (argc < 3)
+ return 1;
+ proc.argv = (const char **)argv + 2;
+
+ if (!strcmp(argv[1], "start-command-ENOENT")) {
+ if (start_command(&proc) < 0 && errno == ENOENT)
+ return 0;
+ fprintf(stderr, "FAIL %s\n", argv[1]);
+ return 1;
+ }
+ if (!strcmp(argv[1], "run-command"))
+ exit(run_command(&proc));
+
+ jobs = atoi(argv[2]);
+ proc.argv = (const char **)argv + 3;
+
+ if (!strcmp(argv[1], "run-command-parallel"))
+ exit(run_processes_parallel(jobs, parallel_next,
+ NULL, NULL, &proc));
+
+ if (!strcmp(argv[1], "run-command-abort"))
+ exit(run_processes_parallel(jobs, parallel_next,
+ NULL, task_finished, &proc));
+
+ if (!strcmp(argv[1], "run-command-no-jobs"))
+ exit(run_processes_parallel(jobs, no_job,
+ NULL, task_finished, &proc));
+
+ fprintf(stderr, "check usage\n");
+ return 1;
+}
diff --git a/t/helper/test-scrap-cache-tree.c b/t/helper/test-scrap-cache-tree.c
new file mode 100644
index 0000000..6efee31
--- /dev/null
+++ b/t/helper/test-scrap-cache-tree.c
@@ -0,0 +1,17 @@
+#include "cache.h"
+#include "lockfile.h"
+#include "tree.h"
+#include "cache-tree.h"
+
+static struct lock_file index_lock;
+
+int main(int ac, char **av)
+{
+ hold_locked_index(&index_lock, 1);
+ if (read_cache() < 0)
+ die("unable to read index file");
+ active_cache_tree = NULL;
+ if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
+ die("unable to write index file");
+ return 0;
+}
diff --git a/t/helper/test-sha1-array.c b/t/helper/test-sha1-array.c
new file mode 100644
index 0000000..60ea1d5
--- /dev/null
+++ b/t/helper/test-sha1-array.c
@@ -0,0 +1,34 @@
+#include "cache.h"
+#include "sha1-array.h"
+
+static void print_sha1(const unsigned char sha1[20], void *data)
+{
+ puts(sha1_to_hex(sha1));
+}
+
+int main(int argc, char **argv)
+{
+ struct sha1_array array = SHA1_ARRAY_INIT;
+ struct strbuf line = STRBUF_INIT;
+
+ while (strbuf_getline(&line, stdin) != EOF) {
+ const char *arg;
+ unsigned char sha1[20];
+
+ if (skip_prefix(line.buf, "append ", &arg)) {
+ if (get_sha1_hex(arg, sha1))
+ die("not a hexadecimal SHA1: %s", arg);
+ sha1_array_append(&array, sha1);
+ } else if (skip_prefix(line.buf, "lookup ", &arg)) {
+ if (get_sha1_hex(arg, sha1))
+ die("not a hexadecimal SHA1: %s", arg);
+ printf("%d\n", sha1_array_lookup(&array, sha1));
+ } else if (!strcmp(line.buf, "clear"))
+ sha1_array_clear(&array);
+ else if (!strcmp(line.buf, "for_each_unique"))
+ sha1_array_for_each_unique(&array, print_sha1, NULL);
+ else
+ die("unknown command: %s", line.buf);
+ }
+ return 0;
+}
diff --git a/t/helper/test-sha1.c b/t/helper/test-sha1.c
new file mode 100644
index 0000000..e57eae1
--- /dev/null
+++ b/t/helper/test-sha1.c
@@ -0,0 +1,56 @@
+#include "cache.h"
+
+int main(int ac, char **av)
+{
+ git_SHA_CTX ctx;
+ unsigned char sha1[20];
+ unsigned bufsz = 8192;
+ int binary = 0;
+ char *buffer;
+
+ if (ac == 2) {
+ if (!strcmp(av[1], "-b"))
+ binary = 1;
+ else
+ bufsz = strtoul(av[1], NULL, 10) * 1024 * 1024;
+ }
+
+ if (!bufsz)
+ bufsz = 8192;
+
+ while ((buffer = malloc(bufsz)) == NULL) {
+ fprintf(stderr, "bufsz %u is too big, halving...\n", bufsz);
+ bufsz /= 2;
+ if (bufsz < 1024)
+ die("OOPS");
+ }
+
+ git_SHA1_Init(&ctx);
+
+ while (1) {
+ ssize_t sz, this_sz;
+ char *cp = buffer;
+ unsigned room = bufsz;
+ this_sz = 0;
+ while (room) {
+ sz = xread(0, cp, room);
+ if (sz == 0)
+ break;
+ if (sz < 0)
+ die_errno("test-sha1");
+ this_sz += sz;
+ cp += sz;
+ room -= sz;
+ }
+ if (this_sz == 0)
+ break;
+ git_SHA1_Update(&ctx, buffer, this_sz);
+ }
+ git_SHA1_Final(sha1, &ctx);
+
+ if (binary)
+ fwrite(sha1, 1, 20, stdout);
+ else
+ puts(sha1_to_hex(sha1));
+ exit(0);
+}
diff --git a/t/helper/test-sha1.sh b/t/helper/test-sha1.sh
new file mode 100755
index 0000000..750b95a
--- /dev/null
+++ b/t/helper/test-sha1.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+dd if=/dev/zero bs=1048576 count=100 2>/dev/null |
+/usr/bin/time t/helper/test-sha1 >/dev/null
+
+while read expect cnt pfx
+do
+ case "$expect" in '#'*) continue ;; esac
+ actual=$(
+ {
+ test -z "$pfx" || echo "$pfx"
+ dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null |
+ perl -pe 'y/\000/g/'
+ } | ./t/helper/test-sha1 $cnt
+ )
+ if test "$expect" = "$actual"
+ then
+ echo "OK: $expect $cnt $pfx"
+ else
+ echo >&2 "OOPS: $cnt"
+ echo >&2 "expect: $expect"
+ echo >&2 "actual: $actual"
+ exit 1
+ fi
+done <<EOF
+da39a3ee5e6b4b0d3255bfef95601890afd80709 0
+3f786850e387550fdab836ed7e6dc881de23001b 0 a
+5277cbb45a15902137d332d97e89cf8136545485 0 ab
+03cfd743661f07975fa2f1220c5194cbaff48451 0 abc
+3330b4373640f9e4604991e73c7e86bfd8da2dc3 0 abcd
+ec11312386ad561674f724b8cca7cf1796e26d1d 0 abcde
+bdc37c074ec4ee6050d68bc133c6b912f36474df 0 abcdef
+69bca99b923859f2dc486b55b87f49689b7358c7 0 abcdefg
+e414af7161c9554089f4106d6f1797ef14a73666 0 abcdefgh
+0707f2970043f9f7c22029482db27733deaec029 0 abcdefghi
+a4dd8aa74a5636728fe52451636e2e17726033aa 1
+9986b45e2f4d7086372533bb6953a8652fa3644a 1 frotz
+23d8d4f788e8526b4877548a32577543cbaaf51f 10
+8cd23f822ab44c7f481b8c92d591f6d1fcad431c 10 frotz
+f3b5604a4e604899c1233edb3bf1cc0ede4d8c32 512
+b095bd837a371593048136e429e9ac4b476e1bb3 512 frotz
+08fa81d6190948de5ccca3966340cc48c10cceac 1200 xyzzy
+e33a291f42c30a159733dd98b8b3e4ff34158ca0 4090 4G
+#a3bf783bc20caa958f6cb24dd140a7b21984838d 9999 nitfol
+EOF
+
+exit
+
+# generating test vectors
+# inputs are number of megabytes followed by some random string to prefix.
+
+while read cnt pfx
+do
+ actual=$(
+ {
+ test -z "$pfx" || echo "$pfx"
+ dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null |
+ perl -pe 'y/\000/g/'
+ } | sha1sum |
+ sed -e 's/ .*//'
+ )
+ echo "$actual $cnt $pfx"
+done <<EOF
+0
+0 a
+0 ab
+0 abc
+0 abcd
+0 abcde
+0 abcdef
+0 abcdefg
+0 abcdefgh
+0 abcdefghi
+1
+1 frotz
+10
+10 frotz
+512
+512 frotz
+1200 xyzzy
+4090 4G
+9999 nitfol
+EOF
diff --git a/t/helper/test-sigchain.c b/t/helper/test-sigchain.c
new file mode 100644
index 0000000..e499fce
--- /dev/null
+++ b/t/helper/test-sigchain.c
@@ -0,0 +1,22 @@
+#include "cache.h"
+#include "sigchain.h"
+
+#define X(f) \
+static void f(int sig) { \
+ puts(#f); \
+ fflush(stdout); \
+ sigchain_pop(sig); \
+ raise(sig); \
+}
+X(one)
+X(two)
+X(three)
+#undef X
+
+int main(int argc, char **argv) {
+ sigchain_push(SIGTERM, one);
+ sigchain_push(SIGTERM, two);
+ sigchain_push(SIGTERM, three);
+ raise(SIGTERM);
+ return 0;
+}
diff --git a/t/helper/test-string-list.c b/t/helper/test-string-list.c
new file mode 100644
index 0000000..14bdf9d
--- /dev/null
+++ b/t/helper/test-string-list.c
@@ -0,0 +1,103 @@
+#include "cache.h"
+#include "string-list.h"
+
+/*
+ * Parse an argument into a string list. arg should either be a
+ * ':'-separated list of strings, or "-" to indicate an empty string
+ * list (as opposed to "", which indicates a string list containing a
+ * single empty string). list->strdup_strings must be set.
+ */
+static void parse_string_list(struct string_list *list, const char *arg)
+{
+ if (!strcmp(arg, "-"))
+ return;
+
+ (void)string_list_split(list, arg, ':', -1);
+}
+
+static void write_list(const struct string_list *list)
+{
+ int i;
+ for (i = 0; i < list->nr; i++)
+ printf("[%d]: \"%s\"\n", i, list->items[i].string);
+}
+
+static void write_list_compact(const struct string_list *list)
+{
+ int i;
+ if (!list->nr)
+ printf("-\n");
+ else {
+ printf("%s", list->items[0].string);
+ for (i = 1; i < list->nr; i++)
+ printf(":%s", list->items[i].string);
+ printf("\n");
+ }
+}
+
+static int prefix_cb(struct string_list_item *item, void *cb_data)
+{
+ const char *prefix = (const char *)cb_data;
+ return starts_with(item->string, prefix);
+}
+
+int main(int argc, char **argv)
+{
+ if (argc == 5 && !strcmp(argv[1], "split")) {
+ struct string_list list = STRING_LIST_INIT_DUP;
+ int i;
+ const char *s = argv[2];
+ int delim = *argv[3];
+ int maxsplit = atoi(argv[4]);
+
+ i = string_list_split(&list, s, delim, maxsplit);
+ printf("%d\n", i);
+ write_list(&list);
+ string_list_clear(&list, 0);
+ return 0;
+ }
+
+ if (argc == 5 && !strcmp(argv[1], "split_in_place")) {
+ struct string_list list = STRING_LIST_INIT_NODUP;
+ int i;
+ char *s = xstrdup(argv[2]);
+ int delim = *argv[3];
+ int maxsplit = atoi(argv[4]);
+
+ i = string_list_split_in_place(&list, s, delim, maxsplit);
+ printf("%d\n", i);
+ write_list(&list);
+ string_list_clear(&list, 0);
+ free(s);
+ return 0;
+ }
+
+ if (argc == 4 && !strcmp(argv[1], "filter")) {
+ /*
+ * Retain only the items that have the specified prefix.
+ * Arguments: list|- prefix
+ */
+ struct string_list list = STRING_LIST_INIT_DUP;
+ const char *prefix = argv[3];
+
+ parse_string_list(&list, argv[2]);
+ filter_string_list(&list, 0, prefix_cb, (void *)prefix);
+ write_list_compact(&list);
+ string_list_clear(&list, 0);
+ return 0;
+ }
+
+ if (argc == 3 && !strcmp(argv[1], "remove_duplicates")) {
+ struct string_list list = STRING_LIST_INIT_DUP;
+
+ parse_string_list(&list, argv[2]);
+ string_list_remove_duplicates(&list, 0);
+ write_list_compact(&list);
+ string_list_clear(&list, 0);
+ return 0;
+ }
+
+ fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
+ argv[1] ? argv[1] : "(there was none)");
+ return 1;
+}
diff --git a/t/helper/test-submodule-config.c b/t/helper/test-submodule-config.c
new file mode 100644
index 0000000..dab8c27
--- /dev/null
+++ b/t/helper/test-submodule-config.c
@@ -0,0 +1,76 @@
+#include "cache.h"
+#include "submodule-config.h"
+#include "submodule.h"
+
+static void die_usage(int argc, char **argv, const char *msg)
+{
+ fprintf(stderr, "%s\n", msg);
+ fprintf(stderr, "Usage: %s [<commit> <submodulepath>] ...\n", argv[0]);
+ exit(1);
+}
+
+static int git_test_config(const char *var, const char *value, void *cb)
+{
+ return parse_submodule_config_option(var, value);
+}
+
+int main(int argc, char **argv)
+{
+ char **arg = argv;
+ int my_argc = argc;
+ int output_url = 0;
+ int lookup_name = 0;
+
+ arg++;
+ my_argc--;
+ while (starts_with(arg[0], "--")) {
+ if (!strcmp(arg[0], "--url"))
+ output_url = 1;
+ if (!strcmp(arg[0], "--name"))
+ lookup_name = 1;
+ arg++;
+ my_argc--;
+ }
+
+ if (my_argc % 2 != 0)
+ die_usage(argc, argv, "Wrong number of arguments.");
+
+ setup_git_directory();
+ gitmodules_config();
+ git_config(git_test_config, NULL);
+
+ while (*arg) {
+ unsigned char commit_sha1[20];
+ const struct submodule *submodule;
+ const char *commit;
+ const char *path_or_name;
+
+ commit = arg[0];
+ path_or_name = arg[1];
+
+ if (commit[0] == '\0')
+ hashcpy(commit_sha1, null_sha1);
+ else if (get_sha1(commit, commit_sha1) < 0)
+ die_usage(argc, argv, "Commit not found.");
+
+ if (lookup_name) {
+ submodule = submodule_from_name(commit_sha1, path_or_name);
+ } else
+ submodule = submodule_from_path(commit_sha1, path_or_name);
+ if (!submodule)
+ die_usage(argc, argv, "Submodule not found.");
+
+ if (output_url)
+ printf("Submodule url: '%s' for path '%s'\n",
+ submodule->url, submodule->path);
+ else
+ printf("Submodule name: '%s' for path '%s'\n",
+ submodule->name, submodule->path);
+
+ arg += 2;
+ }
+
+ submodule_free();
+
+ return 0;
+}
diff --git a/t/helper/test-subprocess.c b/t/helper/test-subprocess.c
new file mode 100644
index 0000000..56881a0
--- /dev/null
+++ b/t/helper/test-subprocess.c
@@ -0,0 +1,19 @@
+#include "cache.h"
+#include "run-command.h"
+
+int main(int argc, char **argv)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ int nogit = 0;
+
+ setup_git_directory_gently(&nogit);
+ if (nogit)
+ die("No git repo found");
+ if (argc > 1 && !strcmp(argv[1], "--setup-work-tree")) {
+ setup_work_tree();
+ argv++;
+ }
+ cp.git_cmd = 1;
+ cp.argv = (const char **)argv + 1;
+ return run_command(&cp);
+}
diff --git a/t/helper/test-svn-fe.c b/t/helper/test-svn-fe.c
new file mode 100644
index 0000000..120ec96
--- /dev/null
+++ b/t/helper/test-svn-fe.c
@@ -0,0 +1,52 @@
+/*
+ * test-svn-fe: Code to exercise the svn import lib
+ */
+
+#include "git-compat-util.h"
+#include "vcs-svn/svndump.h"
+#include "vcs-svn/svndiff.h"
+#include "vcs-svn/sliding_window.h"
+#include "vcs-svn/line_buffer.h"
+
+static const char test_svnfe_usage[] =
+ "test-svn-fe (<dumpfile> | [-d] <preimage> <delta> <len>)";
+
+static int apply_delta(int argc, char *argv[])
+{
+ struct line_buffer preimage = LINE_BUFFER_INIT;
+ struct line_buffer delta = LINE_BUFFER_INIT;
+ struct sliding_view preimage_view = SLIDING_VIEW_INIT(&preimage, -1);
+
+ if (argc != 5)
+ usage(test_svnfe_usage);
+
+ if (buffer_init(&preimage, argv[2]))
+ die_errno("cannot open preimage");
+ if (buffer_init(&delta, argv[3]))
+ die_errno("cannot open delta");
+ if (svndiff0_apply(&delta, (off_t) strtoumax(argv[4], NULL, 0),
+ &preimage_view, stdout))
+ return 1;
+ if (buffer_deinit(&preimage))
+ die_errno("cannot close preimage");
+ if (buffer_deinit(&delta))
+ die_errno("cannot close delta");
+ strbuf_release(&preimage_view.buf);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc == 2) {
+ if (svndump_init(argv[1]))
+ return 1;
+ svndump_read(NULL, "refs/heads/master", "refs/notes/svn/revs");
+ svndump_deinit();
+ svndump_reset();
+ return 0;
+ }
+
+ if (argc >= 2 && !strcmp(argv[1], "-d"))
+ return apply_delta(argc, argv);
+ usage(test_svnfe_usage);
+}
diff --git a/t/helper/test-urlmatch-normalization.c b/t/helper/test-urlmatch-normalization.c
new file mode 100644
index 0000000..090bf21
--- /dev/null
+++ b/t/helper/test-urlmatch-normalization.c
@@ -0,0 +1,50 @@
+#include "git-compat-util.h"
+#include "urlmatch.h"
+
+int main(int argc, char **argv)
+{
+ const char usage[] = "test-urlmatch-normalization [-p | -l] <url1> | <url1> <url2>";
+ char *url1, *url2;
+ int opt_p = 0, opt_l = 0;
+
+ /*
+ * For one url, succeed if url_normalize succeeds on it, fail otherwise.
+ * For two urls, succeed only if url_normalize succeeds on both and
+ * the results compare equal with strcmp. If -p is given (one url only)
+ * and url_normalize succeeds, print the result followed by "\n". If
+ * -l is given (one url only) and url_normalize succeeds, print the
+ * returned length in decimal followed by "\n".
+ */
+
+ if (argc > 1 && !strcmp(argv[1], "-p")) {
+ opt_p = 1;
+ argc--;
+ argv++;
+ } else if (argc > 1 && !strcmp(argv[1], "-l")) {
+ opt_l = 1;
+ argc--;
+ argv++;
+ }
+
+ if (argc < 2 || argc > 3)
+ die("%s", usage);
+
+ if (argc == 2) {
+ struct url_info info;
+ url1 = url_normalize(argv[1], &info);
+ if (!url1)
+ return 1;
+ if (opt_p)
+ printf("%s\n", url1);
+ if (opt_l)
+ printf("%u\n", (unsigned)info.url_len);
+ return 0;
+ }
+
+ if (opt_p || opt_l)
+ die("%s", usage);
+
+ url1 = url_normalize(argv[1], NULL);
+ url2 = url_normalize(argv[2], NULL);
+ return (url1 && url2 && !strcmp(url1, url2)) ? 0 : 1;
+}
diff --git a/t/helper/test-wildmatch.c b/t/helper/test-wildmatch.c
new file mode 100644
index 0000000..578b164
--- /dev/null
+++ b/t/helper/test-wildmatch.c
@@ -0,0 +1,21 @@
+#include "cache.h"
+
+int main(int argc, char **argv)
+{
+ int i;
+ for (i = 2; i < argc; i++) {
+ if (argv[i][0] == '/')
+ die("Forward slash is not allowed at the beginning of the\n"
+ "pattern because Windows does not like it. Use `XXX/' instead.");
+ else if (!strncmp(argv[i], "XXX/", 4))
+ argv[i] += 3;
+ }
+ if (!strcmp(argv[1], "wildmatch"))
+ return !!wildmatch(argv[3], argv[2], WM_PATHNAME, NULL);
+ else if (!strcmp(argv[1], "iwildmatch"))
+ return !!wildmatch(argv[3], argv[2], WM_PATHNAME | WM_CASEFOLD, NULL);
+ else if (!strcmp(argv[1], "pathmatch"))
+ return !!wildmatch(argv[3], argv[2], 0, NULL);
+ else
+ return 1;
+}
diff --git a/t/lib-bash.sh b/t/lib-bash.sh
new file mode 100644
index 0000000..2be955f
--- /dev/null
+++ b/t/lib-bash.sh
@@ -0,0 +1,17 @@
+# Shell library sourced instead of ./test-lib.sh by tests that need
+# to run under Bash; primarily intended for tests of the completion
+# script.
+
+if test -n "$BASH" && test -z "$POSIXLY_CORRECT"; then
+ # we are in full-on bash mode
+ true
+elif type bash >/dev/null 2>&1; then
+ # execute in full-on bash mode
+ unset POSIXLY_CORRECT
+ exec bash "$0" "$@"
+else
+ echo '1..0 #SKIP skipping bash completion tests; bash not available'
+ exit 0
+fi
+
+. ./test-lib.sh
diff --git a/t/lib-credential.sh b/t/lib-credential.sh
new file mode 100755
index 0000000..d8e41f7
--- /dev/null
+++ b/t/lib-credential.sh
@@ -0,0 +1,287 @@
+#!/bin/sh
+
+# Try a set of credential helpers; the expected stdin,
+# stdout and stderr should be provided on stdin,
+# separated by "--".
+check() {
+ credential_opts=
+ credential_cmd=$1
+ shift
+ for arg in "$@"; do
+ credential_opts="$credential_opts -c credential.helper='$arg'"
+ done
+ read_chunk >stdin &&
+ read_chunk >expect-stdout &&
+ read_chunk >expect-stderr &&
+ if ! eval "git $credential_opts credential $credential_cmd <stdin >stdout 2>stderr"; then
+ echo "git credential failed with code $?" &&
+ cat stderr &&
+ false
+ fi &&
+ test_cmp expect-stdout stdout &&
+ test_cmp expect-stderr stderr
+}
+
+read_chunk() {
+ while read line; do
+ case "$line" in
+ --) break ;;
+ *) echo "$line" ;;
+ esac
+ done
+}
+
+# Clear any residual data from previous tests. We only
+# need this when testing third-party helpers which read and
+# write outside of our trash-directory sandbox.
+#
+# Don't bother checking for success here, as it is
+# outside the scope of tests and represents a best effort to
+# clean up after ourselves.
+helper_test_clean() {
+ reject $1 https example.com store-user
+ reject $1 https example.com user1
+ reject $1 https example.com user2
+ reject $1 http path.tld user
+ reject $1 https timeout.tld user
+}
+
+reject() {
+ (
+ echo protocol=$2
+ echo host=$3
+ echo username=$4
+ ) | git -c credential.helper=$1 credential reject
+}
+
+helper_test() {
+ HELPER=$1
+
+ test_expect_success "helper ($HELPER) has no existing data" '
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ --
+ protocol=https
+ host=example.com
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://example.com'\'':
+ askpass: Password for '\''https://askpass-username@example.com'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) stores password" '
+ check approve $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=store-user
+ password=store-pass
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) can retrieve password" '
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ --
+ protocol=https
+ host=example.com
+ username=store-user
+ password=store-pass
+ --
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) requires matching protocol" '
+ check fill $HELPER <<-\EOF
+ protocol=http
+ host=example.com
+ --
+ protocol=http
+ host=example.com
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''http://example.com'\'':
+ askpass: Password for '\''http://askpass-username@example.com'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) requires matching host" '
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=other.tld
+ --
+ protocol=https
+ host=other.tld
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://other.tld'\'':
+ askpass: Password for '\''https://askpass-username@other.tld'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) requires matching username" '
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=other
+ --
+ protocol=https
+ host=example.com
+ username=other
+ password=askpass-password
+ --
+ askpass: Password for '\''https://other@example.com'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) requires matching path" '
+ test_config credential.usehttppath true &&
+ check approve $HELPER <<-\EOF &&
+ protocol=http
+ host=path.tld
+ path=foo.git
+ username=user
+ password=pass
+ EOF
+ check fill $HELPER <<-\EOF
+ protocol=http
+ host=path.tld
+ path=bar.git
+ --
+ protocol=http
+ host=path.tld
+ path=bar.git
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''http://path.tld/bar.git'\'':
+ askpass: Password for '\''http://askpass-username@path.tld/bar.git'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) can forget host" '
+ check reject $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ EOF
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ --
+ protocol=https
+ host=example.com
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://example.com'\'':
+ askpass: Password for '\''https://askpass-username@example.com'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) can store multiple users" '
+ check approve $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user1
+ password=pass1
+ EOF
+ check approve $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user2
+ password=pass2
+ EOF
+ check fill $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user1
+ --
+ protocol=https
+ host=example.com
+ username=user1
+ password=pass1
+ EOF
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user2
+ --
+ protocol=https
+ host=example.com
+ username=user2
+ password=pass2
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) can forget user" '
+ check reject $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user1
+ EOF
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user1
+ --
+ protocol=https
+ host=example.com
+ username=user1
+ password=askpass-password
+ --
+ askpass: Password for '\''https://user1@example.com'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) remembers other user" '
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user2
+ --
+ protocol=https
+ host=example.com
+ username=user2
+ password=pass2
+ EOF
+ '
+}
+
+helper_test_timeout() {
+ HELPER="$*"
+
+ test_expect_success "helper ($HELPER) times out" '
+ check approve "$HELPER" <<-\EOF &&
+ protocol=https
+ host=timeout.tld
+ username=user
+ password=pass
+ EOF
+ sleep 2 &&
+ check fill "$HELPER" <<-\EOF
+ protocol=https
+ host=timeout.tld
+ --
+ protocol=https
+ host=timeout.tld
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://timeout.tld'\'':
+ askpass: Password for '\''https://askpass-username@timeout.tld'\'':
+ EOF
+ '
+}
+
+write_script askpass <<\EOF
+echo >&2 askpass: $*
+what=$(echo $1 | cut -d" " -f1 | tr A-Z a-z | tr -cd a-z)
+echo "askpass-$what"
+EOF
+GIT_ASKPASS="$PWD/askpass"
+export GIT_ASKPASS
diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh
index 648d161..9b2bcfb 100644
--- a/t/lib-cvs.sh
+++ b/t/lib-cvs.sh
@@ -1,11 +1,8 @@
-#!/bin/sh
+# Shell library sourced instead of ./test-lib.sh by cvsimport tests.
. ./test-lib.sh
unset CVS_SERVER
-# for clean cvsps cache
-HOME=$(pwd)
-export HOME
if ! type cvs >/dev/null 2>&1
then
@@ -16,7 +13,7 @@ fi
CVS="cvs -f"
export CVS
-cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`
+cvsps_version=$(cvsps -h 2>&1 | sed -ne 's/cvsps version //p')
case "$cvsps_version" in
2.1 | 2.2*)
;;
@@ -30,6 +27,12 @@ case "$cvsps_version" in
;;
esac
+setup_cvs_test_repository () {
+ CVSROOT="$(pwd)/.cvsroot" &&
+ cp -r "$TEST_DIRECTORY/$1/cvsroot" "$CVSROOT" &&
+ export CVSROOT
+}
+
test_cvs_co () {
# Usage: test_cvs_co BRANCH_NAME
rm -rf module-cvs-"$1"
diff --git a/t/lib-diff-alternative.sh b/t/lib-diff-alternative.sh
new file mode 100644
index 0000000..8b4dbf2
--- /dev/null
+++ b/t/lib-diff-alternative.sh
@@ -0,0 +1,166 @@
+# Helpers shared by the test scripts for diff algorithms (patience,
+# histogram, etc).
+
+test_diff_frobnitz() {
+ 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
+
+ STRATEGY=$1
+
+ test_expect_success "$STRATEGY diff" '
+ test_must_fail git diff --no-index "--$STRATEGY" file1 file2 > output &&
+ test_cmp expect output
+ '
+
+ test_expect_success "$STRATEGY diff output is valid" '
+ mv file2 expect &&
+ git apply < output &&
+ test_cmp expect file2
+ '
+}
+
+test_diff_unique() {
+ 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
+
+ STRATEGY=$1
+
+ test_expect_success 'completely different files' '
+ test_must_fail git diff --no-index "--$STRATEGY" uniq1 uniq2 > output &&
+ test_cmp expect output
+ '
+}
+
diff --git a/t/lib-gettext.sh b/t/lib-gettext.sh
new file mode 100644
index 0000000..eec757f
--- /dev/null
+++ b/t/lib-gettext.sh
@@ -0,0 +1,58 @@
+# Initialization and Icelandic locale for basic git i18n tests,
+# which source this scriptlet instead of ./test-lib.sh.
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+
+. ./test-lib.sh
+
+GIT_TEXTDOMAINDIR="$GIT_BUILD_DIR/po/build/locale"
+GIT_PO_PATH="$GIT_BUILD_DIR/po"
+export GIT_TEXTDOMAINDIR GIT_PO_PATH
+
+. "$GIT_BUILD_DIR"/git-sh-i18n
+
+if test_have_prereq GETTEXT && ! test_have_prereq GETTEXT_POISON
+then
+ # is_IS.UTF-8 on Solaris and FreeBSD, is_IS.utf8 on Debian
+ is_IS_locale=$(locale -a 2>/dev/null |
+ sed -n '/^is_IS\.[uU][tT][fF]-*8$/{
+ p
+ q
+ }')
+ # is_IS.ISO8859-1 on Solaris and FreeBSD, is_IS.iso88591 on Debian
+ is_IS_iso_locale=$(locale -a 2>/dev/null |
+ sed -n '/^is_IS\.[iI][sS][oO]8859-*1$/{
+ p
+ q
+ }')
+
+ # Export them as an environment variable so the t0202/test.pl Perl
+ # test can use it too
+ export is_IS_locale is_IS_iso_locale
+
+ if test -n "$is_IS_locale" &&
+ test $GIT_INTERNAL_GETTEXT_SH_SCHEME != "fallthrough"
+ then
+ # Some of the tests need the reference Icelandic locale
+ test_set_prereq GETTEXT_LOCALE
+
+ # Exporting for t0202/test.pl
+ GETTEXT_LOCALE=1
+ export GETTEXT_LOCALE
+ say "# lib-gettext: Found '$is_IS_locale' as an is_IS UTF-8 locale"
+ else
+ say "# lib-gettext: No is_IS UTF-8 locale available"
+ fi
+
+ if test -n "$is_IS_iso_locale" &&
+ test $GIT_INTERNAL_GETTEXT_SH_SCHEME != "fallthrough"
+ then
+ # Some of the tests need the reference Icelandic locale
+ test_set_prereq GETTEXT_ISO_LOCALE
+
+ say "# lib-gettext: Found '$is_IS_iso_locale' as an is_IS ISO-8859-1 locale"
+ else
+ say "# lib-gettext: No is_IS ISO-8859-1 locale available"
+ fi
+fi
diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh
new file mode 100644
index 0000000..340534c
--- /dev/null
+++ b/t/lib-git-daemon.sh
@@ -0,0 +1,92 @@
+# Shell library to run git-daemon in tests. Ends the test early if
+# GIT_TEST_GIT_DAEMON is not set.
+#
+# Usage:
+#
+# . ./test-lib.sh
+# . "$TEST_DIRECTORY"/lib-git-daemon.sh
+# start_git_daemon
+#
+# test_expect_success '...' '
+# ...
+# '
+#
+# test_expect_success ...
+#
+# stop_git_daemon
+# test_done
+
+test_tristate GIT_TEST_GIT_DAEMON
+if test "$GIT_TEST_GIT_DAEMON" = false
+then
+ skip_all="git-daemon testing disabled (unset GIT_TEST_GIT_DAEMON to enable)"
+ test_done
+fi
+
+if test_have_prereq !PIPE
+then
+ test_skip_or_die $GIT_TEST_GIT_DAEMON "file system does not support FIFOs"
+fi
+
+LIB_GIT_DAEMON_PORT=${LIB_GIT_DAEMON_PORT-${this_test#t}}
+
+GIT_DAEMON_PID=
+GIT_DAEMON_DOCUMENT_ROOT_PATH="$PWD"/repo
+GIT_DAEMON_URL=git://127.0.0.1:$LIB_GIT_DAEMON_PORT
+
+start_git_daemon() {
+ if test -n "$GIT_DAEMON_PID"
+ then
+ error "start_git_daemon already called"
+ fi
+
+ mkdir -p "$GIT_DAEMON_DOCUMENT_ROOT_PATH"
+
+ trap 'code=$?; stop_git_daemon; (exit $code); die' EXIT
+
+ say >&3 "Starting git daemon ..."
+ mkfifo git_daemon_output
+ git daemon --listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \
+ --reuseaddr --verbose \
+ --base-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
+ "$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
+ >&3 2>git_daemon_output &
+ GIT_DAEMON_PID=$!
+ {
+ read line <&7
+ echo >&4 "$line"
+ cat <&7 >&4 &
+ } 7<git_daemon_output &&
+
+ # Check expected output
+ if test x"$(expr "$line" : "\[[0-9]*\] \(.*\)")" != x"Ready to rumble"
+ then
+ kill "$GIT_DAEMON_PID"
+ wait "$GIT_DAEMON_PID"
+ trap 'die' EXIT
+ test_skip_or_die $GIT_TEST_GIT_DAEMON \
+ "git daemon failed to start"
+ fi
+}
+
+stop_git_daemon() {
+ if test -z "$GIT_DAEMON_PID"
+ then
+ return
+ fi
+
+ trap 'die' EXIT
+
+ # kill git-daemon child of git
+ say >&3 "Stopping git daemon ..."
+ kill "$GIT_DAEMON_PID"
+ wait "$GIT_DAEMON_PID" >&3 2>&4
+ ret=$?
+ # expect exit with status 143 = 128+15 for signal TERM=15
+ if test $ret -ne 143
+ then
+ error "git daemon exited with status: $ret"
+ fi
+ GIT_DAEMON_PID=
+ rm -f git_daemon_output
+}
diff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh
new file mode 100644
index 0000000..54fd5a6
--- /dev/null
+++ b/t/lib-git-p4.sh
@@ -0,0 +1,246 @@
+#
+# Library code for git p4 tests
+#
+
+# p4 tests never use the top-level repo; always build/clone into
+# a subdirectory called "$git"
+TEST_NO_CREATE_REPO=NoThanks
+
+# Some operations require multiple attempts to be successful. Define
+# here the maximal retry timeout in seconds.
+RETRY_TIMEOUT=60
+
+# Sometimes p4d seems to hang. Terminate the p4d process automatically after
+# the defined timeout in seconds.
+P4D_TIMEOUT=300
+
+. ./test-lib.sh
+
+if ! test_have_prereq PYTHON
+then
+ skip_all='skipping git p4 tests; python not available'
+ test_done
+fi
+( p4 -h && p4d -h ) >/dev/null 2>&1 || {
+ skip_all='skipping git p4 tests; no p4 or p4d'
+ test_done
+}
+
+# On cygwin, the NT version of Perforce can be used. When giving
+# it paths, either on the command-line or in client specifications,
+# be sure to use the native windows form.
+#
+# Older versions of perforce were available compiled natively for
+# cygwin. Those do not accept native windows paths, so make sure
+# not to convert for them.
+native_path () {
+ path="$1" &&
+ if test_have_prereq CYGWIN && ! p4 -V | grep -q CYGWIN
+ then
+ path=$(cygpath --windows "$path")
+ else
+ path=$(test-path-utils real_path "$path")
+ fi &&
+ echo "$path"
+}
+
+# On Solaris the 'date +%s' function is not supported and therefore we
+# need this replacement.
+# Attention: This function is not safe again against time offset updates
+# at runtime (e.g. via NTP). The 'clock_gettime(CLOCK_MONOTONIC)'
+# function could fix that but it is not in Python until 3.3.
+time_in_seconds () {
+ (cd / && "$PYTHON_PATH" -c 'import time; print(int(time.time()))')
+}
+
+# Try to pick a unique port: guess a large number, then hope
+# no more than one of each test is running.
+#
+# This does not handle the case where somebody else is running the
+# same tests and has chosen the same ports.
+testid=${this_test#t}
+git_p4_test_start=9800
+P4DPORT=$((10669 + ($testid - $git_p4_test_start)))
+
+P4PORT=localhost:$P4DPORT
+P4CLIENT=client
+P4USER=author
+P4EDITOR=true
+unset P4CHARSET
+export P4PORT P4CLIENT P4USER P4EDITOR P4CHARSET
+
+db="$TRASH_DIRECTORY/db"
+cli="$TRASH_DIRECTORY/cli"
+git="$TRASH_DIRECTORY/git"
+pidfile="$TRASH_DIRECTORY/p4d.pid"
+
+# Sometimes "prove" seems to hang on exit because p4d is still running
+cleanup () {
+ if test -f "$pidfile"
+ then
+ kill -9 $(cat "$pidfile") 2>/dev/null && exit 255
+ fi
+}
+trap cleanup EXIT
+
+# git p4 submit generates a temp file, which will
+# not get cleaned up if the submission fails. Don't
+# clutter up /tmp on the test machine.
+TMPDIR="$TRASH_DIRECTORY"
+export TMPDIR
+
+start_p4d () {
+ mkdir -p "$db" "$cli" "$git" &&
+ rm -f "$pidfile" &&
+ (
+ cd "$db" &&
+ {
+ p4d -q -p $P4DPORT "$@" &
+ echo $! >"$pidfile"
+ }
+ ) &&
+
+ # This gives p4d a long time to start up, as it can be
+ # quite slow depending on the machine. Set this environment
+ # variable to something smaller to fail faster in, say,
+ # an automated test setup. If the p4d process dies, that
+ # will be caught with the "kill -0" check below.
+ i=${P4D_START_PATIENCE:-300}
+ pid=$(cat "$pidfile")
+
+ timeout=$(($(time_in_seconds) + $P4D_TIMEOUT))
+ while true
+ do
+ if test $(time_in_seconds) -gt $timeout
+ then
+ kill -9 $pid
+ exit 1
+ fi
+ sleep 1
+ done &
+ watchdog_pid=$!
+
+ ready=
+ while test $i -gt 0
+ do
+ # succeed when p4 client commands start to work
+ if p4 info >/dev/null 2>&1
+ then
+ ready=true
+ break
+ fi
+ # fail if p4d died
+ kill -0 $pid 2>/dev/null || break
+ echo waiting for p4d to start
+ sleep 1
+ i=$(( $i - 1 ))
+ done
+
+ if test -z "$ready"
+ then
+ # p4d failed to start
+ return 1
+ fi
+
+ # build a p4 user so author@example.com has an entry
+ p4_add_user author
+
+ # build a client
+ client_view "//depot/... //client/..." &&
+
+ return 0
+}
+
+p4_add_user () {
+ name=$1 &&
+ p4 user -f -i <<-EOF
+ User: $name
+ Email: $name@example.com
+ FullName: Dr. $name
+ EOF
+}
+
+p4_add_job () {
+ p4 job -f -i <<-EOF
+ Job: $1
+ Status: open
+ User: dummy
+ Description:
+ EOF
+}
+
+retry_until_success () {
+ timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
+ until "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
+ do
+ sleep 1
+ done
+}
+
+retry_until_fail () {
+ timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
+ until ! "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
+ do
+ sleep 1
+ done
+}
+
+kill_p4d () {
+ pid=$(cat "$pidfile")
+ retry_until_fail kill $pid
+ retry_until_fail kill -9 $pid
+ # complain if it would not die
+ test_must_fail kill $pid >/dev/null 2>&1 &&
+ rm -rf "$db" "$cli" "$pidfile" &&
+ retry_until_fail kill -9 $watchdog_pid
+}
+
+cleanup_git () {
+ retry_until_success rm -r "$git"
+ test_must_fail test -d "$git" &&
+ retry_until_success mkdir "$git"
+}
+
+marshal_dump () {
+ what=$1 &&
+ line=${2:-1} &&
+ cat >"$TRASH_DIRECTORY/marshal-dump.py" <<-EOF &&
+ import marshal
+ import sys
+ instream = getattr(sys.stdin, 'buffer', sys.stdin)
+ for i in range($line):
+ d = marshal.load(instream)
+ print(d[b'$what'].decode('utf-8'))
+ EOF
+ "$PYTHON_PATH" "$TRASH_DIRECTORY/marshal-dump.py"
+}
+
+#
+# Construct a client with this list of View lines
+#
+client_view () {
+ (
+ cat <<-EOF &&
+ Client: $P4CLIENT
+ Description: $P4CLIENT
+ Root: $cli
+ AltRoots: $(native_path "$cli")
+ LineEnd: unix
+ View:
+ EOF
+ printf "\t%s\n" "$@"
+ ) | p4 client -i
+}
+
+is_cli_file_writeable () {
+ # cygwin version of p4 does not set read-only attr,
+ # will be marked 444 but -w is true
+ file="$1" &&
+ if test_have_prereq CYGWIN && p4 -V | grep -q CYGWIN
+ then
+ stat=$(stat --format=%a "$file") &&
+ test $stat = 644
+ else
+ test -w "$file"
+ fi
+}
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index c3f6676..fb88232 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -1,8 +1,5 @@
. ./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'
@@ -16,7 +13,6 @@ 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
@@ -30,7 +26,7 @@ export svnrepo
svnconf=$PWD/svnconf
export svnconf
-$PERL -w -e "
+perl -w -e "
use SVN::Core;
use SVN::Repos;
\$SVN::Core::VERSION gt '1.1.0' or exit(42);
@@ -69,41 +65,46 @@ svn_cmd () {
svn "$orig_svncmd" --config-dir "$svnconf" "$@"
}
-for d in \
- "$SVN_HTTPD_PATH" \
- /usr/sbin/apache2 \
- /usr/sbin/httpd \
-; do
- if test -f "$d"
+prepare_httpd () {
+ 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
+ if test -z "$SVN_HTTPD_PATH"
then
- SVN_HTTPD_PATH="$d"
- break
+ echo >&2 '*** error: Apache not found'
+ return 1
fi
-done
-for d in \
- "$SVN_HTTPD_MODULE_PATH" \
- /usr/lib/apache2/modules \
- /usr/libexec/apache2 \
-; do
- if test -d "$d"
+ 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
+ if test -z "$SVN_HTTPD_MODULE_PATH"
then
- SVN_HTTPD_MODULE_PATH="$d"
- break
+ echo >&2 '*** error: Apache module dir not found'
+ return 1
fi
-done
-
-start_httpd () {
- repo_base_path="$1"
- if test -z "$SVN_HTTPD_PORT"
+ if test ! -f "$SVN_HTTPD_MODULE_PATH/mod_dav_svn.so"
then
- echo >&2 'SVN_HTTPD_PORT is not defined!'
- return
- fi
- if test -z "$repo_base_path"
- then
- repo_base_path=svn
+ echo >&2 '*** error: Apache module "mod_dav_svn" not found'
+ return 1
fi
+ repo_base_path="${1-svn}"
mkdir "$GIT_DIR"/logs
cat > "$GIT_DIR/httpd.conf" <<EOF
@@ -120,19 +121,31 @@ LoadModule dav_svn_module $SVN_HTTPD_MODULE_PATH/mod_dav_svn.so
SVNPath "$rawsvnrepo"
</Location>
EOF
+}
+
+start_httpd () {
+ if test -z "$SVN_HTTPD_PORT"
+ then
+ echo >&2 'SVN_HTTPD_PORT is not defined!'
+ return
+ fi
+
+ prepare_httpd "$1" || return 1
+
"$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
+ test ! -f "$GIT_DIR/httpd.conf" && return
"$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k stop
}
convert_to_rev_db () {
- $PERL -w -- - "$@" <<\EOF
+ perl -w -- - "$@" <<\EOF
use strict;
-@ARGV == 2 or die "Usage: convert_to_rev_db <input> <output>";
+@ARGV == 2 or die "usage: convert_to_rev_db <input> <output>";
open my $wr, '+>', $ARGV[1] or die "$!: couldn't open: $ARGV[1]";
open my $rd, '<', $ARGV[0] or die "$!: couldn't open: $ARGV[0]";
my $size = (stat($rd))[7];
@@ -170,3 +183,15 @@ start_svnserve () {
--listen-host 127.0.0.1 &
}
+prepare_a_utf8_locale () {
+ 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
+}
diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
new file mode 100755
index 0000000..ec2aa8f
--- /dev/null
+++ b/t/lib-gpg.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+gpg_version=$(gpg --version 2>&1)
+if test $? != 127
+then
+ # 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 "Your version of gpg (1.0.6) is too buggy for testing"
+ ;;
+ *)
+ # Available key info:
+ # * Type DSA and Elgamal, size 2048 bits, no expiration date,
+ # name and email: C O Mitter <committer@example.com>
+ # * Type RSA, size 2048 bits, no expiration date,
+ # name and email: Eris Discordia <discord@example.net>
+ # No password given, to enable non-interactive operation.
+ # To generate new key:
+ # gpg --homedir /tmp/gpghome --gen-key
+ # To write armored exported key to keyring:
+ # gpg --homedir /tmp/gpghome --export-secret-keys \
+ # --armor 0xDEADBEEF >> lib-gpg/keyring.gpg
+ # gpg --homedir /tmp/gpghome --export \
+ # --armor 0xDEADBEEF >> lib-gpg/keyring.gpg
+ # To export ownertrust:
+ # gpg --homedir /tmp/gpghome --export-ownertrust \
+ # > lib-gpg/ownertrust
+ mkdir ./gpghome &&
+ chmod 0700 ./gpghome &&
+ GNUPGHOME="$(pwd)/gpghome" &&
+ export GNUPGHOME &&
+ gpg --homedir "${GNUPGHOME}" 2>/dev/null --import \
+ "$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
+ gpg --homedir "${GNUPGHOME}" 2>/dev/null --import-ownertrust \
+ "$TEST_DIRECTORY"/lib-gpg/ownertrust &&
+ gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null 2>&1 \
+ --sign -u committer@example.com &&
+ test_set_prereq GPG
+ ;;
+ esac
+fi
+
+if test_have_prereq GPG &&
+ echo | gpg --homedir "${GNUPGHOME}" -b --rfc1991 >/dev/null 2>&1
+then
+ test_set_prereq RFC1991
+fi
+
+sanitize_pgp() {
+ perl -ne '
+ /^-----END PGP/ and $in_pgp = 0;
+ print unless $in_pgp;
+ /^-----BEGIN PGP/ and $in_pgp = 1;
+ '
+}
diff --git a/t/lib-gpg/keyring.gpg b/t/lib-gpg/keyring.gpg
new file mode 100644
index 0000000..d4754a1
--- /dev/null
+++ b/t/lib-gpg/keyring.gpg
@@ -0,0 +1,142 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1
+
+lQG7BEZnyykRBACzCPjIpTYNL7Y2tQqlEGTTDlvZcWNLjF5f7ZzuyOqNOidLUgFD
+36qch1LZLSZkShdR3Gae+bsolyjxrlFuFP0eXRPMtqK20aLw7WZvPFpEV1ThMne+
+PRJjYrvghWw3L0VVIAIZ8GXwrVBuU99uEjHEI0ojYloOvFc2jVPgSaoBvwCg48Tj
+fol2foSoJa7XUu9yAL8szg8D/RUsTzNF+I9hSRHl7MYKFMYoKEY9BDgrgAujp7YY
+8qdGsiUb0Ggyzp2kRjZFt4lpcvKhGfHn5GEjmtk+fRbD5qPfMqKFW+T0NPfYlYmL
+JJ4fs4qZ8Lx7x6iG6X51u+YNwsQuIGjMCC3CeNi3F7or651kkNYASbaQ1NROkCIN
+NudyA/0aasvoZUoNJAc2cP5Ifs6WhXMWLfMR2p2XbfKwKNYneec60usnSComcKqh
+sJVk0Gytvr3FOYVhRkXnKAbx+0W2urFP8OFVBTEKO6Ts2VygWGgneQYoHnqzwlUE
+yjOjlr+lyf7u2s/KAxpKA6jnttEdRZAmzWkhuox1wwAUkr27/QAAn3TEzKR1pxxR
++R3dHuFpnnfatMIDC5O0IkMgTyBNaXR0ZXIgPGNvbW1pdHRlckBleGFtcGxlLmNv
+bT6IXgQTEQIAHgUCRmfLKQIbAwYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRATtvUe
+zd5DDXQdAKC92f+wOrTkbmPEf+u+qA/Gv6BxQwCfQ128JXCi3MpMB8tI2Kmo15tY
+gnmdAj0ERmfLThAIAM65eT9T6+gg0fJn+Qxhs3FFDPjxK6AOBS3SieWWmXO6stZZ
+plvb7r2+sXYp8HMHntnOX3TRPolIx1dsdkv3W3w8yUzf9Lmo2XMPsZ3/isWdEbOI
+A0rO3B1xwbQO7vEoWHeB7uyYIF6YsIH0pMqxkImciwB1tnJPB9OxqPHlD/HyyHr2
+voj6nmEGaPQWj8/dkfyenXm6XmNZUZL/slk6tRhNwv4cW3QQLh39nbiz9rqvZMKF
+XX8wkY4FdQkJjCGwqzG+7yJcyHvem29/iq//jRLZgdiN8BwV3MCTJyDp8/Wb/d9y
+jZcUm1RdtwRiwfhfQ+zmpyspm7OxINfH65rf7f8ABA0IALRiMRs/eOD59jrYXmPS
+ZQUbiALlbJJtuP2c9N3WZ5OgrhDiAW+SDIN+hgDynJ9b7C2dE3xNaud4zaXAAF44
+J4J0bAo2ZtZoJajw+GXwaZfh4Z7nPNHwEcbFD4/uXPCj9jPkcLOJqGmUY1aXdygo
+t3Hn5U/zo8JxPQ83YbJQhkzAOZ/HGowLNqKgGkLLHn1X9qay0CxlfTQeEN5RZyl3
+b4qRzGgGALFvoheyZIUw1TbjRpbn3kqlJooEQY02VwXFXfLI/LwzglilH6sSckvs
+0WHKLZ+0L6b3CgJHN2RsZ7QxwCBi1aemsvr65FeEXp/AYxaG5duUbsugG8PgoJ06
+bsEAAVQNQO3cXWpuiJ/nNLLnWuPunBKJUlurkBdf2GD+m+muF0VpwDchhqqbTO4e
+FqOISQQYEQIACQUCRmfLTgIbDAAKCRATtvUezd5DDcHsAKDQcoAtDWJFupVRqleB
+Cezx4Q2khACcCs+/LtE8Lb9hC+2cvr3uH5p82AI=
+=aEiU
+-----END PGP PRIVATE KEY BLOCK-----
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1
+
+lQOYBFFMlkcBCADJi/xnAF8yI34PHilSCbM7VtOFO17oFMkpu4cgN2QpPuM5MVjy
+cvrzKSguZFvPCDLzeAFJW1uPxL4SHaHSkisCrFhijH7OJWcOPNPSFCwu+inAoAsv
+Hm4ns6pfDZyRjVTHSY4rdMISqKFRozaXu8vHeBRzIhFnubBCepKZW07oKPnrnELV
+TVUSUVI+6el8JFmJIWxxLNLhfRRSPF0v4MDXPF//iCWiZDI+J1pLvQ5V/f7YtfsD
+GV0oPY66J72BFJG555eKBttnNY901LmI3ocn5P5iVnXDaqMElw7FKpnANXucgY3H
+4kLyNkI3s3J0CGbXI7b3MBWtjctuhWv1q2G5ABEBAAEAB/wLiuza/qEfv1Cfj7FQ
+ytAXpz1YoAcrcM/53TeRQhrbvIee5ZNGhLdCkyot81QeuJrSaXO0E9CxRynrjQQ7
+ibYqN7Hy0uu1kAbQQJjmVdQXTKnKJ7Wm7oM4hYhNsVCKNXc+1+5AfDYGg4nZob36
+qqgHtc+Ardl5VfUg7uF+eZrnSMynjZANgikKbPtE09DKVtVOtUE4xTD9ijkpgn65
+glsZDqb7J4QVgTeEiCDKJsQvin3SwrPBqBxBRULF2TIaMbOwe6dHiiaI85rsvAWS
+VGzonUB3IU1470P2SDIVczbXYUK/nDSGx6ZZ0wLu9ZcCyUPvxVEykuh2P4UWHla+
+nHLRBADMLavcfjsCI5CRUsdurYpgE8Y3bEbcDpvzAu5jT5D25p3YPDODOXD3AKTt
+PzVMARVtv8twkbgAyWaoDevJz8OtmoSwsWjdFo4YvsYw9jV7Yf3GwzD3Ya1ZnW32
+JWQr6cX8qcK0AukAD7UZkVyhU2KBvB02t8lKHLbScHXTYVqrywQA/LNUXwmHji+6
+osnSQAC8X9ggMOEs9dGo7Qlk4JgfGAH17CFI3S3ubsaVEdxz3YwzOkD8SNmEbLyW
+a7CZ/RnpdAZU0nB7kSfbfZl7ajhPbgKBMsaV2yvaDdJeor4m5eKdXffRk0SksxjL
+Z/4P1tTIuL8WzetGB/aDcWDFgseSAAsEALzmf579ptlSmDyGRAKQqub+mj4V3EUZ
+1GVGcfBY86w3BZVDsaRiCtcNjk/lcP4AZ1Vbb42RM6jk8nLsENRc7rf2xa7ZPf0T
+6n5F6W+vk7EG76RoFhKVtGZngGKiDGVavxk3FT/yf8lKrT3wYiT03SZDuZ0pWvku
+FiJGEyesAC8WRz60JEVyaXMgRGlzY29yZGlhIDxkaXNjb3JkQGV4YW1wbGUubmV0
+PokBOAQTAQIAIgUCUUyWRwIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQ
+YQkuhbcicYlYowf7B+f+FDcLVfw8XzGlKku1F6PI1yGCt7AMO2/JkmO4LlgHuIgF
+pqe5b/XjKl0IsRcbVLitqiIokc8u+7H8yYU67DDliq7t1gqBy+qThSHcgn6WMKTa
+qCqOE2jzHyqulIAzQsJQ+c5SRofEZAKT4Qa2Dy+nsqWDpIE78aJd0Vnkk9U6H2Vu
+ABvUeN/IMgvxPr525o+rBD7LU4J3CtOzfV+sO6+33da+Bm9UhkR4tC4H/n1dDN1J
+YuxBQbgxTq/h8mKe4/7/Yvy+5WsYd96ZRLE2ZFWeWXtKkwmYbQ42G3SZUXaZ8R8O
+tbTyUrjbFKipO4wvXwhyju1l9cxAsrca6xbSCJ0DmARRTJZHAQgAqTtPFcTXqM+U
+o7bOoo+dcHi8XDf/8XSEmZfMKc/U5pSTBk7h1gSKuGzjF2n3wQm6A8+101vTLaQ6
+PoFDFW8uQB00mjymGrRDYFgz8bjhnaekZnA4XThr1ROjffgMhs3uTpCebdV+lL8K
+0oJTHc39TPLTg23DFcRSDN+3ARJJS7+CRBIbt9L5gObpgA4HUap/o6N7O04rQOPU
+83MAqnwo2JTO/Ded0zoad0Vo31Nmk4F+KvEE52ftGHbd7yqIUGKBt2SeTAh850ac
+LeNZP+V1Y7atBCr7/zm+JpHWq9OH7/NomlEIkxL8WDt8GfAKoqZgqefL+ACEnLbA
+t1du3f0FswARAQABAAf8DclaIQDfPM5kYo3y+YVPoykC11RskmQWpVibdlCLHJm/
+/ISSm1fVYT7lpTOpzl0XfVX/jw9s/cviPtNS/r0G/Iwki+gi9Av5bTDiUm/oWWqd
+1waPYPDGwB4QdKOviY/fOSFI9tOsszt5Czs4wDXWy90AZDWd7fkHYisbgofV1sjK
+Q8bYQPabcepcZ2JyET+EpZBEmUHHqQ76bTiqjN+Vz6k1OFlsEBzGkE+WIakAhkQ2
+57oUrRgFe+h6Ch7meB/v6vVfIRSsLpZe183uc4SigqtfsgjbG9PqOcAJOqovDncB
+Scg3qvpWFOAkTA3Re+yBPUd2HHl9WF/TPa2kBDCT2QQAxcJZeUCuUgDgCizqEgfs
+Kzm6dy4G/OJdW0q9m9psHqD1XWLd7ZLE4+eTS1cxktJiGcGNdGoZD0EtgxkV09uM
+12QYCOBErFJzv4/4oledHeEhTaRR/mFFGRp+kWTz2Ai/zNqUd3D++DYUe8g4mVQJ
+6JP014XhvoRnaCfT8cH9Zd0EANsSL70WGdifcVoWKA9jFJhahc0sSG6IZvMOc7bs
+cSbhBqLEnheObkarBP+A+zgllqIf+sbCassMXjcV52mnl9th3J5RWr7scrQLJ9ZX
+Ivz3uoP85vwlUI98dI9roYK0OpKmG4hNFppAcgiCVNVjnQlhuQ/HoexRHxRmnmcb
+38jPA/sEHPCFbLCGOSB+HQNKx/5Wf6VpFX/4oBNbIUiYoxcRl0jpYT7Lc0zbc8So
+HthjPfWhXhKzYvEDC5YgASEy1cNbGMUJcGyuAInwIQjq44FSwRMkI3ISSHnbv1iH
+0wBVJUzpluMebEAesdZUz1DcZWVf6eVJD0dhZxD6DoG7Xj1m9ThUiQEfBBgBAgAJ
+BQJRTJZHAhsMAAoJEGEJLoW3InGJ7HEIAMXkMf4cOWmnAuvvcSm3KpLghuWik9dy
+fn1sY/IG5atoKK+ypmV/TlBlMZqFQzuPIJQT8VLbmxtLlDhJG04LbI6c8axIZxOO
+ZKLy5nTTSy16ztqEeS7eifHLPZg1UFFyEEIQ1XW0CNDAeuWKh90ERjyl4Cg7PnWS
+Z9Ei+zj6JD5Pcdi3BJhQo9WOLOVEJ0NHmewTYqk9QVXH/0v1Hdl4LMJtgcbdbDWk
+4UTkXbg9pn3umCgkNJ3Vs8fWnIWO9Izdr2/wrFY2JvUT7Yvl+wsNIWatvOEzGy7n
+BOW78WUxzhu0YJTLKy+iKCjg5HS5dx6OC+e4aEEgfhNPCMkbvDsJjtQ=
+=hieJ
+-----END PGP PRIVATE KEY BLOCK-----
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1
+
+mQGiBEZnyykRBACzCPjIpTYNL7Y2tQqlEGTTDlvZcWNLjF5f7ZzuyOqNOidLUgFD
+36qch1LZLSZkShdR3Gae+bsolyjxrlFuFP0eXRPMtqK20aLw7WZvPFpEV1ThMne+
+PRJjYrvghWw3L0VVIAIZ8GXwrVBuU99uEjHEI0ojYloOvFc2jVPgSaoBvwCg48Tj
+fol2foSoJa7XUu9yAL8szg8D/RUsTzNF+I9hSRHl7MYKFMYoKEY9BDgrgAujp7YY
+8qdGsiUb0Ggyzp2kRjZFt4lpcvKhGfHn5GEjmtk+fRbD5qPfMqKFW+T0NPfYlYmL
+JJ4fs4qZ8Lx7x6iG6X51u+YNwsQuIGjMCC3CeNi3F7or651kkNYASbaQ1NROkCIN
+NudyA/0aasvoZUoNJAc2cP5Ifs6WhXMWLfMR2p2XbfKwKNYneec60usnSComcKqh
+sJVk0Gytvr3FOYVhRkXnKAbx+0W2urFP8OFVBTEKO6Ts2VygWGgneQYoHnqzwlUE
+yjOjlr+lyf7u2s/KAxpKA6jnttEdRZAmzWkhuox1wwAUkr27/bQiQyBPIE1pdHRl
+ciA8Y29tbWl0dGVyQGV4YW1wbGUuY29tPoheBBMRAgAeBQJGZ8spAhsDBgsJCAcD
+AgMVAgMDFgIBAh4BAheAAAoJEBO29R7N3kMNdB0AoL3Z/7A6tORuY8R/676oD8a/
+oHFDAJ9DXbwlcKLcykwHy0jYqajXm1iCebkCDQRGZ8tOEAgAzrl5P1Pr6CDR8mf5
+DGGzcUUM+PEroA4FLdKJ5ZaZc7qy1lmmW9vuvb6xdinwcwee2c5fdNE+iUjHV2x2
+S/dbfDzJTN/0uajZcw+xnf+KxZ0Rs4gDSs7cHXHBtA7u8ShYd4Hu7JggXpiwgfSk
+yrGQiZyLAHW2ck8H07Go8eUP8fLIeva+iPqeYQZo9BaPz92R/J6debpeY1lRkv+y
+WTq1GE3C/hxbdBAuHf2duLP2uq9kwoVdfzCRjgV1CQmMIbCrMb7vIlzIe96bb3+K
+r/+NEtmB2I3wHBXcwJMnIOnz9Zv933KNlxSbVF23BGLB+F9D7OanKymbs7Eg18fr
+mt/t/wAEDQgAtGIxGz944Pn2OtheY9JlBRuIAuVskm24/Zz03dZnk6CuEOIBb5IM
+g36GAPKcn1vsLZ0TfE1q53jNpcAAXjgngnRsCjZm1mglqPD4ZfBpl+Hhnuc80fAR
+xsUPj+5c8KP2M+Rws4moaZRjVpd3KCi3ceflT/OjwnE9DzdhslCGTMA5n8cajAs2
+oqAaQssefVf2prLQLGV9NB4Q3lFnKXdvipHMaAYAsW+iF7JkhTDVNuNGlufeSqUm
+igRBjTZXBcVd8sj8vDOCWKUfqxJyS+zRYcotn7QvpvcKAkc3ZGxntDHAIGLVp6ay
++vrkV4Ren8BjFobl25Ruy6Abw+CgnTpuwYhJBBgRAgAJBQJGZ8tOAhsMAAoJEBO2
+9R7N3kMNwewAoNBygC0NYkW6lVGqV4EJ7PHhDaSEAJwKz78u0Twtv2EL7Zy+ve4f
+mnzYApkBDQRRTJZHAQgAyYv8ZwBfMiN+Dx4pUgmzO1bThTte6BTJKbuHIDdkKT7j
+OTFY8nL68ykoLmRbzwgy83gBSVtbj8S+Eh2h0pIrAqxYYox+ziVnDjzT0hQsLvop
+wKALLx5uJ7OqXw2ckY1Ux0mOK3TCEqihUaM2l7vLx3gUcyIRZ7mwQnqSmVtO6Cj5
+65xC1U1VElFSPunpfCRZiSFscSzS4X0UUjxdL+DA1zxf/4glomQyPidaS70OVf3+
+2LX7AxldKD2Ouie9gRSRueeXigbbZzWPdNS5iN6HJ+T+YlZ1w2qjBJcOxSqZwDV7
+nIGNx+JC8jZCN7NydAhm1yO29zAVrY3LboVr9athuQARAQABtCRFcmlzIERpc2Nv
+cmRpYSA8ZGlzY29yZEBleGFtcGxlLm5ldD6JATgEEwECACIFAlFMlkcCGwMGCwkI
+BwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEGEJLoW3InGJWKMH+wfn/hQ3C1X8PF8x
+pSpLtRejyNchgrewDDtvyZJjuC5YB7iIBaanuW/14ypdCLEXG1S4raoiKJHPLvux
+/MmFOuww5Yqu7dYKgcvqk4Uh3IJ+ljCk2qgqjhNo8x8qrpSAM0LCUPnOUkaHxGQC
+k+EGtg8vp7Klg6SBO/GiXdFZ5JPVOh9lbgAb1HjfyDIL8T6+duaPqwQ+y1OCdwrT
+s31frDuvt93WvgZvVIZEeLQuB/59XQzdSWLsQUG4MU6v4fJinuP+/2L8vuVrGHfe
+mUSxNmRVnll7SpMJmG0ONht0mVF2mfEfDrW08lK42xSoqTuML18Ico7tZfXMQLK3
+GusW0gi5AQ0EUUyWRwEIAKk7TxXE16jPlKO2zqKPnXB4vFw3//F0hJmXzCnP1OaU
+kwZO4dYEirhs4xdp98EJugPPtdNb0y2kOj6BQxVvLkAdNJo8phq0Q2BYM/G44Z2n
+pGZwOF04a9UTo334DIbN7k6Qnm3VfpS/CtKCUx3N/Uzy04NtwxXEUgzftwESSUu/
+gkQSG7fS+YDm6YAOB1Gqf6OjeztOK0Dj1PNzAKp8KNiUzvw3ndM6GndFaN9TZpOB
+firxBOdn7Rh23e8qiFBigbdknkwIfOdGnC3jWT/ldWO2rQQq+/85viaR1qvTh+/z
+aJpRCJMS/Fg7fBnwCqKmYKnny/gAhJy2wLdXbt39BbMAEQEAAYkBHwQYAQIACQUC
+UUyWRwIbDAAKCRBhCS6FtyJxiexxCADF5DH+HDlppwLr73EptyqS4IblopPXcn59
+bGPyBuWraCivsqZlf05QZTGahUM7jyCUE/FS25sbS5Q4SRtOC2yOnPGsSGcTjmSi
+8uZ000stes7ahHku3onxyz2YNVBRchBCENV1tAjQwHrliofdBEY8peAoOz51kmfR
+Ivs4+iQ+T3HYtwSYUKPVjizlRCdDR5nsE2KpPUFVx/9L9R3ZeCzCbYHG3Ww1pOFE
+5F24PaZ97pgoJDSd1bPH1pyFjvSM3a9v8KxWNib1E+2L5fsLDSFmrbzhMxsu5wTl
+u/FlMc4btGCUyysvoigo4OR0uXcejgvnuGhBIH4TTwjJG7w7CY7U
+=iYv/
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/t/lib-gpg/ownertrust b/t/lib-gpg/ownertrust
new file mode 100644
index 0000000..b3e3c4f
--- /dev/null
+++ b/t/lib-gpg/ownertrust
@@ -0,0 +1,4 @@
+# List of assigned trustvalues, created Thu 11 Dec 2014 01:26:28 PM CET
+# (Use "gpg --import-ownertrust" to restore them)
+73D758744BE721698EC54E8713B6F51ECDDE430D:6:
+D4BE22311AD3131E5EDA29A461092E85B7227189:3:
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index e733f65..f9f3e5f 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -1,14 +1,59 @@
-#!/bin/sh
+# Shell library to run an HTTP server for use in tests.
+# Ends the test early if httpd tests should not be run,
+# for example because the user has not enabled them.
+#
+# Usage:
+#
+# . ./test-lib.sh
+# . "$TEST_DIRECTORY"/lib-httpd.sh
+# start_httpd
+#
+# test_expect_success '...' '
+# ...
+# '
+#
+# test_expect_success ...
+#
+# stop_httpd
+# test_done
+#
+# Can be configured using the following variables.
+#
+# GIT_TEST_HTTPD enable HTTPD tests
+# LIB_HTTPD_PATH web server path
+# LIB_HTTPD_MODULE_PATH web server modules path
+# LIB_HTTPD_PORT listening port
+# LIB_HTTPD_DAV enable DAV
+# LIB_HTTPD_SVN enable SVN
+# LIB_HTTPD_SSL enable SSL
#
# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
#
-if test -z "$GIT_TEST_HTTPD"
+if test -n "$NO_CURL"
+then
+ skip_all='skipping test, git built without http support'
+ test_done
+fi
+
+if test -n "$NO_EXPAT" && test -n "$LIB_HTTPD_DAV"
+then
+ skip_all='skipping test, git built without expat support'
+ test_done
+fi
+
+test_tristate GIT_TEST_HTTPD
+if test "$GIT_TEST_HTTPD" = false
then
- skip_all="Network testing disabled (define GIT_TEST_HTTPD to enable)"
+ skip_all="Network testing disabled (unset GIT_TEST_HTTPD to enable)"
test_done
fi
+if ! test_have_prereq NOT_ROOT; then
+ test_skip_or_die $GIT_TEST_HTTPD \
+ "Cannot run httpd tests as root"
+fi
+
HTTPD_PARA=""
for DEFAULT_HTTPD_PATH in '/usr/sbin/httpd' '/usr/sbin/apache2'
@@ -37,20 +82,24 @@ case $(uname) in
esac
LIB_HTTPD_PATH=${LIB_HTTPD_PATH-"$DEFAULT_HTTPD_PATH"}
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'8111'}
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-${this_test#t}}
TEST_PATH="$TEST_DIRECTORY"/lib-httpd
HTTPD_ROOT_PATH="$PWD"/httpd
HTTPD_DOCUMENT_ROOT_PATH=$HTTPD_ROOT_PATH/www
+# hack to suppress apache PassEnv warnings
+GIT_VALGRIND=$GIT_VALGRIND; export GIT_VALGRIND
+GIT_VALGRIND_OPTIONS=$GIT_VALGRIND_OPTIONS; export GIT_VALGRIND_OPTIONS
+GIT_TRACE=$GIT_TRACE; export GIT_TRACE
+
if ! test -x "$LIB_HTTPD_PATH"
then
- skip_all="skipping test, no web server found at '$LIB_HTTPD_PATH'"
- test_done
+ test_skip_or_die $GIT_TEST_HTTPD "no web server found at '$LIB_HTTPD_PATH'"
fi
-HTTPD_VERSION=`$LIB_HTTPD_PATH -v | \
- sed -n 's/^Server version: Apache\/\([0-9]*\)\..*$/\1/p; q'`
+HTTPD_VERSION=$($LIB_HTTPD_PATH -v | \
+ sed -n 's/^Server version: Apache\/\([0-9]*\)\..*$/\1/p; q')
if test -n "$HTTPD_VERSION"
then
@@ -58,29 +107,37 @@ then
then
if ! test $HTTPD_VERSION -ge 2
then
- skip_all="skipping test, at least Apache version 2 is required"
- test_done
+ test_skip_or_die $GIT_TEST_HTTPD \
+ "at least Apache version 2 is required"
fi
if ! test -d "$DEFAULT_HTTPD_MODULE_PATH"
then
- skip_all="Apache module directory not found. Skipping tests."
- test_done
+ test_skip_or_die $GIT_TEST_HTTPD \
+ "Apache module directory not found"
fi
LIB_HTTPD_MODULE_PATH="$DEFAULT_HTTPD_MODULE_PATH"
fi
else
- error "Could not identify web server at '$LIB_HTTPD_PATH'"
+ test_skip_or_die $GIT_TEST_HTTPD \
+ "Could not identify web server at '$LIB_HTTPD_PATH'"
fi
+install_script () {
+ write_script "$HTTPD_ROOT_PATH/$1" <"$TEST_PATH/$1"
+}
+
prepare_httpd() {
mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH"
+ cp "$TEST_PATH"/passwd "$HTTPD_ROOT_PATH"
+ install_script broken-smart-http.sh
+ install_script error.sh
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
+ HTTPD_PROTO=https
RANDFILE_PATH="$HTTPD_ROOT_PATH"/.rnd openssl req \
-config "$TEST_PATH/ssl.cnf" \
@@ -91,10 +148,14 @@ prepare_httpd() {
export GIT_SSL_NO_VERIFY
HTTPD_PARA="$HTTPD_PARA -DSSL"
else
- HTTPD_URL=http://127.0.0.1:$LIB_HTTPD_PORT
+ HTTPD_PROTO=http
fi
+ HTTPD_DEST=127.0.0.1:$LIB_HTTPD_PORT
+ HTTPD_URL=$HTTPD_PROTO://$HTTPD_DEST
+ HTTPD_URL_USER=$HTTPD_PROTO://user%40host@$HTTPD_DEST
+ HTTPD_URL_USER_PASS=$HTTPD_PROTO://user%40host:pass%40host@$HTTPD_DEST
- if test -n "$LIB_HTTPD_DAV" -o -n "$LIB_HTTPD_SVN"
+ if test -n "$LIB_HTTPD_DAV" || test -n "$LIB_HTTPD_SVN"
then
HTTPD_PARA="$HTTPD_PARA -DDAV"
@@ -118,9 +179,8 @@ start_httpd() {
>&3 2>&4
if test $? -ne 0
then
- skip_all="skipping test, web server setup failed"
trap 'die' EXIT
- test_done
+ test_skip_or_die $GIT_TEST_HTTPD "web server setup failed"
fi
}
@@ -131,10 +191,11 @@ stop_httpd() {
-f "$TEST_PATH/apache.conf" $HTTPD_PARA -k stop
}
-test_http_push_nonff() {
+test_http_push_nonff () {
REMOTE_REPO=$1
LOCAL_REPO=$2
BRANCH=$3
+ EXPECT_CAS_RESULT=${4-failure}
test_expect_success 'non-fast-forward push fails' '
cd "$REMOTE_REPO" &&
@@ -155,7 +216,71 @@ test_http_push_nonff() {
'
test_expect_success 'non-fast-forward push shows help message' '
- grep "To prevent you from losing history, non-fast-forward updates were rejected" \
- output
+ test_i18ngrep "Updates were rejected because" output
+ '
+
+ test_expect_${EXPECT_CAS_RESULT} 'force with lease aka cas' '
+ HEAD=$( cd "$REMOTE_REPO" && git rev-parse --verify HEAD ) &&
+ test_when_finished '\''
+ (cd "$REMOTE_REPO" && git update-ref HEAD "$HEAD")
+ '\'' &&
+ (
+ cd "$LOCAL_REPO" &&
+ git push -v --force-with-lease=$BRANCH:$HEAD origin
+ ) &&
+ git rev-parse --verify "$BRANCH" >expect &&
+ (
+ cd "$REMOTE_REPO" && git rev-parse --verify HEAD
+ ) >actual &&
+ test_cmp expect actual
'
}
+
+setup_askpass_helper() {
+ test_expect_success 'setup askpass helper' '
+ write_script "$TRASH_DIRECTORY/askpass" <<-\EOF &&
+ echo >>"$TRASH_DIRECTORY/askpass-query" "askpass: $*" &&
+ case "$*" in
+ *Username*)
+ what=user
+ ;;
+ *Password*)
+ what=pass
+ ;;
+ esac &&
+ cat "$TRASH_DIRECTORY/askpass-$what"
+ EOF
+ GIT_ASKPASS="$TRASH_DIRECTORY/askpass" &&
+ export GIT_ASKPASS &&
+ export TRASH_DIRECTORY
+ '
+}
+
+set_askpass() {
+ >"$TRASH_DIRECTORY/askpass-query" &&
+ echo "$1" >"$TRASH_DIRECTORY/askpass-user" &&
+ echo "$2" >"$TRASH_DIRECTORY/askpass-pass"
+}
+
+expect_askpass() {
+ dest=$HTTPD_DEST${3+/$3}
+
+ {
+ case "$1" in
+ none)
+ ;;
+ pass)
+ echo "askpass: Password for 'http://$2@$dest': "
+ ;;
+ both)
+ echo "askpass: Username for 'http://$dest': "
+ echo "askpass: Password for 'http://$2@$dest': "
+ ;;
+ *)
+ false
+ ;;
+ esac
+ } >"$TRASH_DIRECTORY/askpass-expect" &&
+ test_cmp "$TRASH_DIRECTORY/askpass-expect" \
+ "$TRASH_DIRECTORY/askpass-query"
+}
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 4961505..018a83a 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -1,5 +1,4 @@
ServerName dummy
-LockFile accept.lock
PidFile httpd.pid
DocumentRoot www
LogFormat "%h %l %u %t \"%r\" %>s %b" common
@@ -17,25 +16,134 @@ ErrorLog error.log
<IfModule !mod_env.c>
LoadModule env_module modules/mod_env.so
</IfModule>
+<IfModule !mod_rewrite.c>
+ LoadModule rewrite_module modules/mod_rewrite.so
+</IFModule>
+<IfModule !mod_version.c>
+ LoadModule version_module modules/mod_version.so
+</IfModule>
+<IfModule !mod_headers.c>
+ LoadModule headers_module modules/mod_headers.so
+</IfModule>
+
+<IfVersion < 2.4>
+LockFile accept.lock
+</IfVersion>
+
+<IfVersion < 2.1>
+<IfModule !mod_auth.c>
+ LoadModule auth_module modules/mod_auth.so
+</IfModule>
+</IfVersion>
+
+<IfVersion >= 2.1>
+<IfModule !mod_auth_basic.c>
+ LoadModule auth_basic_module modules/mod_auth_basic.so
+</IfModule>
+<IfModule !mod_authn_file.c>
+ LoadModule authn_file_module modules/mod_authn_file.so
+</IfModule>
+<IfModule !mod_authz_user.c>
+ LoadModule authz_user_module modules/mod_authz_user.so
+</IfModule>
+<IfModule !mod_authz_host.c>
+ LoadModule authz_host_module modules/mod_authz_host.so
+</IfModule>
+</IfVersion>
+
+<IfVersion >= 2.4>
+<IfModule !mod_authn_core.c>
+ LoadModule authn_core_module modules/mod_authn_core.so
+</IfModule>
+<IfModule !mod_authz_core.c>
+ LoadModule authz_core_module modules/mod_authz_core.so
+</IfModule>
+<IfModule !mod_access_compat.c>
+ LoadModule access_compat_module modules/mod_access_compat.so
+</IfModule>
+<IfModule !mod_mpm_prefork.c>
+ LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
+</IfModule>
+<IfModule !mod_unixd.c>
+ LoadModule unixd_module modules/mod_unixd.so
+</IfModule>
+</IfVersion>
+
+PassEnv GIT_VALGRIND
+PassEnv GIT_VALGRIND_OPTIONS
+PassEnv GNUPGHOME
+PassEnv ASAN_OPTIONS
+PassEnv GIT_TRACE
+PassEnv GIT_CONFIG_NOSYSTEM
Alias /dumb/ www/
+Alias /auth/dumb/ www/auth/dumb/
-<Location /smart/>
+<LocationMatch /smart/>
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
SetEnv GIT_HTTP_EXPORT_ALL
-</Location>
-<Location /smart_noexport/>
+</LocationMatch>
+<LocationMatch /smart_noexport/>
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
-</Location>
-ScriptAlias /smart/ ${GIT_EXEC_PATH}/git-http-backend/
-ScriptAlias /smart_noexport/ ${GIT_EXEC_PATH}/git-http-backend/
+</LocationMatch>
+<LocationMatch /smart_custom_env/>
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+ SetEnv GIT_HTTP_EXPORT_ALL
+ SetEnv GIT_COMMITTER_NAME "Custom User"
+ SetEnv GIT_COMMITTER_EMAIL custom@example.com
+</LocationMatch>
+<LocationMatch /smart_namespace/>
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+ SetEnv GIT_HTTP_EXPORT_ALL
+ SetEnv GIT_NAMESPACE ns
+</LocationMatch>
+<LocationMatch /smart_cookies/>
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+ SetEnv GIT_HTTP_EXPORT_ALL
+ Header set Set-Cookie name=value
+</LocationMatch>
+<LocationMatch /smart_headers/>
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+ SetEnv GIT_HTTP_EXPORT_ALL
+</LocationMatch>
+ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
+ScriptAlias /broken_smart/ broken-smart-http.sh/
+ScriptAlias /error/ error.sh/
<Directory ${GIT_EXEC_PATH}>
- Options None
+ Options FollowSymlinks
</Directory>
+<Files broken-smart-http.sh>
+ Options ExecCGI
+</Files>
+<Files error.sh>
+ Options ExecCGI
+</Files>
<Files ${GIT_EXEC_PATH}/git-http-backend>
Options ExecCGI
</Files>
+RewriteEngine on
+RewriteRule ^/smart-redir-perm/(.*)$ /smart/$1 [R=301]
+RewriteRule ^/smart-redir-temp/(.*)$ /smart/$1 [R=302]
+RewriteRule ^/smart-redir-auth/(.*)$ /auth/smart/$1 [R=301]
+RewriteRule ^/smart-redir-limited/(.*)/info/refs$ /smart/$1/info/refs [R=301]
+RewriteRule ^/ftp-redir/(.*)$ ftp://localhost:1000/$1 [R=302]
+
+RewriteRule ^/loop-redir/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-(.*) /$1 [R=302]
+RewriteRule ^/loop-redir/(.*)$ /loop-redir/x-$1 [R=302]
+
+# Apache 2.2 does not understand <RequireAll>, so we use RewriteCond.
+# And as RewriteCond does not allow testing for non-matches, we match
+# the desired case first (one has abra, two has cadabra), and let it
+# pass by marking the RewriteRule as [L], "last rule, do not process
+# any other matching RewriteRules after this"), and then have another
+# RewriteRule that matches all other cases and lets them fail via '[F]',
+# "fail the request".
+RewriteCond %{HTTP:x-magic-one} =abra
+RewriteCond %{HTTP:x-magic-two} =cadabra
+RewriteRule ^/smart_headers/.* - [L]
+RewriteRule ^/smart_headers/.* - [F]
+
<IfDefine SSL>
LoadModule ssl_module modules/mod_ssl.so
@@ -48,6 +156,42 @@ SSLMutex file:ssl_mutex
SSLEngine On
</IfDefine>
+<Location /auth/>
+ AuthType Basic
+ AuthName "git-auth"
+ AuthUserFile passwd
+ Require valid-user
+</Location>
+
+<LocationMatch "^/auth-push/.*/git-receive-pack$">
+ AuthType Basic
+ AuthName "git-auth"
+ AuthUserFile passwd
+ Require valid-user
+</LocationMatch>
+
+<LocationMatch "^/auth-fetch/.*/git-upload-pack$">
+ AuthType Basic
+ AuthName "git-auth"
+ AuthUserFile passwd
+ Require valid-user
+</LocationMatch>
+
+RewriteCond %{QUERY_STRING} service=git-receive-pack [OR]
+RewriteCond %{REQUEST_URI} /git-receive-pack$
+RewriteRule ^/half-auth-complete/ - [E=AUTHREQUIRED:yes]
+
+<Location /half-auth-complete/>
+ Order Deny,Allow
+ Deny from env=AUTHREQUIRED
+
+ AuthType Basic
+ AuthName "Git Access"
+ AuthUserFile passwd
+ Require valid-user
+ Satisfy Any
+</Location>
+
<IfDefine DAV>
LoadModule dav_module modules/mod_dav.so
LoadModule dav_fs_module modules/mod_dav_fs.so
@@ -56,6 +200,9 @@ SSLEngine On
<Location /dumb/>
Dav on
</Location>
+ <Location /auth/dumb>
+ Dav on
+ </Location>
</IfDefine>
<IfDefine SVN>
diff --git a/t/lib-httpd/broken-smart-http.sh b/t/lib-httpd/broken-smart-http.sh
new file mode 100644
index 0000000..82cc610
--- /dev/null
+++ b/t/lib-httpd/broken-smart-http.sh
@@ -0,0 +1,10 @@
+printf "Content-Type: text/%s\n" "html"
+echo
+printf "%s\n" "001e# service=git-upload-pack"
+printf "%s" "0000"
+printf "%s%c%s%s\n" \
+ "00a58681d9f286a48b08f37b3a095330da16689e3693 HEAD" \
+ 0 \
+ " include-tag multi_ack_detailed multi_ack ofs-delta" \
+ " side-band side-band-64k thin-pack no-progress shallow no-done "
+printf "%s" "0000"
diff --git a/t/lib-httpd/error.sh b/t/lib-httpd/error.sh
new file mode 100755
index 0000000..a77b8e5
--- /dev/null
+++ b/t/lib-httpd/error.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+printf "Status: 500 Intentional Breakage\n"
+
+printf "Content-Type: "
+charset=iso-8859-1
+case "$PATH_INFO" in
+*html*)
+ printf "text/html"
+ ;;
+*text*)
+ printf "text/plain"
+ ;;
+*charset*)
+ printf "text/plain; charset=utf-8"
+ charset=utf-8
+ ;;
+*utf16*)
+ printf "text/plain; charset=utf-16"
+ charset=utf-16
+ ;;
+*odd-spacing*)
+ printf "text/plain; foo=bar ;charset=utf-16; other=nonsense"
+ charset=utf-16
+ ;;
+esac
+printf "\n"
+
+printf "\n"
+printf "this is the error message\n" |
+iconv -f us-ascii -t $charset
diff --git a/t/lib-httpd/passwd b/t/lib-httpd/passwd
new file mode 100644
index 0000000..99a34d6
--- /dev/null
+++ b/t/lib-httpd/passwd
@@ -0,0 +1 @@
+user@host:xb4E8pqD81KQs
diff --git a/t/lib-pack.sh b/t/lib-pack.sh
new file mode 100644
index 0000000..7509846
--- /dev/null
+++ b/t/lib-pack.sh
@@ -0,0 +1,98 @@
+# Support routines for hand-crafting weird or malicious packs.
+#
+# You can make a complete pack like:
+#
+# pack_header 2 >foo.pack &&
+# pack_obj e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 >>foo.pack &&
+# pack_obj e68fe8129b546b101aee9510c5328e7f21ca1d18 >>foo.pack &&
+# pack_trailer foo.pack
+
+# Print the big-endian 4-byte octal representation of $1
+uint32_octal () {
+ n=$1
+ printf '\\%o' $(($n / 16777216)); n=$((n % 16777216))
+ printf '\\%o' $(($n / 65536)); n=$((n % 65536))
+ printf '\\%o' $(($n / 256)); n=$((n % 256))
+ printf '\\%o' $(($n ));
+}
+
+# Print the big-endian 4-byte binary representation of $1
+uint32_binary () {
+ printf "$(uint32_octal "$1")"
+}
+
+# Print a pack header, version 2, for a pack with $1 objects
+pack_header () {
+ printf 'PACK' &&
+ printf '\0\0\0\2' &&
+ uint32_binary "$1"
+}
+
+# Print the pack data for object $1, as a delta against object $2 (or as a full
+# object if $2 is missing or empty). The output is suitable for including
+# directly in the packfile, and represents the entirety of the object entry.
+# Doing this on the fly (especially picking your deltas) is quite tricky, so we
+# have hardcoded some well-known objects. See the case statements below for the
+# complete list.
+pack_obj () {
+ case "$1" in
+ # empty blob
+ e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)
+ case "$2" in
+ '')
+ printf '\060\170\234\003\0\0\0\0\1'
+ return
+ ;;
+ esac
+ ;;
+
+ # blob containing "\7\76"
+ e68fe8129b546b101aee9510c5328e7f21ca1d18)
+ case "$2" in
+ '')
+ printf '\062\170\234\143\267\3\0\0\116\0\106'
+ return
+ ;;
+ 01d7713666f4de822776c7622c10f1b07de280dc)
+ printf '\165\1\327\161\66\146\364\336\202\47\166' &&
+ printf '\307\142\54\20\361\260\175\342\200\334\170' &&
+ printf '\234\143\142\142\142\267\003\0\0\151\0\114'
+ return
+ ;;
+ esac
+ ;;
+
+ # blob containing "\7\0"
+ 01d7713666f4de822776c7622c10f1b07de280dc)
+ case "$2" in
+ '')
+ printf '\062\170\234\143\147\0\0\0\20\0\10'
+ return
+ ;;
+ e68fe8129b546b101aee9510c5328e7f21ca1d18)
+ printf '\165\346\217\350\22\233\124\153\20\32\356' &&
+ printf '\225\20\305\62\216\177\41\312\35\30\170\234' &&
+ printf '\143\142\142\142\147\0\0\0\53\0\16'
+ return
+ ;;
+ esac
+ ;;
+ esac
+
+ echo >&2 "BUG: don't know how to print $1${2:+ (from $2)}"
+ return 1
+}
+
+# Compute and append pack trailer to "$1"
+pack_trailer () {
+ test-sha1 -b <"$1" >trailer.tmp &&
+ cat trailer.tmp >>"$1" &&
+ rm -f trailer.tmp
+}
+
+# Remove any existing packs to make sure that
+# whatever we index next will be the pack that we
+# actually use.
+clear_packs () {
+ rm -f .git/objects/pack/*
+}
diff --git a/t/lib-pager.sh b/t/lib-pager.sh
index ba03eab..3aa7a3f 100644
--- a/t/lib-pager.sh
+++ b/t/lib-pager.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+# Helpers for tests of git's choice of pager.
test_expect_success 'determine default pager' '
test_might_fail git config --unset core.pager &&
diff --git a/t/lib-patch-mode.sh b/t/lib-patch-mode.sh
index 375e248..06c3c91 100644
--- a/t/lib-patch-mode.sh
+++ b/t/lib-patch-mode.sh
@@ -2,11 +2,6 @@
. ./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" &&
diff --git a/t/lib-proto-disable.sh b/t/lib-proto-disable.sh
new file mode 100644
index 0000000..b0917d9
--- /dev/null
+++ b/t/lib-proto-disable.sh
@@ -0,0 +1,96 @@
+# Test routines for checking protocol disabling.
+
+# test cloning a particular protocol
+# $1 - description of the protocol
+# $2 - machine-readable name of the protocol
+# $3 - the URL to try cloning
+test_proto () {
+ desc=$1
+ proto=$2
+ url=$3
+
+ test_expect_success "clone $1 (enabled)" '
+ rm -rf tmp.git &&
+ (
+ GIT_ALLOW_PROTOCOL=$proto &&
+ export GIT_ALLOW_PROTOCOL &&
+ git clone --bare "$url" tmp.git
+ )
+ '
+
+ test_expect_success "fetch $1 (enabled)" '
+ (
+ cd tmp.git &&
+ GIT_ALLOW_PROTOCOL=$proto &&
+ export GIT_ALLOW_PROTOCOL &&
+ git fetch
+ )
+ '
+
+ test_expect_success "push $1 (enabled)" '
+ (
+ cd tmp.git &&
+ GIT_ALLOW_PROTOCOL=$proto &&
+ export GIT_ALLOW_PROTOCOL &&
+ git push origin HEAD:pushed
+ )
+ '
+
+ test_expect_success "push $1 (disabled)" '
+ (
+ cd tmp.git &&
+ GIT_ALLOW_PROTOCOL=none &&
+ export GIT_ALLOW_PROTOCOL &&
+ test_must_fail git push origin HEAD:pushed
+ )
+ '
+
+ test_expect_success "fetch $1 (disabled)" '
+ (
+ cd tmp.git &&
+ GIT_ALLOW_PROTOCOL=none &&
+ export GIT_ALLOW_PROTOCOL &&
+ test_must_fail git fetch
+ )
+ '
+
+ test_expect_success "clone $1 (disabled)" '
+ rm -rf tmp.git &&
+ (
+ GIT_ALLOW_PROTOCOL=none &&
+ export GIT_ALLOW_PROTOCOL &&
+ test_must_fail git clone --bare "$url" tmp.git
+ )
+ '
+}
+
+# set up an ssh wrapper that will access $host/$repo in the
+# trash directory, and enable it for subsequent tests.
+setup_ssh_wrapper () {
+ test_expect_success 'setup ssh wrapper' '
+ write_script ssh-wrapper <<-\EOF &&
+ echo >&2 "ssh: $*"
+ host=$1; shift
+ cd "$TRASH_DIRECTORY/$host" &&
+ eval "$*"
+ EOF
+ GIT_SSH="$PWD/ssh-wrapper" &&
+ export GIT_SSH &&
+ export TRASH_DIRECTORY
+ '
+}
+
+# set up a wrapper that can be used with remote-ext to
+# access repositories in the "remote" directory of trash-dir,
+# like "ext::fake-remote %S repo.git"
+setup_ext_wrapper () {
+ test_expect_success 'setup ext wrapper' '
+ write_script fake-remote <<-\EOF &&
+ echo >&2 "fake-remote: $*"
+ cd "$TRASH_DIRECTORY/remote" &&
+ eval "$*"
+ EOF
+ PATH=$TRASH_DIRECTORY:$PATH &&
+ export TRASH_DIRECTORY
+ '
+}
diff --git a/t/lib-read-tree.sh b/t/lib-read-tree.sh
new file mode 100644
index 0000000..b95f485
--- /dev/null
+++ b/t/lib-read-tree.sh
@@ -0,0 +1,41 @@
+# Helper functions to check if read-tree would succeed/fail as expected with
+# and without the dry-run option. They also test that the dry-run does not
+# write the index and that together with -u it doesn't touch the work tree.
+#
+read_tree_must_succeed () {
+ git ls-files -s >pre-dry-run &&
+ git read-tree -n "$@" &&
+ git ls-files -s >post-dry-run &&
+ test_cmp pre-dry-run post-dry-run &&
+ git read-tree "$@"
+}
+
+read_tree_must_fail () {
+ git ls-files -s >pre-dry-run &&
+ test_must_fail git read-tree -n "$@" &&
+ git ls-files -s >post-dry-run &&
+ test_cmp pre-dry-run post-dry-run &&
+ test_must_fail git read-tree "$@"
+}
+
+read_tree_u_must_succeed () {
+ git ls-files -s >pre-dry-run &&
+ git diff-files -p >pre-dry-run-wt &&
+ git read-tree -n "$@" &&
+ git ls-files -s >post-dry-run &&
+ git diff-files -p >post-dry-run-wt &&
+ test_cmp pre-dry-run post-dry-run &&
+ test_cmp pre-dry-run-wt post-dry-run-wt &&
+ git read-tree "$@"
+}
+
+read_tree_u_must_fail () {
+ git ls-files -s >pre-dry-run &&
+ git diff-files -p >pre-dry-run-wt &&
+ test_must_fail git read-tree -n "$@" &&
+ git ls-files -s >post-dry-run &&
+ git diff-files -p >post-dry-run-wt &&
+ test_cmp pre-dry-run post-dry-run &&
+ test_cmp pre-dry-run-wt post-dry-run-wt &&
+ test_must_fail git read-tree "$@"
+}
diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh
index 6ccf797..9a96e15 100644
--- a/t/lib-rebase.sh
+++ b/t/lib-rebase.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+# Helper functions used by interactive rebase tests.
# After setting the fake editor with this function, you can
#
@@ -14,54 +14,106 @@
# specified line.
#
# "<cmd> <lineno>" -- add a line with the specified command
-# ("squash", "fixup", "edit", or "reword") and the SHA1 taken
+# ("squash", "fixup", "edit", "reword" or "drop") and the SHA1 taken
# from the specified line.
#
+# "exec_cmd_with_args" -- add an "exec cmd with args" line.
+#
# "#" -- Add a comment line.
#
# ">" -- Add a blank line.
set_fake_editor () {
- echo "#!$SHELL_PATH" >fake-editor.sh
- cat >> fake-editor.sh <<\EOF
-case "$1" in
-*/COMMIT_EDITMSG)
- test -z "$EXPECT_HEADER_COUNT" ||
- test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" ||
+ write_script fake-editor.sh <<-\EOF
+ case "$1" in
+ */COMMIT_EDITMSG)
+ test -z "$EXPECT_HEADER_COUNT" ||
+ test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" ||
+ exit
+ test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
+ test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
exit
- test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
- test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
- exit
- ;;
-esac
-test -z "$EXPECT_COUNT" ||
- test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
- exit
-test -z "$FAKE_LINES" && exit
-grep -v '^#' < "$1" > "$1".tmp
-rm -f "$1"
-echo 'rebase -i script before editing:'
-cat "$1".tmp
-action=pick
-for line in $FAKE_LINES; do
- case $line in
- squash|fixup|edit|reword)
- action="$line";;
- exec*)
- echo "$line" | sed 's/_/ /g' >> "$1";;
- "#")
- echo '# comment' >> "$1";;
- ">")
- echo >> "$1";;
- *)
- sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
- action=pick;;
+ ;;
esac
-done
-echo 'rebase -i script after editing:'
-cat "$1"
-EOF
+ test -z "$EXPECT_COUNT" ||
+ test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
+ exit
+ test -z "$FAKE_LINES" && exit
+ grep -v '^#' < "$1" > "$1".tmp
+ rm -f "$1"
+ echo 'rebase -i script before editing:'
+ cat "$1".tmp
+ action=pick
+ for line in $FAKE_LINES; do
+ case $line in
+ squash|fixup|edit|reword|drop)
+ action="$line";;
+ exec*)
+ echo "$line" | sed 's/_/ /g' >> "$1";;
+ "#")
+ echo '# comment' >> "$1";;
+ ">")
+ echo >> "$1";;
+ bad)
+ action="badcmd";;
+ fakesha)
+ echo "$action XXXXXXX False commit" >> "$1"
+ action=pick;;
+ *)
+ sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
+ action=pick;;
+ esac
+ done
+ echo 'rebase -i script after editing:'
+ cat "$1"
+ EOF
+
+ test_set_editor "$(pwd)/fake-editor.sh"
+}
+
+# After set_cat_todo_editor, rebase -i will write the todo list (ignoring
+# blank lines and comments) to stdout, and exit failure (so you should run
+# it with test_must_fail). This can be used to verify the expected user
+# experience, for todo list changes that do not affect the outcome of
+# rebase; or as an extra check in addition to checking the outcome.
+set_cat_todo_editor () {
+ write_script fake-editor.sh <<-\EOF
+ grep "^[^#]" "$1"
+ exit 1
+ EOF
test_set_editor "$(pwd)/fake-editor.sh"
- chmod a+x fake-editor.sh
+}
+
+# checks that the revisions in "$2" represent a linear range with the
+# subjects in "$1"
+test_linear_range () {
+ revlist_merges=$(git rev-list --merges "$2") &&
+ test -z "$revlist_merges" &&
+ expected=$1
+ set -- $(git log --reverse --format=%s "$2")
+ test "$expected" = "$*"
+}
+
+reset_rebase () {
+ test_might_fail git rebase --abort &&
+ git reset --hard &&
+ git clean -f
+}
+
+cherry_pick () {
+ git cherry-pick -n "$2" &&
+ git commit -m "$1" &&
+ git tag "$1"
+}
+
+revert () {
+ git revert -n "$2" &&
+ git commit -m "$1" &&
+ git tag "$1"
+}
+
+make_empty () {
+ git commit --allow-empty -m "$1" &&
+ git tag "$1"
}
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
new file mode 100755
index 0000000..79cdd34
--- /dev/null
+++ b/t/lib-submodule-update.sh
@@ -0,0 +1,680 @@
+# Create a submodule layout used for all tests below.
+#
+# The following use cases are covered:
+# - New submodule (no_submodule => add_sub1)
+# - Removed submodule (add_sub1 => remove_sub1)
+# - Updated submodule (add_sub1 => modify_sub1)
+# - Submodule updated to invalid commit (add_sub1 => invalid_sub1)
+# - Submodule updated from invalid commit (invalid_sub1 => valid_sub1)
+# - Submodule replaced by tracked files in directory (add_sub1 =>
+# replace_sub1_with_directory)
+# - Directory containing tracked files replaced by submodule
+# (replace_sub1_with_directory => replace_directory_with_sub1)
+# - Submodule replaced by tracked file with the same name (add_sub1 =>
+# replace_sub1_with_file)
+# - Tracked file replaced by submodule (replace_sub1_with_file =>
+# replace_file_with_sub1)
+#
+# --O-----O
+# / ^ replace_directory_with_sub1
+# / replace_sub1_with_directory
+# /----O
+# / ^
+# / modify_sub1
+# O------O-------O
+# ^ ^\ ^
+# | | \ remove_sub1
+# | | -----O-----O
+# | | \ ^ replace_file_with_sub1
+# | | \ replace_sub1_with_file
+# | add_sub1 --O-----O
+# no_submodule ^ valid_sub1
+# invalid_sub1
+#
+create_lib_submodule_repo () {
+ git init submodule_update_repo &&
+ (
+ cd submodule_update_repo &&
+ echo "expect" >>.gitignore &&
+ echo "actual" >>.gitignore &&
+ echo "x" >file1 &&
+ echo "y" >file2 &&
+ git add .gitignore file1 file2 &&
+ git commit -m "Base" &&
+ git branch "no_submodule" &&
+
+ git checkout -b "add_sub1" &&
+ git submodule add ./. sub1 &&
+ git config -f .gitmodules submodule.sub1.ignore all &&
+ git config submodule.sub1.ignore all &&
+ git add .gitmodules &&
+ git commit -m "Add sub1" &&
+ git checkout -b remove_sub1 &&
+ git revert HEAD &&
+
+ git checkout -b "modify_sub1" "add_sub1" &&
+ git submodule update &&
+ (
+ cd sub1 &&
+ git fetch &&
+ git checkout -b "modifications" &&
+ echo "z" >file2 &&
+ echo "x" >file3 &&
+ git add file2 file3 &&
+ git commit -m "modified file2 and added file3" &&
+ git push origin modifications
+ ) &&
+ git add sub1 &&
+ git commit -m "Modify sub1" &&
+
+ git checkout -b "replace_sub1_with_directory" "add_sub1" &&
+ git submodule update &&
+ (
+ cd sub1 &&
+ git checkout modifications
+ ) &&
+ git rm --cached sub1 &&
+ rm sub1/.git* &&
+ git config -f .gitmodules --remove-section "submodule.sub1" &&
+ git add .gitmodules sub1/* &&
+ git commit -m "Replace sub1 with directory" &&
+ git checkout -b replace_directory_with_sub1 &&
+ git revert HEAD &&
+
+ git checkout -b "replace_sub1_with_file" "add_sub1" &&
+ git rm sub1 &&
+ echo "content" >sub1 &&
+ git add sub1 &&
+ git commit -m "Replace sub1 with file" &&
+ git checkout -b replace_file_with_sub1 &&
+ git revert HEAD &&
+
+ git checkout -b "invalid_sub1" "add_sub1" &&
+ git update-index --cacheinfo 160000 0123456789012345678901234567890123456789 sub1 &&
+ git commit -m "Invalid sub1 commit" &&
+ git checkout -b valid_sub1 &&
+ git revert HEAD &&
+ git checkout master
+ )
+}
+
+# Helper function to replace gitfile with .git directory
+replace_gitfile_with_git_dir () {
+ (
+ cd "$1" &&
+ git_dir="$(git rev-parse --git-dir)" &&
+ rm -f .git &&
+ cp -R "$git_dir" .git &&
+ GIT_WORK_TREE=. git config --unset core.worktree
+ )
+}
+
+# Test that the .git directory in the submodule is unchanged (except for the
+# core.worktree setting, which appears only in $GIT_DIR/modules/$1/config).
+# Call this function before test_submodule_content as the latter might
+# write the index file leading to false positive index differences.
+#
+# Note that this only supports submodules at the root level of the
+# superproject, with the default name, i.e. same as its path.
+test_git_directory_is_unchanged () {
+ (
+ cd ".git/modules/$1" &&
+ # does core.worktree point at the right place?
+ test "$(git config core.worktree)" = "../../../$1" &&
+ # remove it temporarily before comparing, as
+ # "$1/.git/config" lacks it...
+ git config --unset core.worktree
+ ) &&
+ diff -r ".git/modules/$1" "$1/.git" &&
+ (
+ # ... and then restore.
+ cd ".git/modules/$1" &&
+ git config core.worktree "../../../$1"
+ )
+}
+
+# Helper function to be executed at the start of every test below, it sets up
+# the submodule repo if it doesn't exist and configures the most problematic
+# settings for diff.ignoreSubmodules.
+prolog () {
+ (test -d submodule_update_repo || create_lib_submodule_repo) &&
+ test_config_global diff.ignoreSubmodules all &&
+ test_config diff.ignoreSubmodules all
+}
+
+# Helper function to bring work tree back into the state given by the
+# commit. This includes trying to populate sub1 accordingly if it exists and
+# should be updated to an existing commit.
+reset_work_tree_to () {
+ rm -rf submodule_update &&
+ git clone submodule_update_repo submodule_update &&
+ (
+ cd submodule_update &&
+ rm -rf sub1 &&
+ git checkout -f "$1" &&
+ git status -u -s >actual &&
+ test_must_be_empty actual &&
+ sha1=$(git rev-parse --revs-only HEAD:sub1) &&
+ if test -n "$sha1" &&
+ test $(cd "sub1" && git rev-parse --verify "$sha1^{commit}")
+ then
+ git submodule update --init --recursive "sub1"
+ fi
+ )
+}
+
+# Test that the superproject contains the content according to commit "$1"
+# (the work tree must match the index for everything but submodules but the
+# index must exactly match the given commit including any submodule SHA-1s).
+test_superproject_content () {
+ git diff-index --cached "$1" >actual &&
+ test_must_be_empty actual &&
+ git diff-files --ignore-submodules >actual &&
+ test_must_be_empty actual
+}
+
+# Test that the given submodule at path "$1" contains the content according
+# to the submodule commit recorded in the superproject's commit "$2"
+test_submodule_content () {
+ if test $# != 2
+ then
+ echo "test_submodule_content needs two arguments"
+ return 1
+ fi &&
+ submodule="$1" &&
+ commit="$2" &&
+ test -d "$submodule"/ &&
+ if ! test -f "$submodule"/.git && ! test -d "$submodule"/.git
+ then
+ echo "Submodule $submodule is not populated"
+ return 1
+ fi &&
+ sha1=$(git rev-parse --verify "$commit:$submodule") &&
+ if test -z "$sha1"
+ then
+ echo "Couldn't retrieve SHA-1 of $submodule for $commit"
+ return 1
+ fi &&
+ (
+ cd "$submodule" &&
+ git status -u -s >actual &&
+ test_must_be_empty actual &&
+ git diff "$sha1" >actual &&
+ test_must_be_empty actual
+ )
+}
+
+# Test that the following transitions are correctly handled:
+# - Updated submodule
+# - New submodule
+# - Removed submodule
+# - Directory containing tracked files replaced by submodule
+# - Submodule replaced by tracked files in directory
+# - Submodule replaced by tracked file with the same name
+# - tracked file replaced by submodule
+#
+# The default is that submodule contents aren't changed until "git submodule
+# update" is run. And even then that command doesn't delete the work tree of
+# a removed submodule.
+#
+# Removing a submodule containing a .git directory must fail even when forced
+# to protect the history!
+#
+
+# Test that submodule contents are currently not updated when switching
+# between commits that change a submodule.
+test_submodule_switch () {
+ command="$1"
+ ######################### Appearing submodule #########################
+ # Switching to a commit letting a submodule appear creates empty dir ...
+ if test "$KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES" = 1
+ then
+ # Restoring stash fails to restore submodule index entry
+ RESULT="failure"
+ else
+ RESULT="success"
+ fi
+ test_expect_$RESULT "$command: added submodule creates empty directory" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ $command add_sub1 &&
+ test_superproject_content origin/add_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... and doesn't care if it already exists ...
+ test_expect_$RESULT "$command: added submodule leaves existing empty directory alone" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ mkdir sub1 &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ $command add_sub1 &&
+ test_superproject_content origin/add_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... unless there is an untracked file in its place.
+ test_expect_success "$command: added submodule doesn't remove untracked unignored file with same name" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ >sub1 &&
+ test_must_fail $command add_sub1 &&
+ test_superproject_content origin/no_submodule &&
+ test_must_be_empty sub1
+ )
+ '
+ # Replacing a tracked file with a submodule produces an empty
+ # directory ...
+ test_expect_$RESULT "$command: replace tracked file with submodule creates empty directory" '
+ prolog &&
+ reset_work_tree_to replace_sub1_with_file &&
+ (
+ cd submodule_update &&
+ git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
+ $command replace_file_with_sub1 &&
+ test_superproject_content origin/replace_file_with_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/replace_file_with_sub1
+ )
+ '
+ # ... as does removing a directory with tracked files with a
+ # submodule.
+ if test "$KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR" = 1
+ then
+ # Non fast-forward merges fail with "Directory sub1 doesn't
+ # exist. sub1" because the empty submodule directory is not
+ # created
+ RESULT="failure"
+ else
+ RESULT="success"
+ fi
+ test_expect_$RESULT "$command: replace directory with submodule" '
+ prolog &&
+ reset_work_tree_to replace_sub1_with_directory &&
+ (
+ cd submodule_update &&
+ git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
+ $command replace_directory_with_sub1 &&
+ test_superproject_content origin/replace_directory_with_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/replace_directory_with_sub1
+ )
+ '
+
+ ######################## Disappearing submodule #######################
+ # Removing a submodule doesn't remove its work tree ...
+ if test "$KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES" = 1
+ then
+ RESULT="failure"
+ else
+ RESULT="success"
+ fi
+ test_expect_$RESULT "$command: removed submodule leaves submodule directory and its contents in place" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t remove_sub1 origin/remove_sub1 &&
+ $command remove_sub1 &&
+ test_superproject_content origin/remove_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... especially when it contains a .git directory.
+ test_expect_$RESULT "$command: removed submodule leaves submodule containing a .git directory alone" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t remove_sub1 origin/remove_sub1 &&
+ replace_gitfile_with_git_dir sub1 &&
+ $command remove_sub1 &&
+ test_superproject_content origin/remove_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Replacing a submodule with files in a directory must fail as the
+ # submodule work tree isn't removed ...
+ if test "$KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES" = 1
+ then
+ # Non fast-forward merges attempt to merge the former
+ # submodule files with the newly checked out ones in the
+ # directory of the same name while it shouldn't.
+ RESULT="failure"
+ else
+ RESULT="success"
+ fi
+ test_expect_$RESULT "$command: replace submodule with a directory must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+ test_must_fail $command replace_sub1_with_directory &&
+ test_superproject_content origin/add_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... especially when it contains a .git directory.
+ test_expect_$RESULT "$command: replace submodule containing a .git directory with a directory must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+ replace_gitfile_with_git_dir sub1 &&
+ test_must_fail $command replace_sub1_with_directory &&
+ test_superproject_content origin/add_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Replacing it with a file must fail as it could throw away any local
+ # work tree changes ...
+ test_expect_failure "$command: replace submodule with a file must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+ test_must_fail $command replace_sub1_with_file &&
+ test_superproject_content origin/add_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... or even destroy unpushed parts of submodule history if that
+ # still uses a .git directory.
+ test_expect_failure "$command: replace submodule containing a .git directory with a file must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+ replace_gitfile_with_git_dir sub1 &&
+ test_must_fail $command replace_sub1_with_file &&
+ test_superproject_content origin/add_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+
+ ########################## Modified submodule #########################
+ # Updating a submodule sha1 doesn't update the submodule's work tree
+ if test "$KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT" = 1
+ then
+ # When cherry picking a SHA-1 update for an ignored submodule
+ # the commit incorrectly fails with "The previous cherry-pick
+ # is now empty, possibly due to conflict resolution."
+ RESULT="failure"
+ else
+ RESULT="success"
+ fi
+ test_expect_$RESULT "$command: modified submodule does not update submodule work tree" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t modify_sub1 origin/modify_sub1 &&
+ $command modify_sub1 &&
+ test_superproject_content origin/modify_sub1 &&
+ test_submodule_content sub1 origin/add_sub1 &&
+ git submodule update &&
+ test_submodule_content sub1 origin/modify_sub1
+ )
+ '
+
+ # Updating a submodule to an invalid sha1 doesn't update the
+ # submodule's work tree, subsequent update will fail
+ test_expect_$RESULT "$command: modified submodule does not update submodule work tree to invalid commit" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t invalid_sub1 origin/invalid_sub1 &&
+ $command invalid_sub1 &&
+ test_superproject_content origin/invalid_sub1 &&
+ test_submodule_content sub1 origin/add_sub1 &&
+ test_must_fail git submodule update &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Updating a submodule from an invalid sha1 doesn't update the
+ # submodule's work tree, subsequent update will succeed
+ test_expect_$RESULT "$command: modified submodule does not update submodule work tree from invalid commit" '
+ prolog &&
+ reset_work_tree_to invalid_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t valid_sub1 origin/valid_sub1 &&
+ $command valid_sub1 &&
+ test_superproject_content origin/valid_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/valid_sub1
+ )
+ '
+}
+
+# Test that submodule contents are currently not updated when switching
+# between commits that change a submodule, but throwing away local changes in
+# the superproject is allowed.
+test_submodule_forced_switch () {
+ command="$1"
+ ######################### Appearing submodule #########################
+ # Switching to a commit letting a submodule appear creates empty dir ...
+ test_expect_success "$command: added submodule creates empty directory" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ $command add_sub1 &&
+ test_superproject_content origin/add_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... and doesn't care if it already exists ...
+ test_expect_success "$command: added submodule leaves existing empty directory alone" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ mkdir sub1 &&
+ $command add_sub1 &&
+ test_superproject_content origin/add_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... unless there is an untracked file in its place.
+ test_expect_success "$command: added submodule does remove untracked unignored file with same name when forced" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ >sub1 &&
+ $command add_sub1 &&
+ test_superproject_content origin/add_sub1 &&
+ test_dir_is_empty sub1
+ )
+ '
+ # Replacing a tracked file with a submodule produces an empty
+ # directory ...
+ test_expect_success "$command: replace tracked file with submodule creates empty directory" '
+ prolog &&
+ reset_work_tree_to replace_sub1_with_file &&
+ (
+ cd submodule_update &&
+ git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
+ $command replace_file_with_sub1 &&
+ test_superproject_content origin/replace_file_with_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/replace_file_with_sub1
+ )
+ '
+ # ... as does removing a directory with tracked files with a
+ # submodule.
+ test_expect_success "$command: replace directory with submodule" '
+ prolog &&
+ reset_work_tree_to replace_sub1_with_directory &&
+ (
+ cd submodule_update &&
+ git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
+ $command replace_directory_with_sub1 &&
+ test_superproject_content origin/replace_directory_with_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/replace_directory_with_sub1
+ )
+ '
+
+ ######################## Disappearing submodule #######################
+ # Removing a submodule doesn't remove its work tree ...
+ test_expect_success "$command: removed submodule leaves submodule directory and its contents in place" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t remove_sub1 origin/remove_sub1 &&
+ $command remove_sub1 &&
+ test_superproject_content origin/remove_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... especially when it contains a .git directory.
+ test_expect_success "$command: removed submodule leaves submodule containing a .git directory alone" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t remove_sub1 origin/remove_sub1 &&
+ replace_gitfile_with_git_dir sub1 &&
+ $command remove_sub1 &&
+ test_superproject_content origin/remove_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Replacing a submodule with files in a directory must fail as the
+ # submodule work tree isn't removed ...
+ test_expect_failure "$command: replace submodule with a directory must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+ test_must_fail $command replace_sub1_with_directory &&
+ test_superproject_content origin/add_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... especially when it contains a .git directory.
+ test_expect_failure "$command: replace submodule containing a .git directory with a directory must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+ replace_gitfile_with_git_dir sub1 &&
+ test_must_fail $command replace_sub1_with_directory &&
+ test_superproject_content origin/add_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Replacing it with a file must fail as it could throw away any local
+ # work tree changes ...
+ test_expect_failure "$command: replace submodule with a file must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+ test_must_fail $command replace_sub1_with_file &&
+ test_superproject_content origin/add_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... or even destroy unpushed parts of submodule history if that
+ # still uses a .git directory.
+ test_expect_failure "$command: replace submodule containing a .git directory with a file must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+ replace_gitfile_with_git_dir sub1 &&
+ test_must_fail $command replace_sub1_with_file &&
+ test_superproject_content origin/add_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+
+ ########################## Modified submodule #########################
+ # Updating a submodule sha1 doesn't update the submodule's work tree
+ test_expect_success "$command: modified submodule does not update submodule work tree" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t modify_sub1 origin/modify_sub1 &&
+ $command modify_sub1 &&
+ test_superproject_content origin/modify_sub1 &&
+ test_submodule_content sub1 origin/add_sub1 &&
+ git submodule update &&
+ test_submodule_content sub1 origin/modify_sub1
+ )
+ '
+ # Updating a submodule to an invalid sha1 doesn't update the
+ # submodule's work tree, subsequent update will fail
+ test_expect_success "$command: modified submodule does not update submodule work tree to invalid commit" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t invalid_sub1 origin/invalid_sub1 &&
+ $command invalid_sub1 &&
+ test_superproject_content origin/invalid_sub1 &&
+ test_submodule_content sub1 origin/add_sub1 &&
+ test_must_fail git submodule update &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Updating a submodule from an invalid sha1 doesn't update the
+ # submodule's work tree, subsequent update will succeed
+ test_expect_success "$command: modified submodule does not update submodule work tree from invalid commit" '
+ prolog &&
+ reset_work_tree_to invalid_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t valid_sub1 origin/valid_sub1 &&
+ $command valid_sub1 &&
+ test_superproject_content origin/valid_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/valid_sub1
+ )
+ '
+}
diff --git a/t/lib-t6000.sh b/t/lib-t6000.sh
index ea25dd8..3f2d873 100644
--- a/t/lib-t6000.sh
+++ b/t/lib-t6000.sh
@@ -1,55 +1,50 @@
: included from 6002 and others
-[ -d .git/refs/tags ] || mkdir -p .git/refs/tags
+mkdir -p .git/refs/tags
-:> sed.script
+>sed.script
-# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags
-tag()
-{
+# Answer the sha1 has associated with the tag. The tag must exist in .git/refs/tags
+tag () {
_tag=$1
- [ -f .git/refs/tags/$_tag ] || error "tag: \"$_tag\" does not exist"
- cat .git/refs/tags/$_tag
+ test -f ".git/refs/tags/$_tag" || error "tag: \"$_tag\" does not exist"
+ cat ".git/refs/tags/$_tag"
}
# Generate a commit using the text specified to make it unique and the tree
# named by the tag specified.
-unique_commit()
-{
+unique_commit () {
_text=$1
- _tree=$2
+ _tree=$2
shift 2
- echo $_text | git commit-tree $(tag $_tree) "$@"
+ echo "$_text" | git commit-tree $(tag "$_tree") "$@"
}
# Save the output of a command into the tag specified. Prepend
# a substitution script for the tag onto the front of sed.script
-save_tag()
-{
+save_tag () {
_tag=$1
- [ -n "$_tag" ] || error "usage: save_tag tag commit-args ..."
+ test -n "$_tag" || error "usage: save_tag tag commit-args ..."
shift 1
- "$@" >.git/refs/tags/$_tag
+ "$@" >".git/refs/tags/$_tag"
- echo "s/$(tag $_tag)/$_tag/g" > sed.script.tmp
- cat sed.script >> sed.script.tmp
+ echo "s/$(tag $_tag)/$_tag/g" >sed.script.tmp
+ cat sed.script >>sed.script.tmp
rm sed.script
mv sed.script.tmp sed.script
}
-# Replace unhelpful sha1 hashses with their symbolic equivalents
-entag()
-{
+# Replace unhelpful sha1 hashes with their symbolic equivalents
+entag () {
sed -f sed.script
}
# Execute a command after first saving, then setting the GIT_AUTHOR_EMAIL
# tag to a specified value. Restore the original value on return.
-as_author()
-{
+as_author () {
_author=$1
shift 1
- _save=$GIT_AUTHOR_EMAIL
+ _save=$GIT_AUTHOR_EMAIL
GIT_AUTHOR_EMAIL="$_author"
export GIT_AUTHOR_EMAIL
@@ -63,45 +58,58 @@ as_author()
fi
}
-commit_date()
-{
- _commit=$1
- git cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
+commit_date () {
+ _commit=$1
+ git cat-file commit $_commit |
+ sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
}
-on_committer_date()
-{
- _date=$1
- shift 1
- GIT_COMMITTER_DATE="$_date"
- export GIT_COMMITTER_DATE
- "$@"
- unset GIT_COMMITTER_DATE
+# Assign the value of fake date to a variable, but
+# allow fairly common "1971-08-16 00:00" to be omittd
+assign_fake_date () {
+ case "$2" in
+ ??:??:??) eval "$1='1971-08-16 $2'" ;;
+ ??:??) eval "$1='1971-08-16 00:$2'" ;;
+ ??) eval "$1='1971-08-16 00:00:$2'" ;;
+ *) eval "$1='$2'" ;;
+ esac
+}
+
+on_committer_date () {
+ assign_fake_date GIT_COMMITTER_DATE "$1"
+ export GIT_COMMITTER_DATE
+ shift 1
+ "$@"
+}
+
+on_dates () {
+ assign_fake_date GIT_COMMITTER_DATE "$1"
+ assign_fake_date GIT_AUTHOR_DATE "$2"
+ export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+ shift 2
+ "$@"
}
# Execute a command and suppress any error output.
-hide_error()
-{
+hide_error () {
"$@" 2>/dev/null
}
-check_output()
-{
+check_output () {
_name=$1
shift 1
- if eval "$*" | entag > $_name.actual
+ if eval "$*" | entag >"$_name.actual"
then
- test_cmp $_name.expected $_name.actual
+ test_cmp "$_name.expected" "$_name.actual"
else
- return 1;
+ return 1
fi
}
# Turn a reasonable test description into a reasonable test name.
# All alphanums translated into -'s which are then compressed and stripped
# from front and back.
-name_from_description()
-{
+name_from_description () {
perl -pe '
s/[^A-Za-z0-9.]/-/g;
s/-+/-/g;
@@ -119,9 +127,11 @@ name_from_description()
test_output_expect_success()
{
_description=$1
- _test=$2
- [ $# -eq 2 ] || error "usage: test_output_expect_success description test <<EOF ... EOF"
- _name=$(echo $_description | name_from_description)
- cat > $_name.expected
+ _test=$2
+ test $# -eq 2 ||
+ error "usage: test_output_expect_success description test <<EOF ... EOF"
+
+ _name=$(echo $_description | name_from_description)
+ cat >"$_name.expected"
test_expect_success "$_description" "check_output $_name \"$_test\""
}
diff --git a/t/lib-terminal.sh b/t/lib-terminal.sh
new file mode 100644
index 0000000..cd220e3
--- /dev/null
+++ b/t/lib-terminal.sh
@@ -0,0 +1,36 @@
+# Helpers for terminal output tests.
+
+# Catch tests which should depend on TTY but forgot to. There's no need
+# to additionally check that the TTY prereq is set here. If the test declared
+# it and we are running the test, then it must have been set.
+test_terminal () {
+ if ! test_declared_prereq TTY
+ then
+ echo >&4 "test_terminal: need to declare TTY prerequisite"
+ return 127
+ fi
+ perl "$TEST_DIRECTORY"/test-terminal.perl "$@"
+}
+
+test_lazy_prereq TTY '
+ test_have_prereq PERL &&
+
+ # Reading from the pty master seems to get stuck _sometimes_
+ # on Mac OS X 10.5.0, using Perl 5.10.0 or 5.8.9.
+ #
+ # Reproduction recipe: run
+ #
+ # i=0
+ # while ./test-terminal.perl echo hi $i
+ # do
+ # : $((i = $i + 1))
+ # done
+ #
+ # After 2000 iterations or so it hangs.
+ # https://rt.cpan.org/Ticket/Display.html?id=65692
+ #
+ test "$(uname -s)" != Darwin &&
+
+ perl "$TEST_DIRECTORY"/test-terminal.perl \
+ sh -c "test -t 1 && test -t 2"
+'
diff --git a/t/perf/.gitignore b/t/perf/.gitignore
new file mode 100644
index 0000000..982eb8e
--- /dev/null
+++ b/t/perf/.gitignore
@@ -0,0 +1,3 @@
+/build/
+/test-results/
+/trash directory*/
diff --git a/t/perf/Makefile b/t/perf/Makefile
new file mode 100644
index 0000000..8c47155
--- /dev/null
+++ b/t/perf/Makefile
@@ -0,0 +1,15 @@
+-include ../../config.mak
+export GIT_TEST_OPTIONS
+
+all: perf
+
+perf: pre-clean
+ ./run
+
+pre-clean:
+ rm -rf test-results
+
+clean:
+ rm -rf build "trash directory".* test-results
+
+.PHONY: all perf pre-clean clean
diff --git a/t/perf/README b/t/perf/README
new file mode 100644
index 0000000..8848c14
--- /dev/null
+++ b/t/perf/README
@@ -0,0 +1,146 @@
+Git performance tests
+=====================
+
+This directory holds performance testing scripts for git tools. The
+first part of this document describes the various ways in which you
+can run them.
+
+When fixing the tools or adding enhancements, you are strongly
+encouraged to add tests in this directory to cover what you are
+trying to fix or enhance. The later part of this short document
+describes how your test scripts should be organized.
+
+
+Running Tests
+-------------
+
+The easiest way to run tests is to say "make". This runs all
+the tests on the current git repository.
+
+ === Running 2 tests in this tree ===
+ [...]
+ Test this tree
+ ---------------------------------------------------------
+ 0001.1: rev-list --all 0.54(0.51+0.02)
+ 0001.2: rev-list --all --objects 6.14(5.99+0.11)
+ 7810.1: grep worktree, cheap regex 0.16(0.16+0.35)
+ 7810.2: grep worktree, expensive regex 7.90(29.75+0.37)
+ 7810.3: grep --cached, cheap regex 3.07(3.02+0.25)
+ 7810.4: grep --cached, expensive regex 9.39(30.57+0.24)
+
+You can compare multiple repositories and even git revisions with the
+'run' script:
+
+ $ ./run . origin/next /path/to/git-tree p0001-rev-list.sh
+
+where . stands for the current git tree. The full invocation is
+
+ ./run [<revision|directory>...] [--] [<test-script>...]
+
+A '.' argument is implied if you do not pass any other
+revisions/directories.
+
+You can also manually test this or another git build tree, and then
+call the aggregation script to summarize the results:
+
+ $ ./p0001-rev-list.sh
+ [...]
+ $ GIT_BUILD_DIR=/path/to/other/git ./p0001-rev-list.sh
+ [...]
+ $ ./aggregate.perl . /path/to/other/git ./p0001-rev-list.sh
+
+aggregate.perl has the same invocation as 'run', it just does not run
+anything beforehand.
+
+You can set the following variables (also in your config.mak):
+
+ GIT_PERF_REPEAT_COUNT
+ Number of times a test should be repeated for best-of-N
+ measurements. Defaults to 3.
+
+ GIT_PERF_MAKE_OPTS
+ Options to use when automatically building a git tree for
+ performance testing. E.g., -j6 would be useful.
+
+ GIT_PERF_REPO
+ GIT_PERF_LARGE_REPO
+ Repositories to copy for the performance tests. The normal
+ repo should be at least git.git size. The large repo should
+ probably be about linux.git size for optimal results.
+ Both default to the git.git you are running from.
+
+You can also pass the options taken by ordinary git tests; the most
+useful one is:
+
+--root=<directory>::
+ Create "trash" directories used to store all temporary data during
+ testing under <directory>, instead of the t/ directory.
+ Using this option with a RAM-based filesystem (such as tmpfs)
+ can massively speed up the test suite.
+
+
+Naming Tests
+------------
+
+The performance test files are named as:
+
+ pNNNN-commandname-details.sh
+
+where N is a decimal digit. The same conventions for choosing NNNN as
+for normal tests apply.
+
+
+Writing Tests
+-------------
+
+The perf script starts much like a normal test script, except it
+sources perf-lib.sh:
+
+ #!/bin/sh
+ #
+ # Copyright (c) 2005 Junio C Hamano
+ #
+
+ test_description='xxx performance test'
+ . ./perf-lib.sh
+
+After that you will want to use some of the following:
+
+ test_perf_default_repo # sets up a "normal" repository
+ test_perf_large_repo # sets up a "large" repository
+
+ test_perf_default_repo sub # ditto, in a subdir "sub"
+
+ test_checkout_worktree # if you need the worktree too
+
+At least one of the first two is required!
+
+You can use test_expect_success as usual. For actual performance
+tests, use
+
+ test_perf 'descriptive string' '
+ command1 &&
+ command2
+ '
+
+test_perf spawns a subshell, for lack of better options. This means
+that
+
+* you _must_ export all variables that you need in the subshell
+
+* you _must_ flag all variables that you want to persist from the
+ subshell with 'test_export':
+
+ test_perf 'descriptive string' '
+ foo=$(git rev-parse HEAD) &&
+ test_export foo
+ '
+
+ The so-exported variables are automatically marked for export in the
+ shell executing the perf test. For your convenience, test_export is
+ the same as export in the main shell.
+
+ This feature relies on a bit of magic using 'set' and 'source'.
+ While we have tried to make sure that it can cope with embedded
+ whitespace and other special characters, it will not work with
+ multi-line data.
diff --git a/t/perf/aggregate.perl b/t/perf/aggregate.perl
new file mode 100755
index 0000000..924b19d
--- /dev/null
+++ b/t/perf/aggregate.perl
@@ -0,0 +1,167 @@
+#!/usr/bin/perl
+
+use lib '../../perl/blib/lib';
+use strict;
+use warnings;
+use Git;
+
+sub get_times {
+ my $name = shift;
+ open my $fh, "<", $name or return undef;
+ my $line = <$fh>;
+ return undef if not defined $line;
+ close $fh or die "cannot close $name: $!";
+ $line =~ /^(?:(\d+):)?(\d+):(\d+(?:\.\d+)?) (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)$/
+ or die "bad input line: $line";
+ my $rt = ((defined $1 ? $1 : 0.0)*60+$2)*60+$3;
+ return ($rt, $4, $5);
+}
+
+sub format_times {
+ my ($r, $u, $s, $firstr) = @_;
+ if (!defined $r) {
+ return "<missing>";
+ }
+ my $out = sprintf "%.2f(%.2f+%.2f)", $r, $u, $s;
+ if (defined $firstr) {
+ if ($firstr > 0) {
+ $out .= sprintf " %+.1f%%", 100.0*($r-$firstr)/$firstr;
+ } elsif ($r == 0) {
+ $out .= " =";
+ } else {
+ $out .= " +inf";
+ }
+ }
+ return $out;
+}
+
+my (@dirs, %dirnames, %dirabbrevs, %prefixes, @tests);
+while (scalar @ARGV) {
+ my $arg = $ARGV[0];
+ my $dir;
+ last if -f $arg or $arg eq "--";
+ if (! -d $arg) {
+ my $rev = Git::command_oneline(qw(rev-parse --verify), $arg);
+ $dir = "build/".$rev;
+ } else {
+ $arg =~ s{/*$}{};
+ $dir = $arg;
+ $dirabbrevs{$dir} = $dir;
+ }
+ push @dirs, $dir;
+ $dirnames{$dir} = $arg;
+ my $prefix = $dir;
+ $prefix =~ tr/^a-zA-Z0-9/_/c;
+ $prefixes{$dir} = $prefix . '.';
+ shift @ARGV;
+}
+
+if (not @dirs) {
+ @dirs = ('.');
+}
+$dirnames{'.'} = $dirabbrevs{'.'} = "this tree";
+$prefixes{'.'} = '';
+
+shift @ARGV if scalar @ARGV and $ARGV[0] eq "--";
+
+@tests = @ARGV;
+if (not @tests) {
+ @tests = glob "p????-*.sh";
+}
+
+my @subtests;
+my %shorttests;
+for my $t (@tests) {
+ $t =~ s{(?:.*/)?(p(\d+)-[^/]+)\.sh$}{$1} or die "bad test name: $t";
+ my $n = $2;
+ my $fname = "test-results/$t.subtests";
+ open my $fp, "<", $fname or die "cannot open $fname: $!";
+ for (<$fp>) {
+ chomp;
+ /^(\d+)$/ or die "malformed subtest line: $_";
+ push @subtests, "$t.$1";
+ $shorttests{"$t.$1"} = "$n.$1";
+ }
+ close $fp or die "cannot close $fname: $!";
+}
+
+sub read_descr {
+ my $name = shift;
+ open my $fh, "<", $name or return "<error reading description>";
+ my $line = <$fh>;
+ close $fh or die "cannot close $name";
+ chomp $line;
+ return $line;
+}
+
+my %descrs;
+my $descrlen = 4; # "Test"
+for my $t (@subtests) {
+ $descrs{$t} = $shorttests{$t}.": ".read_descr("test-results/$t.descr");
+ $descrlen = length $descrs{$t} if length $descrs{$t}>$descrlen;
+}
+
+sub have_duplicate {
+ my %seen;
+ for (@_) {
+ return 1 if exists $seen{$_};
+ $seen{$_} = 1;
+ }
+ return 0;
+}
+sub have_slash {
+ for (@_) {
+ return 1 if m{/};
+ }
+ return 0;
+}
+
+my %newdirabbrevs = %dirabbrevs;
+while (!have_duplicate(values %newdirabbrevs)) {
+ %dirabbrevs = %newdirabbrevs;
+ last if !have_slash(values %dirabbrevs);
+ %newdirabbrevs = %dirabbrevs;
+ for (values %newdirabbrevs) {
+ s{^[^/]*/}{};
+ }
+}
+
+my %times;
+my @colwidth = ((0)x@dirs);
+for my $i (0..$#dirs) {
+ my $d = $dirs[$i];
+ my $w = length (exists $dirabbrevs{$d} ? $dirabbrevs{$d} : $dirnames{$d});
+ $colwidth[$i] = $w if $w > $colwidth[$i];
+}
+for my $t (@subtests) {
+ my $firstr;
+ for my $i (0..$#dirs) {
+ my $d = $dirs[$i];
+ $times{$prefixes{$d}.$t} = [get_times("test-results/$prefixes{$d}$t.times")];
+ my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}};
+ my $w = length format_times($r,$u,$s,$firstr);
+ $colwidth[$i] = $w if $w > $colwidth[$i];
+ $firstr = $r unless defined $firstr;
+ }
+}
+my $totalwidth = 3*@dirs+$descrlen;
+$totalwidth += $_ for (@colwidth);
+
+printf "%-${descrlen}s", "Test";
+for my $i (0..$#dirs) {
+ my $d = $dirs[$i];
+ printf " %-$colwidth[$i]s", (exists $dirabbrevs{$d} ? $dirabbrevs{$d} : $dirnames{$d});
+}
+print "\n";
+print "-"x$totalwidth, "\n";
+for my $t (@subtests) {
+ printf "%-${descrlen}s", $descrs{$t};
+ my $firstr;
+ for my $i (0..$#dirs) {
+ my $d = $dirs[$i];
+ my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}};
+ printf " %-$colwidth[$i]s", format_times($r,$u,$s,$firstr);
+ $firstr = $r unless defined $firstr;
+ }
+ print "\n";
+}
diff --git a/t/perf/min_time.perl b/t/perf/min_time.perl
new file mode 100755
index 0000000..c1a2717
--- /dev/null
+++ b/t/perf/min_time.perl
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+
+my $minrt = 1e100;
+my $min;
+
+while (<>) {
+ # [h:]m:s.xx U.xx S.xx
+ /^(?:(\d+):)?(\d+):(\d+(?:\.\d+)?) (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)$/
+ or die "bad input line: $_";
+ my $rt = ((defined $1 ? $1 : 0.0)*60+$2)*60+$3;
+ if ($rt < $minrt) {
+ $min = $_;
+ $minrt = $rt;
+ }
+}
+
+if (!defined $min) {
+ die "no input found";
+}
+
+print $min;
diff --git a/t/perf/p0000-perf-lib-sanity.sh b/t/perf/p0000-perf-lib-sanity.sh
new file mode 100755
index 0000000..cf8e1ef
--- /dev/null
+++ b/t/perf/p0000-perf-lib-sanity.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='Tests whether perf-lib facilities work'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_perf 'test_perf_default_repo works' '
+ foo=$(git rev-parse HEAD) &&
+ test_export foo
+'
+
+test_checkout_worktree
+
+test_perf 'test_checkout_worktree works' '
+ wt=$(find . | wc -l) &&
+ idx=$(git ls-files | wc -l) &&
+ test $wt -gt $idx
+'
+
+baz=baz
+test_export baz
+
+test_expect_success 'test_export works' '
+ echo "$foo" &&
+ test "$foo" = "$(git rev-parse HEAD)" &&
+ echo "$baz" &&
+ test "$baz" = baz
+'
+
+test_perf 'export a weird var' '
+ bar="weird # variable" &&
+ test_export bar
+'
+
+test_expect_success 'test_export works with weird vars' '
+ echo "$bar" &&
+ test "$bar" = "weird # variable"
+'
+
+test_perf 'important variables available in subshells' '
+ test -n "$HOME" &&
+ test -n "$TEST_DIRECTORY" &&
+ test -n "$TRASH_DIRECTORY" &&
+ test -n "$GIT_BUILD_DIR"
+'
+
+test_perf 'test-lib-functions correctly loaded in subshells' '
+ : >a &&
+ test_path_is_file a &&
+ : >b &&
+ test_cmp a b
+'
+
+test_done
diff --git a/t/perf/p0001-rev-list.sh b/t/perf/p0001-rev-list.sh
new file mode 100755
index 0000000..16359d5
--- /dev/null
+++ b/t/perf/p0001-rev-list.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+test_description="Tests history walking performance"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_perf 'rev-list --all' '
+ git rev-list --all >/dev/null
+'
+
+test_perf 'rev-list --all --objects' '
+ git rev-list --all --objects >/dev/null
+'
+
+test_expect_success 'create new unreferenced commit' '
+ commit=$(git commit-tree HEAD^{tree} -p HEAD)
+'
+
+test_perf 'rev-list $commit --not --all' '
+ git rev-list $commit --not --all >/dev/null
+'
+
+test_perf 'rev-list --objects $commit --not --all' '
+ git rev-list --objects $commit --not --all >/dev/null
+'
+
+test_done
diff --git a/t/perf/p0002-read-cache.sh b/t/perf/p0002-read-cache.sh
new file mode 100755
index 0000000..9180ae9
--- /dev/null
+++ b/t/perf/p0002-read-cache.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+test_description="Tests performance of reading the index"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+count=1000
+test_perf "read_cache/discard_cache $count times" "
+ test-read-cache $count
+"
+
+test_done
diff --git a/t/perf/p3404-rebase-interactive.sh b/t/perf/p3404-rebase-interactive.sh
new file mode 100755
index 0000000..88f47de
--- /dev/null
+++ b/t/perf/p3404-rebase-interactive.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='Tests rebase -i performance'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+# This commit merges a sufficiently long topic branch for reasonable
+# performance testing
+branch_merge=ba5312da19c6fdb6c6747d479f58932aae6e900c^{commit}
+export branch_merge
+
+git rev-parse --verify $branch_merge >/dev/null 2>&1 || {
+ skip_all='skipping because $branch_merge was not found'
+ test_done
+}
+
+write_script swap-first-two.sh <<\EOF
+case "$1" in
+*/COMMIT_EDITMSG)
+ mv "$1" "$1".bak &&
+ sed -e '1{h;d}' -e 2G <"$1".bak >"$1"
+ ;;
+esac
+EOF
+
+test_expect_success 'setup' '
+ git config core.editor "\"$PWD"/swap-first-two.sh\" &&
+ git checkout -f $branch_merge^2
+'
+
+test_perf 'rebase -i' '
+ git rebase -i $branch_merge^
+'
+
+test_done
diff --git a/t/perf/p4000-diff-algorithms.sh b/t/perf/p4000-diff-algorithms.sh
new file mode 100755
index 0000000..7e00c9d
--- /dev/null
+++ b/t/perf/p4000-diff-algorithms.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+test_description="Tests diff generation performance"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_perf 'log -3000 (baseline)' '
+ git log -3000 >/dev/null
+'
+
+test_perf 'log --raw -3000 (tree-only)' '
+ git log --raw -3000 >/dev/null
+'
+
+test_perf 'log -p -3000 (Myers)' '
+ git log -p -3000 >/dev/null
+'
+
+test_perf 'log -p -3000 --histogram' '
+ git log -p -3000 --histogram >/dev/null
+'
+
+test_perf 'log -p -3000 --patience' '
+ git log -p -3000 --patience >/dev/null
+'
+
+test_done
diff --git a/t/perf/p4001-diff-no-index.sh b/t/perf/p4001-diff-no-index.sh
new file mode 100755
index 0000000..683be69
--- /dev/null
+++ b/t/perf/p4001-diff-no-index.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+test_description="Test diff --no-index performance"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+file1=$(git ls-files | tail -n 2 | head -1)
+file2=$(git ls-files | tail -n 1 | head -1)
+
+test_expect_success "empty files, so they take no time to diff" "
+ echo >$file1 &&
+ echo >$file2
+"
+
+test_perf "diff --no-index" "
+ git diff --no-index $file1 $file2 >/dev/null
+"
+
+test_done
diff --git a/t/perf/p4211-line-log.sh b/t/perf/p4211-line-log.sh
new file mode 100755
index 0000000..3d074b0
--- /dev/null
+++ b/t/perf/p4211-line-log.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+test_description='Tests log -L performance'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+# Pick a file to log pseudo-randomly. The sort key is the blob hash,
+# so it is stable.
+test_expect_success 'select a file' '
+ git ls-tree HEAD | grep ^100644 |
+ sort -k 3 | head -1 | cut -f 2 >filelist
+'
+
+file=$(cat filelist)
+export file
+
+test_perf 'git rev-list --topo-order (baseline)' '
+ git rev-list --topo-order HEAD >/dev/null
+'
+
+test_perf 'git log --follow (baseline for -M)' '
+ git log --oneline --follow -- "$file" >/dev/null
+'
+
+test_perf 'git log -L' '
+ git log -L 1:"$file" >/dev/null
+'
+
+test_perf 'git log -M -L' '
+ git log -M -L 1:"$file" >/dev/null
+'
+
+test_done
diff --git a/t/perf/p5302-pack-index.sh b/t/perf/p5302-pack-index.sh
new file mode 100755
index 0000000..5ee9211
--- /dev/null
+++ b/t/perf/p5302-pack-index.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+test_description="Tests index-pack performance"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+test_expect_success 'repack' '
+ git repack -ad &&
+ PACK=$(ls .git/objects/pack/*.pack | head -n1) &&
+ test -f "$PACK" &&
+ export PACK
+'
+
+test_perf 'index-pack 0 threads' '
+ GIT_DIR=t1 git index-pack --threads=1 --stdin < $PACK
+'
+
+test_perf 'index-pack 1 thread ' '
+ GIT_DIR=t2 GIT_FORCE_THREADS=1 git index-pack --threads=1 --stdin < $PACK
+'
+
+test_perf 'index-pack 2 threads' '
+ GIT_DIR=t3 git index-pack --threads=2 --stdin < $PACK
+'
+
+test_perf 'index-pack 4 threads' '
+ GIT_DIR=t4 git index-pack --threads=4 --stdin < $PACK
+'
+
+test_perf 'index-pack 8 threads' '
+ GIT_DIR=t5 git index-pack --threads=8 --stdin < $PACK
+'
+
+test_perf 'index-pack default number of threads' '
+ GIT_DIR=t6 git index-pack --stdin < $PACK
+'
+
+test_done
diff --git a/t/perf/p5310-pack-bitmaps.sh b/t/perf/p5310-pack-bitmaps.sh
new file mode 100755
index 0000000..de2a224
--- /dev/null
+++ b/t/perf/p5310-pack-bitmaps.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='Tests pack performance using bitmaps'
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+# note that we do everything through config,
+# since we want to be able to compare bitmap-aware
+# git versus non-bitmap git
+#
+# We intentionally use the deprecated pack.writebitmaps
+# config so that we can test against older versions of git.
+test_expect_success 'setup bitmap config' '
+ git config pack.writebitmaps true &&
+ git config pack.writebitmaphashcache true
+'
+
+test_perf 'repack to disk' '
+ git repack -ad
+'
+
+test_perf 'simulated clone' '
+ git pack-objects --stdout --all </dev/null >/dev/null
+'
+
+test_perf 'simulated fetch' '
+ have=$(git rev-list HEAD~100 -1) &&
+ {
+ echo HEAD &&
+ echo ^$have
+ } | git pack-objects --revs --stdout >/dev/null
+'
+
+test_expect_success 'create partial bitmap state' '
+ # pick a commit to represent the repo tip in the past
+ cutoff=$(git rev-list HEAD~100 -1) &&
+ orig_tip=$(git rev-parse HEAD) &&
+
+ # now kill off all of the refs and pretend we had
+ # just the one tip
+ rm -rf .git/logs .git/refs/* .git/packed-refs &&
+ git update-ref HEAD $cutoff &&
+
+ # and then repack, which will leave us with a nice
+ # big bitmap pack of the "old" history, and all of
+ # the new history will be loose, as if it had been pushed
+ # up incrementally and exploded via unpack-objects
+ git repack -Ad &&
+
+ # and now restore our original tip, as if the pushes
+ # had happened
+ git update-ref HEAD $orig_tip
+'
+
+test_perf 'partial bitmap' '
+ git pack-objects --stdout --all </dev/null >/dev/null
+'
+
+test_done
diff --git a/t/perf/p7000-filter-branch.sh b/t/perf/p7000-filter-branch.sh
new file mode 100755
index 0000000..15ee5d1
--- /dev/null
+++ b/t/perf/p7000-filter-branch.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_description='performance of filter-branch'
+. ./perf-lib.sh
+
+test_perf_default_repo
+test_checkout_worktree
+
+test_expect_success 'mark bases for tests' '
+ git tag -f tip &&
+ git tag -f base HEAD~100
+'
+
+test_perf 'noop filter' '
+ git checkout --detach tip &&
+ git filter-branch -f base..HEAD
+'
+
+test_done
diff --git a/t/perf/p7300-clean.sh b/t/perf/p7300-clean.sh
new file mode 100755
index 0000000..7c1888a
--- /dev/null
+++ b/t/perf/p7300-clean.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_description="Test git-clean performance"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+test_checkout_worktree
+
+test_expect_success 'setup untracked directory with many sub dirs' '
+ rm -rf 500_sub_dirs 100000_sub_dirs clean_test_dir &&
+ mkdir 500_sub_dirs 100000_sub_dirs clean_test_dir &&
+ for i in $(test_seq 1 500)
+ do
+ mkdir 500_sub_dirs/dir$i || return $?
+ done &&
+ for i in $(test_seq 1 200)
+ do
+ cp -r 500_sub_dirs 100000_sub_dirs/dir$i || return $?
+ done
+'
+
+test_perf 'clean many untracked sub dirs, check for nested git' '
+ git clean -n -q -f -d 100000_sub_dirs/
+'
+
+test_perf 'clean many untracked sub dirs, ignore nested git' '
+ git clean -n -q -f -f -d 100000_sub_dirs/
+'
+
+test_perf 'ls-files -o' '
+ git ls-files -o
+'
+
+test_done
diff --git a/t/perf/p7810-grep.sh b/t/perf/p7810-grep.sh
new file mode 100755
index 0000000..9f4ade6
--- /dev/null
+++ b/t/perf/p7810-grep.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+test_description="git-grep performance in various modes"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+test_perf 'grep worktree, cheap regex' '
+ git grep some_nonexistent_string || :
+'
+test_perf 'grep worktree, expensive regex' '
+ git grep "^.* *some_nonexistent_string$" || :
+'
+test_perf 'grep --cached, cheap regex' '
+ git grep --cached some_nonexistent_string || :
+'
+test_perf 'grep --cached, expensive regex' '
+ git grep --cached "^.* *some_nonexistent_string$" || :
+'
+
+test_done
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
new file mode 100644
index 0000000..773f955
--- /dev/null
+++ b/t/perf/perf-lib.sh
@@ -0,0 +1,216 @@
+# Performance testing framework. Each perf script starts much like
+# a normal test script, except it sources this library instead of
+# test-lib.sh. See t/perf/README for documentation.
+#
+# Copyright (c) 2011 Thomas Rast
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see http://www.gnu.org/licenses/ .
+
+# do the --tee work early; it otherwise confuses our careful
+# GIT_BUILD_DIR mangling
+case "$GIT_TEST_TEE_STARTED, $* " in
+done,*)
+ # do not redirect again
+ ;;
+*' --tee '*|*' --va'*)
+ mkdir -p test-results
+ BASE=test-results/$(basename "$0" .sh)
+ (GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1;
+ echo $? > $BASE.exit) | tee $BASE.out
+ test "$(cat $BASE.exit)" = 0
+ exit
+ ;;
+esac
+
+TEST_DIRECTORY=$(pwd)/..
+TEST_OUTPUT_DIRECTORY=$(pwd)
+if test -z "$GIT_TEST_INSTALLED"; then
+ perf_results_prefix=
+else
+ perf_results_prefix=$(printf "%s" "${GIT_TEST_INSTALLED%/bin-wrappers}" | tr -c "[a-zA-Z0-9]" "[_*]")"."
+ # make the tested dir absolute
+ GIT_TEST_INSTALLED=$(cd "$GIT_TEST_INSTALLED" && pwd)
+fi
+
+TEST_NO_CREATE_REPO=t
+TEST_NO_MALLOC_CHECK=t
+
+. ../test-lib.sh
+
+# Variables from test-lib that are normally internal to the tests; we
+# need to export them for test_perf subshells
+export TEST_DIRECTORY TRASH_DIRECTORY GIT_BUILD_DIR GIT_TEST_CMP
+
+perf_results_dir=$TEST_OUTPUT_DIRECTORY/test-results
+mkdir -p "$perf_results_dir"
+rm -f "$perf_results_dir"/$(basename "$0" .sh).subtests
+
+if test -z "$GIT_PERF_REPEAT_COUNT"; then
+ GIT_PERF_REPEAT_COUNT=3
+fi
+die_if_build_dir_not_repo () {
+ if ! ( cd "$TEST_DIRECTORY/.." &&
+ git rev-parse --build-dir >/dev/null 2>&1 ); then
+ error "No $1 defined, and your build directory is not a repo"
+ fi
+}
+
+if test -z "$GIT_PERF_REPO"; then
+ die_if_build_dir_not_repo '$GIT_PERF_REPO'
+ GIT_PERF_REPO=$TEST_DIRECTORY/..
+fi
+if test -z "$GIT_PERF_LARGE_REPO"; then
+ die_if_build_dir_not_repo '$GIT_PERF_LARGE_REPO'
+ GIT_PERF_LARGE_REPO=$TEST_DIRECTORY/..
+fi
+
+test_perf_create_repo_from () {
+ test "$#" = 2 ||
+ error "bug in the test script: not 2 parameters to test-create-repo"
+ repo="$1"
+ source="$2"
+ source_git="$(git -C "$source" rev-parse --git-dir)"
+ objects_dir="$(git -C "$source" rev-parse --git-path objects)"
+ mkdir -p "$repo/.git"
+ (
+ cd "$source" &&
+ { cp -Rl "$objects_dir" "$repo/.git/" 2>/dev/null ||
+ cp -R "$objects_dir" "$repo/.git/"; } &&
+ for stuff in "$source_git"/*; do
+ case "$stuff" in
+ */objects|*/hooks|*/config|*/commondir)
+ ;;
+ *)
+ cp -R "$stuff" "$repo/.git/" || exit 1
+ ;;
+ esac
+ done
+ ) &&
+ (
+ cd "$repo" &&
+ git init -q && {
+ test_have_prereq SYMLINKS ||
+ git config core.symlinks false
+ } &&
+ mv .git/hooks .git/hooks-disabled 2>/dev/null
+ ) || error "failed to copy repository '$source' to '$repo'"
+}
+
+# call at least one of these to establish an appropriately-sized repository
+test_perf_default_repo () {
+ test_perf_create_repo_from "${1:-$TRASH_DIRECTORY}" "$GIT_PERF_REPO"
+}
+test_perf_large_repo () {
+ if test "$GIT_PERF_LARGE_REPO" = "$GIT_BUILD_DIR"; then
+ echo "warning: \$GIT_PERF_LARGE_REPO is \$GIT_BUILD_DIR." >&2
+ echo "warning: This will work, but may not be a sufficiently large repo" >&2
+ echo "warning: for representative measurements." >&2
+ fi
+ test_perf_create_repo_from "${1:-$TRASH_DIRECTORY}" "$GIT_PERF_LARGE_REPO"
+}
+test_checkout_worktree () {
+ git checkout-index -u -a ||
+ error "git checkout-index failed"
+}
+
+# Performance tests should never fail. If they do, stop immediately
+immediate=t
+
+# Perf tests require GNU time
+case "$(uname -s)" in Darwin) GTIME="${GTIME:-gtime}";; esac
+GTIME="${GTIME:-/usr/bin/time}"
+
+test_run_perf_ () {
+ test_cleanup=:
+ test_export_="test_cleanup"
+ export test_cleanup test_export_
+ "$GTIME" -f "%E %U %S" -o test_time.$i "$SHELL" -c '
+. '"$TEST_DIRECTORY"/test-lib-functions.sh'
+test_export () {
+ [ $# != 0 ] || return 0
+ test_export_="$test_export_\\|$1"
+ shift
+ test_export "$@"
+}
+'"$1"'
+ret=$?
+set | sed -n "s'"/'/'\\\\''/g"';s/^\\($test_export_\\)/export '"'&'"'/p" >test_vars
+exit $ret' >&3 2>&4
+ eval_ret=$?
+
+ if test $eval_ret = 0 || test -n "$expecting_failure"
+ then
+ test_eval_ "$test_cleanup"
+ . ./test_vars || error "failed to load updated environment"
+ fi
+ if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"; then
+ echo ""
+ fi
+ return "$eval_ret"
+}
+
+
+test_perf () {
+ test_start_
+ test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
+ test "$#" = 2 ||
+ error "bug in the test script: not 2 or 3 parameters to test-expect-success"
+ export test_prereq
+ if ! test_skip "$@"
+ then
+ base=$(basename "$0" .sh)
+ echo "$test_count" >>"$perf_results_dir"/$base.subtests
+ echo "$1" >"$perf_results_dir"/$base.$test_count.descr
+ if test -z "$verbose"; then
+ printf "%s" "perf $test_count - $1:"
+ else
+ echo "perf $test_count - $1:"
+ fi
+ for i in $(test_seq 1 $GIT_PERF_REPEAT_COUNT); do
+ say >&3 "running: $2"
+ if test_run_perf_ "$2"
+ then
+ if test -z "$verbose"; then
+ printf " %s" "$i"
+ else
+ echo "* timing run $i/$GIT_PERF_REPEAT_COUNT:"
+ fi
+ else
+ test -z "$verbose" && echo
+ test_failure_ "$@"
+ break
+ fi
+ done
+ if test -z "$verbose"; then
+ echo " ok"
+ else
+ test_ok_ "$1"
+ fi
+ base="$perf_results_dir"/"$perf_results_prefix$(basename "$0" .sh)"."$test_count"
+ "$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".times
+ fi
+ test_finish_
+}
+
+# We extend test_done to print timings at the end (./run disables this
+# and does it after running everything)
+test_at_end_hook_ () {
+ if test -z "$GIT_PERF_AGGREGATING_LATER"; then
+ ( cd "$TEST_DIRECTORY"/perf && ./aggregate.perl $(basename "$0") )
+ fi
+}
+
+test_export () {
+ export "$@"
+}
diff --git a/t/perf/run b/t/perf/run
new file mode 100755
index 0000000..cfd7012
--- /dev/null
+++ b/t/perf/run
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+case "$1" in
+ --help)
+ echo "usage: $0 [other_git_tree...] [--] [test_scripts]"
+ exit 0
+ ;;
+esac
+
+die () {
+ echo >&2 "error: $*"
+ exit 1
+}
+
+run_one_dir () {
+ if test $# -eq 0; then
+ set -- p????-*.sh
+ fi
+ echo "=== Running $# tests in ${GIT_TEST_INSTALLED:-this tree} ==="
+ for t in "$@"; do
+ ./$t $GIT_TEST_OPTS
+ done
+}
+
+unpack_git_rev () {
+ rev=$1
+ mkdir -p build/$rev
+ (cd "$(git rev-parse --show-cdup)" && git archive --format=tar $rev) |
+ (cd build/$rev && tar x)
+}
+build_git_rev () {
+ rev=$1
+ cp ../../config.mak build/$rev/config.mak
+ (cd build/$rev && make $GIT_PERF_MAKE_OPTS) ||
+ die "failed to build revision '$mydir'"
+}
+
+run_dirs_helper () {
+ mydir=${1%/}
+ shift
+ while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do
+ shift
+ done
+ if test $# -gt 0 -a "$1" = --; then
+ shift
+ fi
+ if [ ! -d "$mydir" ]; then
+ rev=$(git rev-parse --verify "$mydir" 2>/dev/null) ||
+ die "'$mydir' is neither a directory nor a valid revision"
+ if [ ! -d build/$rev ]; then
+ unpack_git_rev $rev
+ fi
+ build_git_rev $rev
+ mydir=build/$rev
+ fi
+ if test "$mydir" = .; then
+ unset GIT_TEST_INSTALLED
+ else
+ GIT_TEST_INSTALLED="$mydir/bin-wrappers"
+ export GIT_TEST_INSTALLED
+ fi
+ run_one_dir "$@"
+}
+
+run_dirs () {
+ while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do
+ run_dirs_helper "$@"
+ shift
+ done
+}
+
+GIT_PERF_AGGREGATING_LATER=t
+export GIT_PERF_AGGREGATING_LATER
+
+cd "$(dirname $0)"
+. ../../GIT-BUILD-OPTIONS
+
+if test $# = 0 -o "$1" = -- -o -f "$1"; then
+ set -- . "$@"
+fi
+run_dirs "$@"
+./aggregate.perl "$@"
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index f2c7336..60811a3 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -18,54 +18,684 @@ swapping compression and hashing order, the person who is making the
modification *should* take notice and update the test vectors here.
'
-################################################################
-# It appears that people try to run tests without building...
-
-../git >/dev/null
-if test $? != 1
-then
- echo >&2 'You do not seem to have built git yet.'
- exit 1
-fi
-
. ./test-lib.sh
################################################################
# git init has been done in an empty repository.
# make sure it is empty.
-find .git/objects -type f -print >should-be-empty
-test_expect_success \
- '.git/objects should be empty after git init in an empty repo.' \
- 'cmp -s /dev/null should-be-empty'
+test_expect_success '.git/objects should be empty after git init in an empty repo' '
+ find .git/objects -type f -print >should-be-empty &&
+ test_line_count = 0 should-be-empty
+'
# also it should have 2 subdirectories; no fan-out anymore, pack, and info.
# 3 is counting "objects" itself
-find .git/objects -type d -print >full-of-directories
-test_expect_success \
- '.git/objects should have 3 subdirectories.' \
- 'test $(wc -l < full-of-directories) = 3'
+test_expect_success '.git/objects should have 3 subdirectories' '
+ find .git/objects -type d -print >full-of-directories &&
+ test_line_count = 3 full-of-directories
+'
################################################################
# Test harness
test_expect_success 'success is reported like this' '
- :
+ :
'
-test_expect_failure 'pretend we have a known breakage' '
- false
+
+_run_sub_test_lib_test_common () {
+ neg="$1" name="$2" descr="$3" # stdin is the body of the test code
+ shift 3
+ mkdir "$name" &&
+ (
+ # Pretend we're not running under a test harness, whether we
+ # are or not. The test-lib output depends on the setting of
+ # this variable, so we need a stable setting under which to run
+ # the sub-test.
+ sane_unset HARNESS_ACTIVE &&
+ cd "$name" &&
+ cat >"$name.sh" <<-EOF &&
+ #!$SHELL_PATH
+
+ test_description='$descr (run in sub test-lib)
+
+ This is run in a sub test-lib so that we do not get incorrect
+ passing metrics
+ '
+
+ # Point to the t/test-lib.sh, which isn't in ../ as usual
+ . "\$TEST_DIRECTORY"/test-lib.sh
+ EOF
+ cat >>"$name.sh" &&
+ chmod +x "$name.sh" &&
+ export TEST_DIRECTORY &&
+ TEST_OUTPUT_DIRECTORY=$(pwd) &&
+ export TEST_OUTPUT_DIRECTORY &&
+ if test -z "$neg"
+ then
+ ./"$name.sh" "$@" >out 2>err
+ else
+ ! ./"$name.sh" "$@" >out 2>err
+ fi
+ )
+}
+
+run_sub_test_lib_test () {
+ _run_sub_test_lib_test_common '' "$@"
+}
+
+run_sub_test_lib_test_err () {
+ _run_sub_test_lib_test_common '!' "$@"
+}
+
+check_sub_test_lib_test () {
+ name="$1" # stdin is the expected output from the test
+ (
+ cd "$name" &&
+ ! test -s err &&
+ sed -e 's/^> //' -e 's/Z$//' >expect &&
+ test_cmp expect out
+ )
+}
+
+check_sub_test_lib_test_err () {
+ name="$1" # stdin is the expected output from the test
+ # expected error output is in descriptior 3
+ (
+ cd "$name" &&
+ sed -e 's/^> //' -e 's/Z$//' >expect.out &&
+ test_cmp expect.out out &&
+ sed -e 's/^> //' -e 's/Z$//' <&3 >expect.err &&
+ test_cmp expect.err err
+ )
+}
+
+test_expect_success 'pretend we have a fully passing test suite' "
+ run_sub_test_lib_test full-pass '3 passing tests' <<-\\EOF &&
+ for i in 1 2 3
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test full-pass <<-\\EOF
+ > ok 1 - passing test #1
+ > ok 2 - passing test #2
+ > ok 3 - passing test #3
+ > # passed all 3 test(s)
+ > 1..3
+ EOF
+"
+
+test_expect_success 'pretend we have a partially passing test suite' "
+ test_must_fail run_sub_test_lib_test \
+ partial-pass '2/3 tests passing' <<-\\EOF &&
+ test_expect_success 'passing test #1' 'true'
+ test_expect_success 'failing test #2' 'false'
+ test_expect_success 'passing test #3' 'true'
+ test_done
+ EOF
+ check_sub_test_lib_test partial-pass <<-\\EOF
+ > ok 1 - passing test #1
+ > not ok 2 - failing test #2
+ # false
+ > ok 3 - passing test #3
+ > # failed 1 among 3 test(s)
+ > 1..3
+ EOF
+"
+
+test_expect_success 'pretend we have a known breakage' "
+ run_sub_test_lib_test failing-todo 'A failing TODO test' <<-\\EOF &&
+ test_expect_success 'passing test' 'true'
+ test_expect_failure 'pretend we have a known breakage' 'false'
+ test_done
+ EOF
+ check_sub_test_lib_test failing-todo <<-\\EOF
+ > ok 1 - passing test
+ > not ok 2 - pretend we have a known breakage # TODO known breakage
+ > # still have 1 known breakage(s)
+ > # passed all remaining 1 test(s)
+ > 1..2
+ EOF
+"
+
+test_expect_success 'pretend we have fixed a known breakage' "
+ run_sub_test_lib_test passing-todo 'A passing TODO test' <<-\\EOF &&
+ test_expect_failure 'pretend we have fixed a known breakage' 'true'
+ test_done
+ EOF
+ check_sub_test_lib_test passing-todo <<-\\EOF
+ > ok 1 - pretend we have fixed a known breakage # TODO known breakage vanished
+ > # 1 known breakage(s) vanished; please update test(s)
+ > 1..1
+ EOF
+"
+
+test_expect_success 'pretend we have fixed one of two known breakages (run in sub test-lib)' "
+ run_sub_test_lib_test partially-passing-todos \
+ '2 TODO tests, one passing' <<-\\EOF &&
+ test_expect_failure 'pretend we have a known breakage' 'false'
+ test_expect_success 'pretend we have a passing test' 'true'
+ test_expect_failure 'pretend we have fixed another known breakage' 'true'
+ test_done
+ EOF
+ check_sub_test_lib_test partially-passing-todos <<-\\EOF
+ > not ok 1 - pretend we have a known breakage # TODO known breakage
+ > ok 2 - pretend we have a passing test
+ > ok 3 - pretend we have fixed another known breakage # TODO known breakage vanished
+ > # 1 known breakage(s) vanished; please update test(s)
+ > # still have 1 known breakage(s)
+ > # passed all remaining 1 test(s)
+ > 1..3
+ EOF
+"
+
+test_expect_success 'pretend we have a pass, fail, and known breakage' "
+ test_must_fail run_sub_test_lib_test \
+ mixed-results1 'mixed results #1' <<-\\EOF &&
+ test_expect_success 'passing test' 'true'
+ test_expect_success 'failing test' 'false'
+ test_expect_failure 'pretend we have a known breakage' 'false'
+ test_done
+ EOF
+ check_sub_test_lib_test mixed-results1 <<-\\EOF
+ > ok 1 - passing test
+ > not ok 2 - failing test
+ > # false
+ > not ok 3 - pretend we have a known breakage # TODO known breakage
+ > # still have 1 known breakage(s)
+ > # failed 1 among remaining 2 test(s)
+ > 1..3
+ EOF
+"
+
+test_expect_success 'pretend we have a mix of all possible results' "
+ test_must_fail run_sub_test_lib_test \
+ mixed-results2 'mixed results #2' <<-\\EOF &&
+ test_expect_success 'passing test' 'true'
+ test_expect_success 'passing test' 'true'
+ test_expect_success 'passing test' 'true'
+ test_expect_success 'passing test' 'true'
+ test_expect_success 'failing test' 'false'
+ test_expect_success 'failing test' 'false'
+ test_expect_success 'failing test' 'false'
+ test_expect_failure 'pretend we have a known breakage' 'false'
+ test_expect_failure 'pretend we have a known breakage' 'false'
+ test_expect_failure 'pretend we have fixed a known breakage' 'true'
+ test_done
+ EOF
+ check_sub_test_lib_test mixed-results2 <<-\\EOF
+ > ok 1 - passing test
+ > ok 2 - passing test
+ > ok 3 - passing test
+ > ok 4 - passing test
+ > not ok 5 - failing test
+ > # false
+ > not ok 6 - failing test
+ > # false
+ > not ok 7 - failing test
+ > # false
+ > not ok 8 - pretend we have a known breakage # TODO known breakage
+ > not ok 9 - pretend we have a known breakage # TODO known breakage
+ > ok 10 - pretend we have fixed a known breakage # TODO known breakage vanished
+ > # 1 known breakage(s) vanished; please update test(s)
+ > # still have 2 known breakage(s)
+ > # failed 3 among remaining 7 test(s)
+ > 1..10
+ EOF
+"
+
+test_expect_success 'test --verbose' '
+ test_must_fail run_sub_test_lib_test \
+ test-verbose "test verbose" --verbose <<-\EOF &&
+ test_expect_success "passing test" true
+ test_expect_success "test with output" "echo foo"
+ test_expect_success "failing test" false
+ test_done
+ EOF
+ mv test-verbose/out test-verbose/out+ &&
+ grep -v "^Initialized empty" test-verbose/out+ >test-verbose/out &&
+ check_sub_test_lib_test test-verbose <<-\EOF
+ > expecting success: true
+ > ok 1 - passing test
+ > Z
+ > expecting success: echo foo
+ > foo
+ > ok 2 - test with output
+ > Z
+ > expecting success: false
+ > not ok 3 - failing test
+ > # false
+ > Z
+ > # failed 1 among 3 test(s)
+ > 1..3
+ EOF
'
-test_expect_failure 'pretend we have fixed a known breakage' '
- :
+
+test_expect_success 'test --verbose-only' '
+ test_must_fail run_sub_test_lib_test \
+ test-verbose-only-2 "test verbose-only=2" \
+ --verbose-only=2 <<-\EOF &&
+ test_expect_success "passing test" true
+ test_expect_success "test with output" "echo foo"
+ test_expect_success "failing test" false
+ test_done
+ EOF
+ check_sub_test_lib_test test-verbose-only-2 <<-\EOF
+ > ok 1 - passing test
+ > Z
+ > expecting success: echo foo
+ > foo
+ > ok 2 - test with output
+ > Z
+ > not ok 3 - failing test
+ > # false
+ > # failed 1 among 3 test(s)
+ > 1..3
+ EOF
'
+
+test_expect_success 'GIT_SKIP_TESTS' "
+ (
+ GIT_SKIP_TESTS='git.2' && export GIT_SKIP_TESTS &&
+ run_sub_test_lib_test git-skip-tests-basic \
+ 'GIT_SKIP_TESTS' <<-\\EOF &&
+ for i in 1 2 3
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test git-skip-tests-basic <<-\\EOF
+ > ok 1 - passing test #1
+ > ok 2 # skip passing test #2 (GIT_SKIP_TESTS)
+ > ok 3 - passing test #3
+ > # passed all 3 test(s)
+ > 1..3
+ EOF
+ )
+"
+
+test_expect_success 'GIT_SKIP_TESTS several tests' "
+ (
+ GIT_SKIP_TESTS='git.2 git.5' && export GIT_SKIP_TESTS &&
+ run_sub_test_lib_test git-skip-tests-several \
+ 'GIT_SKIP_TESTS several tests' <<-\\EOF &&
+ for i in 1 2 3 4 5 6
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test git-skip-tests-several <<-\\EOF
+ > ok 1 - passing test #1
+ > ok 2 # skip passing test #2 (GIT_SKIP_TESTS)
+ > ok 3 - passing test #3
+ > ok 4 - passing test #4
+ > ok 5 # skip passing test #5 (GIT_SKIP_TESTS)
+ > ok 6 - passing test #6
+ > # passed all 6 test(s)
+ > 1..6
+ EOF
+ )
+"
+
+test_expect_success 'GIT_SKIP_TESTS sh pattern' "
+ (
+ GIT_SKIP_TESTS='git.[2-5]' && export GIT_SKIP_TESTS &&
+ run_sub_test_lib_test git-skip-tests-sh-pattern \
+ 'GIT_SKIP_TESTS sh pattern' <<-\\EOF &&
+ for i in 1 2 3 4 5 6
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test git-skip-tests-sh-pattern <<-\\EOF
+ > ok 1 - passing test #1
+ > ok 2 # skip passing test #2 (GIT_SKIP_TESTS)
+ > ok 3 # skip passing test #3 (GIT_SKIP_TESTS)
+ > ok 4 # skip passing test #4 (GIT_SKIP_TESTS)
+ > ok 5 # skip passing test #5 (GIT_SKIP_TESTS)
+ > ok 6 - passing test #6
+ > # passed all 6 test(s)
+ > 1..6
+ EOF
+ )
+"
+
+test_expect_success '--run basic' "
+ run_sub_test_lib_test run-basic \
+ '--run basic' --run='1 3 5' <<-\\EOF &&
+ for i in 1 2 3 4 5 6
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test run-basic <<-\\EOF
+ > ok 1 - passing test #1
+ > ok 2 # skip passing test #2 (--run)
+ > ok 3 - passing test #3
+ > ok 4 # skip passing test #4 (--run)
+ > ok 5 - passing test #5
+ > ok 6 # skip passing test #6 (--run)
+ > # passed all 6 test(s)
+ > 1..6
+ EOF
+"
+
+test_expect_success '--run with a range' "
+ run_sub_test_lib_test run-range \
+ '--run with a range' --run='1-3' <<-\\EOF &&
+ for i in 1 2 3 4 5 6
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test run-range <<-\\EOF
+ > ok 1 - passing test #1
+ > ok 2 - passing test #2
+ > ok 3 - passing test #3
+ > ok 4 # skip passing test #4 (--run)
+ > ok 5 # skip passing test #5 (--run)
+ > ok 6 # skip passing test #6 (--run)
+ > # passed all 6 test(s)
+ > 1..6
+ EOF
+"
+
+test_expect_success '--run with two ranges' "
+ run_sub_test_lib_test run-two-ranges \
+ '--run with two ranges' --run='1-2 5-6' <<-\\EOF &&
+ for i in 1 2 3 4 5 6
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test run-two-ranges <<-\\EOF
+ > ok 1 - passing test #1
+ > ok 2 - passing test #2
+ > ok 3 # skip passing test #3 (--run)
+ > ok 4 # skip passing test #4 (--run)
+ > ok 5 - passing test #5
+ > ok 6 - passing test #6
+ > # passed all 6 test(s)
+ > 1..6
+ EOF
+"
+
+test_expect_success '--run with a left open range' "
+ run_sub_test_lib_test run-left-open-range \
+ '--run with a left open range' --run='-3' <<-\\EOF &&
+ for i in 1 2 3 4 5 6
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test run-left-open-range <<-\\EOF
+ > ok 1 - passing test #1
+ > ok 2 - passing test #2
+ > ok 3 - passing test #3
+ > ok 4 # skip passing test #4 (--run)
+ > ok 5 # skip passing test #5 (--run)
+ > ok 6 # skip passing test #6 (--run)
+ > # passed all 6 test(s)
+ > 1..6
+ EOF
+"
+
+test_expect_success '--run with a right open range' "
+ run_sub_test_lib_test run-right-open-range \
+ '--run with a right open range' --run='4-' <<-\\EOF &&
+ for i in 1 2 3 4 5 6
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test run-right-open-range <<-\\EOF
+ > ok 1 # skip passing test #1 (--run)
+ > ok 2 # skip passing test #2 (--run)
+ > ok 3 # skip passing test #3 (--run)
+ > ok 4 - passing test #4
+ > ok 5 - passing test #5
+ > ok 6 - passing test #6
+ > # passed all 6 test(s)
+ > 1..6
+ EOF
+"
+
+test_expect_success '--run with basic negation' "
+ run_sub_test_lib_test run-basic-neg \
+ '--run with basic negation' --run='"'!3'"' <<-\\EOF &&
+ for i in 1 2 3 4 5 6
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test run-basic-neg <<-\\EOF
+ > ok 1 - passing test #1
+ > ok 2 - passing test #2
+ > ok 3 # skip passing test #3 (--run)
+ > ok 4 - passing test #4
+ > ok 5 - passing test #5
+ > ok 6 - passing test #6
+ > # passed all 6 test(s)
+ > 1..6
+ EOF
+"
+
+test_expect_success '--run with two negations' "
+ run_sub_test_lib_test run-two-neg \
+ '--run with two negations' --run='"'!3 !6'"' <<-\\EOF &&
+ for i in 1 2 3 4 5 6
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test run-two-neg <<-\\EOF
+ > ok 1 - passing test #1
+ > ok 2 - passing test #2
+ > ok 3 # skip passing test #3 (--run)
+ > ok 4 - passing test #4
+ > ok 5 - passing test #5
+ > ok 6 # skip passing test #6 (--run)
+ > # passed all 6 test(s)
+ > 1..6
+ EOF
+"
+
+test_expect_success '--run a range and negation' "
+ run_sub_test_lib_test run-range-and-neg \
+ '--run a range and negation' --run='"'-4 !2'"' <<-\\EOF &&
+ for i in 1 2 3 4 5 6
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test run-range-and-neg <<-\\EOF
+ > ok 1 - passing test #1
+ > ok 2 # skip passing test #2 (--run)
+ > ok 3 - passing test #3
+ > ok 4 - passing test #4
+ > ok 5 # skip passing test #5 (--run)
+ > ok 6 # skip passing test #6 (--run)
+ > # passed all 6 test(s)
+ > 1..6
+ EOF
+"
+
+test_expect_success '--run range negation' "
+ run_sub_test_lib_test run-range-neg \
+ '--run range negation' --run='"'!1-3'"' <<-\\EOF &&
+ for i in 1 2 3 4 5 6
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test run-range-neg <<-\\EOF
+ > ok 1 # skip passing test #1 (--run)
+ > ok 2 # skip passing test #2 (--run)
+ > ok 3 # skip passing test #3 (--run)
+ > ok 4 - passing test #4
+ > ok 5 - passing test #5
+ > ok 6 - passing test #6
+ > # passed all 6 test(s)
+ > 1..6
+ EOF
+"
+
+test_expect_success '--run include, exclude and include' "
+ run_sub_test_lib_test run-inc-neg-inc \
+ '--run include, exclude and include' \
+ --run='"'1-5 !1-3 2'"' <<-\\EOF &&
+ for i in 1 2 3 4 5 6
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test run-inc-neg-inc <<-\\EOF
+ > ok 1 # skip passing test #1 (--run)
+ > ok 2 - passing test #2
+ > ok 3 # skip passing test #3 (--run)
+ > ok 4 - passing test #4
+ > ok 5 - passing test #5
+ > ok 6 # skip passing test #6 (--run)
+ > # passed all 6 test(s)
+ > 1..6
+ EOF
+"
+
+test_expect_success '--run include, exclude and include, comma separated' "
+ run_sub_test_lib_test run-inc-neg-inc-comma \
+ '--run include, exclude and include, comma separated' \
+ --run=1-5,\!1-3,2 <<-\\EOF &&
+ for i in 1 2 3 4 5 6
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test run-inc-neg-inc-comma <<-\\EOF
+ > ok 1 # skip passing test #1 (--run)
+ > ok 2 - passing test #2
+ > ok 3 # skip passing test #3 (--run)
+ > ok 4 - passing test #4
+ > ok 5 - passing test #5
+ > ok 6 # skip passing test #6 (--run)
+ > # passed all 6 test(s)
+ > 1..6
+ EOF
+"
+
+test_expect_success '--run exclude and include' "
+ run_sub_test_lib_test run-neg-inc \
+ '--run exclude and include' \
+ --run='"'!3- 5'"' <<-\\EOF &&
+ for i in 1 2 3 4 5 6
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test run-neg-inc <<-\\EOF
+ > ok 1 - passing test #1
+ > ok 2 - passing test #2
+ > ok 3 # skip passing test #3 (--run)
+ > ok 4 # skip passing test #4 (--run)
+ > ok 5 - passing test #5
+ > ok 6 # skip passing test #6 (--run)
+ > # passed all 6 test(s)
+ > 1..6
+ EOF
+"
+
+test_expect_success '--run empty selectors' "
+ run_sub_test_lib_test run-empty-sel \
+ '--run empty selectors' \
+ --run='1,,3,,,5' <<-\\EOF &&
+ for i in 1 2 3 4 5 6
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test run-empty-sel <<-\\EOF
+ > ok 1 - passing test #1
+ > ok 2 # skip passing test #2 (--run)
+ > ok 3 - passing test #3
+ > ok 4 # skip passing test #4 (--run)
+ > ok 5 - passing test #5
+ > ok 6 # skip passing test #6 (--run)
+ > # passed all 6 test(s)
+ > 1..6
+ EOF
+"
+
+test_expect_success '--run invalid range start' "
+ run_sub_test_lib_test_err run-inv-range-start \
+ '--run invalid range start' \
+ --run='a-5' <<-\\EOF &&
+ test_expect_success \"passing test #1\" 'true'
+ test_done
+ EOF
+ check_sub_test_lib_test_err run-inv-range-start \
+ <<-\\EOF_OUT 3<<-\\EOF_ERR
+ > FATAL: Unexpected exit with code 1
+ EOF_OUT
+ > error: --run: invalid non-numeric in range start: 'a-5'
+ EOF_ERR
+"
+
+test_expect_success '--run invalid range end' "
+ run_sub_test_lib_test_err run-inv-range-end \
+ '--run invalid range end' \
+ --run='1-z' <<-\\EOF &&
+ test_expect_success \"passing test #1\" 'true'
+ test_done
+ EOF
+ check_sub_test_lib_test_err run-inv-range-end \
+ <<-\\EOF_OUT 3<<-\\EOF_ERR
+ > FATAL: Unexpected exit with code 1
+ EOF_OUT
+ > error: --run: invalid non-numeric in range end: '1-z'
+ EOF_ERR
+"
+
+test_expect_success '--run invalid selector' "
+ run_sub_test_lib_test_err run-inv-selector \
+ '--run invalid selector' \
+ --run='1?' <<-\\EOF &&
+ test_expect_success \"passing test #1\" 'true'
+ test_done
+ EOF
+ check_sub_test_lib_test_err run-inv-selector \
+ <<-\\EOF_OUT 3<<-\\EOF_ERR
+ > FATAL: Unexpected exit with code 1
+ EOF_OUT
+ > error: --run: invalid non-numeric in test selector: '1?'
+ EOF_ERR
+"
+
+
test_set_prereq HAVEIT
haveit=no
test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
- test_have_prereq HAVEIT &&
- haveit=yes
+ test_have_prereq HAVEIT &&
+ haveit=yes
'
donthaveit=yes
test_expect_success DONTHAVEIT 'unmet prerequisite causes test to be skipped' '
- donthaveit=no
+ donthaveit=no
'
if test $haveit$donthaveit != yesyes
then
@@ -73,223 +703,286 @@ then
exit 1
fi
-clean=no
-test_expect_success 'tests clean up after themselves' '
- test_when_finished clean=yes
+test_set_prereq HAVETHIS
+haveit=no
+test_expect_success HAVETHIS,HAVEIT 'test runs if prerequisites are satisfied' '
+ test_have_prereq HAVEIT &&
+ test_have_prereq HAVETHIS &&
+ haveit=yes
+'
+donthaveit=yes
+test_expect_success HAVEIT,DONTHAVEIT 'unmet prerequisites causes test to be skipped' '
+ donthaveit=no
+'
+donthaveiteither=yes
+test_expect_success DONTHAVEIT,HAVEIT 'unmet prerequisites causes test to be skipped' '
+ donthaveiteither=no
+'
+if test $haveit$donthaveit$donthaveiteither != yesyesyes
+then
+ say "bug in test framework: multiple prerequisite tags do not work reliably"
+ exit 1
+fi
+
+test_lazy_prereq LAZY_TRUE true
+havetrue=no
+test_expect_success LAZY_TRUE 'test runs if lazy prereq is satisfied' '
+ havetrue=yes
+'
+donthavetrue=yes
+test_expect_success !LAZY_TRUE 'missing lazy prereqs skip tests' '
+ donthavetrue=no
'
-cleaner=no
-test_expect_code 1 'tests clean up even after a failure' '
- test_when_finished cleaner=yes &&
- (exit 1)
+if test "$havetrue$donthavetrue" != yesyes
+then
+ say 'bug in test framework: lazy prerequisites do not work'
+ exit 1
+fi
+
+test_lazy_prereq LAZY_FALSE false
+nothavefalse=no
+test_expect_success !LAZY_FALSE 'negative lazy prereqs checked' '
+ nothavefalse=yes
+'
+havefalse=yes
+test_expect_success LAZY_FALSE 'missing negative lazy prereqs will skip' '
+ havefalse=no
'
-if test $clean$cleaner != yesyes
+if test "$nothavefalse$havefalse" != yesyes
then
- say "bug in test framework: cleanup commands do not work reliably"
+ say 'bug in test framework: negative lazy prerequisites do not work'
exit 1
fi
-test_expect_code 2 'failure to clean up causes the test to fail' '
- test_when_finished "(exit 2)"
+clean=no
+test_expect_success 'tests clean up after themselves' '
+ test_when_finished clean=yes
'
+if test $clean != yes
+then
+ say "bug in test framework: basic cleanup command does not work reliably"
+ exit 1
+fi
+
+test_expect_success 'tests clean up even on failures' "
+ test_must_fail run_sub_test_lib_test \
+ failing-cleanup 'Failing tests with cleanup commands' <<-\\EOF &&
+ test_expect_success 'tests clean up even after a failure' '
+ touch clean-after-failure &&
+ test_when_finished rm clean-after-failure &&
+ (exit 1)
+ '
+ test_expect_success 'failure to clean up causes the test to fail' '
+ test_when_finished \"(exit 2)\"
+ '
+ test_done
+ EOF
+ check_sub_test_lib_test failing-cleanup <<-\\EOF
+ > not ok 1 - tests clean up even after a failure
+ > # Z
+ > # touch clean-after-failure &&
+ > # test_when_finished rm clean-after-failure &&
+ > # (exit 1)
+ > # Z
+ > not ok 2 - failure to clean up causes the test to fail
+ > # Z
+ > # test_when_finished \"(exit 2)\"
+ > # Z
+ > # failed 2 among 2 test(s)
+ > 1..2
+ EOF
+"
+
################################################################
# Basics of the basics
# updating a new file without --add should fail.
-test_expect_success 'git update-index without --add should fail adding.' '
- test_must_fail git update-index should-be-empty
+test_expect_success 'git update-index without --add should fail adding' '
+ test_must_fail git update-index should-be-empty
'
# and with --add it should succeed, even if it is empty (it used to fail).
-test_expect_success \
- 'git update-index with --add should succeed.' \
- 'git update-index --add should-be-empty'
+test_expect_success 'git update-index with --add should succeed' '
+ git update-index --add should-be-empty
+'
-test_expect_success \
- 'writing tree out with git write-tree' \
- 'tree=$(git write-tree)'
+test_expect_success 'writing tree out with git write-tree' '
+ tree=$(git write-tree)
+'
# we know the shape and contents of the tree and know the object ID for it.
-test_expect_success \
- 'validate object ID of a known tree.' \
- 'test "$tree" = 7bb943559a305bdd6bdee2cef6e5df2413c3d30a'
+test_expect_success 'validate object ID of a known tree' '
+ test "$tree" = 7bb943559a305bdd6bdee2cef6e5df2413c3d30a
+ '
# Removing paths.
-rm -f should-be-empty full-of-directories
-test_expect_success 'git update-index without --remove should fail removing.' '
- test_must_fail git update-index should-be-empty
+test_expect_success 'git update-index without --remove should fail removing' '
+ rm -f should-be-empty full-of-directories &&
+ test_must_fail git update-index should-be-empty
'
-test_expect_success \
- 'git update-index with --remove should be able to remove.' \
- 'git update-index --remove should-be-empty'
+test_expect_success 'git update-index with --remove should be able to remove' '
+ git update-index --remove should-be-empty
+'
# Empty tree can be written with recent write-tree.
-test_expect_success \
- 'git write-tree should be able to write an empty tree.' \
- 'tree=$(git write-tree)'
+test_expect_success 'git write-tree should be able to write an empty tree' '
+ tree=$(git write-tree)
+'
-test_expect_success \
- 'validate object ID of a known tree.' \
- 'test "$tree" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904'
+test_expect_success 'validate object ID of a known tree' '
+ test "$tree" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904
+'
# Various types of objects
-# Some filesystems do not support symblic links; on such systems
-# some expected values are different
-mkdir path2 path3 path3/subp3
-paths='path0 path2/file2 path3/file3 path3/subp3/file3'
-for p in $paths
-do
- echo "hello $p" >$p
-done
-if test_have_prereq SYMLINKS
-then
- for p in $paths
- do
- ln -s "hello $p" ${p}sym
- done
- expectfilter=cat
- expectedtree=087704a96baf1c2d1c869a8b084481e121c88b5b
- expectedptree1=21ae8269cacbe57ae09138dcc3a2887f904d02b3
- expectedptree2=3c5e5399f3a333eddecce7a9b9465b63f65f51e2
-else
- expectfilter='grep -v sym'
- expectedtree=8e18edf7d7edcf4371a3ac6ae5f07c2641db7c46
- expectedptree1=cfb8591b2f65de8b8cc1020cd7d9e67e7793b325
- expectedptree2=ce580448f0148b985a513b693fdf7d802cacb44f
-fi
-test_expect_success \
- 'adding various types of objects with git update-index --add.' \
- 'find path* ! -type d -print | xargs git update-index --add'
+test_expect_success 'adding various types of objects with git update-index --add' '
+ mkdir path2 path3 path3/subp3 &&
+ paths="path0 path2/file2 path3/file3 path3/subp3/file3" &&
+ (
+ for p in $paths
+ do
+ echo "hello $p" >$p || exit 1
+ test_ln_s_add "hello $p" ${p}sym || exit 1
+ done
+ ) &&
+ find path* ! -type d -print | xargs git update-index --add
+'
# Show them and see that matches what we expect.
-test_expect_success \
- 'showing stage with git ls-files --stage' \
- 'git ls-files --stage >current'
-
-$expectfilter >expected <<\EOF
-100644 f87290f8eb2cbbea7857214459a0739927eab154 0 path0
-120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0 path0sym
-100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0 path2/file2
-120000 d8ce161addc5173867a3c3c730924388daedbc38 0 path2/file2sym
-100644 0aa34cae68d0878578ad119c86ca2b5ed5b28376 0 path3/file3
-120000 8599103969b43aff7e430efea79ca4636466794f 0 path3/file3sym
-100644 00fb5908cb97c2564a9783c0c64087333b3b464f 0 path3/subp3/file3
-120000 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c 0 path3/subp3/file3sym
-EOF
-test_expect_success \
- 'validate git ls-files output for a known tree.' \
- 'test_cmp expected current'
-
-test_expect_success \
- 'writing tree out with git write-tree.' \
- 'tree=$(git write-tree)'
-test_expect_success \
- 'validate object ID for a known tree.' \
- 'test "$tree" = "$expectedtree"'
-
-test_expect_success \
- 'showing tree with git ls-tree' \
- 'git ls-tree $tree >current'
-cat >expected <<\EOF
-100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
-120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
-040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe path2
-040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3 path3
-EOF
-test_expect_success SYMLINKS \
- 'git ls-tree output for a known tree.' \
- 'test_cmp expected current'
+test_expect_success 'showing stage with git ls-files --stage' '
+ git ls-files --stage >current
+'
+
+test_expect_success 'validate git ls-files output for a known tree' '
+ cat >expected <<-\EOF &&
+ 100644 f87290f8eb2cbbea7857214459a0739927eab154 0 path0
+ 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0 path0sym
+ 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0 path2/file2
+ 120000 d8ce161addc5173867a3c3c730924388daedbc38 0 path2/file2sym
+ 100644 0aa34cae68d0878578ad119c86ca2b5ed5b28376 0 path3/file3
+ 120000 8599103969b43aff7e430efea79ca4636466794f 0 path3/file3sym
+ 100644 00fb5908cb97c2564a9783c0c64087333b3b464f 0 path3/subp3/file3
+ 120000 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c 0 path3/subp3/file3sym
+ EOF
+ test_cmp expected current
+'
+
+test_expect_success 'writing tree out with git write-tree' '
+ tree=$(git write-tree)
+'
+
+test_expect_success 'validate object ID for a known tree' '
+ test "$tree" = 087704a96baf1c2d1c869a8b084481e121c88b5b
+'
+
+test_expect_success 'showing tree with git ls-tree' '
+ git ls-tree $tree >current
+'
+
+test_expect_success 'git ls-tree output for a known tree' '
+ cat >expected <<-\EOF &&
+ 100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
+ 120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
+ 040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe path2
+ 040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3 path3
+ EOF
+ test_cmp expected current
+'
# This changed in ls-tree pathspec change -- recursive does
# not show tree nodes anymore.
-test_expect_success \
- 'showing tree with git ls-tree -r' \
- 'git ls-tree -r $tree >current'
-$expectfilter >expected <<\EOF
-100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
-120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
-100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 path2/file2
-120000 blob d8ce161addc5173867a3c3c730924388daedbc38 path2/file2sym
-100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376 path3/file3
-120000 blob 8599103969b43aff7e430efea79ca4636466794f path3/file3sym
-100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f path3/subp3/file3
-120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c path3/subp3/file3sym
-EOF
-test_expect_success \
- 'git ls-tree -r output for a known tree.' \
- 'test_cmp expected current'
+test_expect_success 'showing tree with git ls-tree -r' '
+ git ls-tree -r $tree >current
+'
+
+test_expect_success 'git ls-tree -r output for a known tree' '
+ cat >expected <<-\EOF &&
+ 100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
+ 120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
+ 100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 path2/file2
+ 120000 blob d8ce161addc5173867a3c3c730924388daedbc38 path2/file2sym
+ 100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376 path3/file3
+ 120000 blob 8599103969b43aff7e430efea79ca4636466794f path3/file3sym
+ 100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f path3/subp3/file3
+ 120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c path3/subp3/file3sym
+ EOF
+ test_cmp expected current
+'
# But with -r -t we can have both.
-test_expect_success \
- 'showing tree with git ls-tree -r -t' \
- 'git ls-tree -r -t $tree >current'
-cat >expected <<\EOF
-100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
-120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
-040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe path2
-100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 path2/file2
-120000 blob d8ce161addc5173867a3c3c730924388daedbc38 path2/file2sym
-040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3 path3
-100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376 path3/file3
-120000 blob 8599103969b43aff7e430efea79ca4636466794f path3/file3sym
-040000 tree 3c5e5399f3a333eddecce7a9b9465b63f65f51e2 path3/subp3
-100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f path3/subp3/file3
-120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c path3/subp3/file3sym
-EOF
-test_expect_success SYMLINKS \
- 'git ls-tree -r output for a known tree.' \
- 'test_cmp expected current'
-
-test_expect_success \
- 'writing partial tree out with git write-tree --prefix.' \
- 'ptree=$(git write-tree --prefix=path3)'
-test_expect_success \
- 'validate object ID for a known tree.' \
- 'test "$ptree" = "$expectedptree1"'
-
-test_expect_success \
- 'writing partial tree out with git write-tree --prefix.' \
- 'ptree=$(git write-tree --prefix=path3/subp3)'
-test_expect_success \
- 'validate object ID for a known tree.' \
- 'test "$ptree" = "$expectedptree2"'
-
-cat >badobjects <<EOF
-100644 blob 1000000000000000000000000000000000000000 dir/file1
-100644 blob 2000000000000000000000000000000000000000 dir/file2
-100644 blob 3000000000000000000000000000000000000000 dir/file3
-100644 blob 4000000000000000000000000000000000000000 dir/file4
-100644 blob 5000000000000000000000000000000000000000 dir/file5
-EOF
+test_expect_success 'showing tree with git ls-tree -r -t' '
+ git ls-tree -r -t $tree >current
+'
+
+test_expect_success 'git ls-tree -r output for a known tree' '
+ cat >expected <<-\EOF &&
+ 100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
+ 120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
+ 040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe path2
+ 100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 path2/file2
+ 120000 blob d8ce161addc5173867a3c3c730924388daedbc38 path2/file2sym
+ 040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3 path3
+ 100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376 path3/file3
+ 120000 blob 8599103969b43aff7e430efea79ca4636466794f path3/file3sym
+ 040000 tree 3c5e5399f3a333eddecce7a9b9465b63f65f51e2 path3/subp3
+ 100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f path3/subp3/file3
+ 120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c path3/subp3/file3sym
+ EOF
+ test_cmp expected current
+'
+
+test_expect_success 'writing partial tree out with git write-tree --prefix' '
+ ptree=$(git write-tree --prefix=path3)
+'
+
+test_expect_success 'validate object ID for a known tree' '
+ test "$ptree" = 21ae8269cacbe57ae09138dcc3a2887f904d02b3
+'
+
+test_expect_success 'writing partial tree out with git write-tree --prefix' '
+ ptree=$(git write-tree --prefix=path3/subp3)
+'
+
+test_expect_success 'validate object ID for a known tree' '
+ test "$ptree" = 3c5e5399f3a333eddecce7a9b9465b63f65f51e2
+'
-rm .git/index
-test_expect_success \
- 'put invalid objects into the index.' \
- 'git update-index --index-info < badobjects'
+test_expect_success 'put invalid objects into the index' '
+ rm -f .git/index &&
+ cat >badobjects <<-\EOF &&
+ 100644 blob 1000000000000000000000000000000000000000 dir/file1
+ 100644 blob 2000000000000000000000000000000000000000 dir/file2
+ 100644 blob 3000000000000000000000000000000000000000 dir/file3
+ 100644 blob 4000000000000000000000000000000000000000 dir/file4
+ 100644 blob 5000000000000000000000000000000000000000 dir/file5
+ EOF
+ git update-index --index-info <badobjects
+'
-test_expect_success 'writing this tree without --missing-ok.' '
- test_must_fail git write-tree
+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'
+test_expect_success 'writing this tree with --missing-ok' '
+ git write-tree --missing-ok
+'
################################################################
-rm .git/index
-test_expect_success \
- 'git read-tree followed by write-tree should be idempotent.' \
- 'git read-tree $tree &&
- test -f .git/index &&
- newtree=$(git write-tree) &&
- test "$newtree" = "$tree"'
-
-$expectfilter >expected <<\EOF
+test_expect_success 'git read-tree followed by write-tree should be idempotent' '
+ rm -f .git/index &&
+ git read-tree $tree &&
+ test -f .git/index &&
+ newtree=$(git write-tree) &&
+ test "$newtree" = "$tree"
+'
+
+test_expect_success 'validate git diff-files output for a know cache/work tree state' '
+ cat >expected <<\EOF &&
:100644 100644 f87290f8eb2cbbea7857214459a0739927eab154 0000000000000000000000000000000000000000 M path0
:120000 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0000000000000000000000000000000000000000 M path0sym
:100644 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0000000000000000000000000000000000000000 M path2/file2
@@ -299,45 +992,47 @@ $expectfilter >expected <<\EOF
:100644 100644 00fb5908cb97c2564a9783c0c64087333b3b464f 0000000000000000000000000000000000000000 M path3/subp3/file3
:120000 120000 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c 0000000000000000000000000000000000000000 M path3/subp3/file3sym
EOF
-test_expect_success \
- 'validate git diff-files output for a know cache/work tree state.' \
- 'git diff-files >current && test_cmp current expected >/dev/null'
+ git diff-files >current &&
+ test_cmp current expected
+'
-test_expect_success \
- 'git update-index --refresh should succeed.' \
- 'git update-index --refresh'
+test_expect_success 'git update-index --refresh should succeed' '
+ git update-index --refresh
+'
-test_expect_success \
- 'no diff after checkout and git update-index --refresh.' \
- 'git diff-files >current && cmp -s current /dev/null'
+test_expect_success 'no diff after checkout and git update-index --refresh' '
+ git diff-files >current &&
+ cmp -s current /dev/null
+'
################################################################
-P=$expectedtree
-test_expect_success \
- 'git commit-tree records the correct tree in a commit.' \
- 'commit0=$(echo NO | git commit-tree $P) &&
- tree=$(git show --pretty=raw $commit0 |
- sed -n -e "s/^tree //p" -e "/^author /q") &&
- test "z$tree" = "z$P"'
-
-test_expect_success \
- 'git commit-tree records the correct parent in a commit.' \
- 'commit1=$(echo NO | git commit-tree $P -p $commit0) &&
- parent=$(git show --pretty=raw $commit1 |
- sed -n -e "s/^parent //p" -e "/^author /q") &&
- test "z$commit0" = "z$parent"'
-
-test_expect_success \
- 'git commit-tree omits duplicated parent in a commit.' \
- 'commit2=$(echo NO | git commit-tree $P -p $commit0 -p $commit0) &&
- parent=$(git show --pretty=raw $commit2 |
- sed -n -e "s/^parent //p" -e "/^author /q" |
- sort -u) &&
- test "z$commit0" = "z$parent" &&
- numparent=$(git show --pretty=raw $commit2 |
- sed -n -e "s/^parent //p" -e "/^author /q" |
- wc -l) &&
- test $numparent = 1'
+P=087704a96baf1c2d1c869a8b084481e121c88b5b
+
+test_expect_success 'git commit-tree records the correct tree in a commit' '
+ commit0=$(echo NO | git commit-tree $P) &&
+ tree=$(git show --pretty=raw $commit0 |
+ sed -n -e "s/^tree //p" -e "/^author /q") &&
+ test "z$tree" = "z$P"
+'
+
+test_expect_success 'git commit-tree records the correct parent in a commit' '
+ commit1=$(echo NO | git commit-tree $P -p $commit0) &&
+ parent=$(git show --pretty=raw $commit1 |
+ sed -n -e "s/^parent //p" -e "/^author /q") &&
+ test "z$commit0" = "z$parent"
+'
+
+test_expect_success 'git commit-tree omits duplicated parent in a commit' '
+ commit2=$(echo NO | git commit-tree $P -p $commit0 -p $commit0) &&
+ parent=$(git show --pretty=raw $commit2 |
+ sed -n -e "s/^parent //p" -e "/^author /q" |
+ sort -u) &&
+ test "z$commit0" = "z$parent" &&
+ numparent=$(git show --pretty=raw $commit2 |
+ sed -n -e "s/^parent //p" -e "/^author /q" |
+ wc -l) &&
+ test $numparent = 1
+'
test_expect_success 'update-index D/F conflict' '
mv path0 tmp &&
@@ -348,24 +1043,6 @@ test_expect_success 'update-index D/F conflict' '
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
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index 7c0a698..a6fdd5e 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -12,8 +12,15 @@ check_config () {
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) ||
+
+ if test_have_prereq POSIXPERM && test -x "$1/config"
+ then
+ echo "$1/config is executable?"
+ return 1
+ fi
+
+ bare=$(cd "$1" && git config --bool core.bare)
+ worktree=$(cd "$1" && git config core.worktree) ||
worktree=unset
test "$bare" = "$2" && test "$worktree" = "$3" || {
@@ -24,76 +31,108 @@ check_config () {
}
test_expect_success 'plain' '
+ git init plain &&
+ check_config plain/.git false unset
+'
+
+test_expect_success 'plain nested in bare' '
(
- unset GIT_DIR GIT_WORK_TREE
- mkdir plain &&
- cd plain &&
+ git init --bare bare-ancestor.git &&
+ cd bare-ancestor.git &&
+ mkdir plain-nested &&
+ cd plain-nested &&
git init
) &&
- check_config plain/.git false unset
+ check_config bare-ancestor.git/plain-nested/.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 through aliased command, outside any git repo' '
+ (
+ HOME=$(pwd)/alias-config &&
+ export HOME &&
+ mkdir alias-config &&
+ echo "[alias] aliasedinit = init" >alias-config/.gitconfig &&
+
+ GIT_CEILING_DIRECTORIES=$(pwd) &&
+ export GIT_CEILING_DIRECTORIES &&
+
+ mkdir plain-aliased &&
+ cd plain-aliased &&
+ git aliasedinit
+ ) &&
+ check_config plain-aliased/.git false unset
'
-test_expect_success 'plain bare' '
+test_expect_success 'plain nested through aliased command' '
(
- unset GIT_DIR GIT_WORK_TREE GIT_CONFIG
- mkdir plain-bare-1 &&
- cd plain-bare-1 &&
- git --bare init
+ git init plain-ancestor-aliased &&
+ cd plain-ancestor-aliased &&
+ echo "[alias] aliasedinit = init" >>.git/config &&
+ mkdir plain-nested &&
+ cd plain-nested &&
+ git aliasedinit
) &&
+ check_config plain-ancestor-aliased/plain-nested/.git false unset
+'
+
+test_expect_success 'plain nested in bare through aliased command' '
+ (
+ git init --bare bare-ancestor-aliased.git &&
+ cd bare-ancestor-aliased.git &&
+ echo "[alias] aliasedinit = init" >>config &&
+ mkdir plain-nested &&
+ cd plain-nested &&
+ git aliasedinit
+ ) &&
+ check_config bare-ancestor-aliased.git/plain-nested/.git false unset
+'
+
+test_expect_success 'No extra GIT_* on alias scripts' '
+ write_script script <<-\EOF &&
+ env |
+ sed -n \
+ -e "/^GIT_PREFIX=/d" \
+ -e "/^GIT_TEXTDOMAINDIR=/d" \
+ -e "/^GIT_/s/=.*//p" |
+ sort
+ EOF
+ ./script >expected &&
+ git config alias.script \!./script &&
+ ( mkdir sub && cd sub && git script >../actual ) &&
+ test_cmp expected actual
+'
+
+test_expect_success 'plain with GIT_WORK_TREE' '
+ mkdir plain-wt &&
+ test_must_fail env GIT_WORK_TREE="$(pwd)/plain-wt" git init plain-wt
+'
+
+test_expect_success 'plain bare' '
+ git --bare init plain-bare-1 &&
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
+ mkdir plain-bare-2 &&
+ test_must_fail \
+ env GIT_WORK_TREE="$(pwd)/plain-bare-2" \
+ git --bare init plain-bare-2
'
test_expect_success 'GIT_DIR bare' '
-
- (
- unset GIT_CONFIG
- mkdir git-dir-bare.git &&
- GIT_DIR=git-dir-bare.git git init
- ) &&
+ 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
- ) &&
+ git init --bare init-bare.git &&
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
@@ -104,7 +143,6 @@ test_expect_success 'GIT_DIR non-bare' '
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
) &&
@@ -112,73 +150,50 @@ test_expect_success 'GIT_DIR & GIT_WORK_TREE (1)' '
'
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
+ mkdir git-dir-wt-2.git &&
+ test_must_fail env \
+ GIT_WORK_TREE="$(pwd)" \
+ GIT_DIR=git-dir-wt-2.git \
+ git --bare init
'
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 &&
+ test_i18ngrep "Initialized empty" again/out1 &&
+ test_i18ngrep "Reinitialized existing" again/out2 &&
>again/empty &&
- test_cmp again/empty again/err1 &&
- test_cmp again/empty again/err2
+ test_i18ncmp again/empty again/err1 &&
+ test_i18ncmp 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
- ) &&
+ git init --template=../template-source template-custom &&
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
+ git init template-plain &&
+ test_path_is_file template-plain/.git/info/exclude &&
+ git init --template= template-blank &&
+ test_path_is_missing template-blank/.git/info/exclude
'
test_expect_success 'init with init.templatedir set' '
mkdir templatedir-source &&
echo Content >templatedir-source/file &&
+ test_config_global init.templatedir "${HOME}/templatedir-source" &&
(
- 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 &&
+ sane_unset GIT_TEMPLATE_DIR &&
NO_SET_GIT_TEMPLATE_DIR=t &&
export NO_SET_GIT_TEMPLATE_DIR &&
git init
@@ -187,84 +202,55 @@ test_expect_success 'init with init.templatedir set' '
'
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
- ) &&
+ test_config_global core.bare false &&
+ test_config_global core.sharedRepository 0640 &&
+ git init --bare --shared=0666 init-bare-shared-override &&
check_config init-bare-shared-override true unset &&
test x0666 = \
- x`git config -f init-bare-shared-override/config core.sharedRepository`
+ 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_config_global core.sharedRepository 0666 &&
+ git init shared-honor-global &&
test x0666 = \
- x`git config -f shared-honor-global/.git/config core.sharedRepository`
+ 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 allows insanely long --template' '
+ git init --template=$(printf "x%09999dx" 1) test
'
test_expect_success 'init creates a new directory' '
rm -fr newdir &&
- (
- git init newdir &&
- test -d newdir/.git/refs
- )
+ git init newdir &&
+ test_path_is_dir newdir/.git/refs
'
test_expect_success 'init creates a new bare directory' '
rm -fr newdir &&
- (
- git init --bare newdir &&
- test -d newdir/refs
- )
+ git init --bare newdir &&
+ test_path_is_dir newdir/refs
'
test_expect_success 'init recreates a directory' '
rm -fr newdir &&
- (
- mkdir newdir &&
- git init newdir &&
- test -d newdir/.git/refs
- )
+ mkdir newdir &&
+ git init newdir &&
+ test_path_is_dir 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
- )
+ mkdir newdir &&
+ git init --bare newdir &&
+ test_path_is_dir 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_path_is_dir newdir/a/b/c/.git/refs
'
test_expect_success POSIXPERM 'init creates a new deep directory (umask vs. shared)' '
@@ -274,7 +260,7 @@ test_expect_success POSIXPERM 'init creates a new deep directory (umask vs. shar
# the repository itself should follow "shared"
umask 002 &&
git init --bare --shared=0660 newdir/a/b/c &&
- test -d newdir/a/b/c/refs &&
+ test_path_is_dir 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 &&
@@ -284,44 +270,118 @@ test_expect_success POSIXPERM 'init creates a new deep directory (umask vs. shar
test_expect_success 'init notices EEXIST (1)' '
rm -fr newdir &&
- (
- >newdir &&
- test_must_fail git init newdir &&
- test -f newdir
- )
+ >newdir &&
+ test_must_fail git init newdir &&
+ test_path_is_file 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
- )
+ mkdir newdir &&
+ >newdir/a &&
+ test_must_fail git init newdir/a/b &&
+ test_path_is_file newdir/a
'
-test_expect_success POSIXPERM 'init notices EPERM' '
+test_expect_success POSIXPERM,SANITY 'init notices EPERM' '
rm -fr newdir &&
- (
- mkdir newdir &&
- chmod -w newdir &&
- test_must_fail git init newdir/a/b
- )
+ 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_path_is_dir 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_path_is_dir newdir/refs &&
+ test_path_is_missing otherdir/refs
+'
+
+test_expect_success 'init with separate gitdir' '
+ rm -rf newdir &&
+ git init --separate-git-dir realgitdir newdir &&
+ echo "gitdir: $(pwd)/realgitdir" >expected &&
+ test_cmp expected newdir/.git &&
+ test_path_is_dir realgitdir/refs
+'
+
+test_expect_success 're-init on .git file' '
+ ( cd newdir && git init )
+'
+
+test_expect_success 're-init to update git link' '
+ (
+ cd newdir &&
+ git init --separate-git-dir ../surrealgitdir
+ ) &&
+ echo "gitdir: $(pwd)/surrealgitdir" >expected &&
+ test_cmp expected newdir/.git &&
+ test_path_is_dir surrealgitdir/refs &&
+ test_path_is_missing realgitdir/refs
+'
+
+test_expect_success 're-init to move gitdir' '
+ rm -rf newdir realgitdir surrealgitdir &&
+ git init newdir &&
+ (
+ cd newdir &&
+ git init --separate-git-dir ../realgitdir
+ ) &&
+ echo "gitdir: $(pwd)/realgitdir" >expected &&
+ test_cmp expected newdir/.git &&
+ test_path_is_dir realgitdir/refs
+'
+
+test_expect_success SYMLINKS 're-init to move gitdir symlink' '
+ rm -rf newdir realgitdir &&
+ git init newdir &&
+ (
+ cd newdir &&
+ mv .git here &&
+ ln -s here .git &&
+ git init --separate-git-dir ../realgitdir
+ ) &&
+ echo "gitdir: $(pwd)/realgitdir" >expected &&
+ test_cmp expected newdir/.git &&
+ test_cmp expected newdir/here &&
+ test_path_is_dir realgitdir/refs
+'
+
+# Tests for the hidden file attribute on windows
+is_hidden () {
+ # Use the output of `attrib`, ignore the absolute path
+ case "$(attrib "$1")" in *H*?:*) return 0;; esac
+ return 1
+}
+
+test_expect_success MINGW '.git hidden' '
+ rm -rf newdir &&
+ (
+ unset GIT_DIR GIT_WORK_TREE
+ mkdir newdir &&
+ cd newdir &&
+ git init &&
+ is_hidden .git
+ ) &&
+ check_config newdir/.git false unset
+'
+
+test_expect_success MINGW 'bare git dir not hidden' '
+ rm -rf newdir &&
+ (
+ unset GIT_DIR GIT_WORK_TREE GIT_CONFIG
+ mkdir newdir &&
+ cd newdir &&
+ git --bare init
+ ) &&
+ ! is_hidden newdir
'
test_done
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
index cb14425..9670e8c 100755
--- a/t/t0002-gitfile.sh
+++ b/t/t0002-gitfile.sh
@@ -7,7 +7,7 @@ Verify that plumbing commands work when .git is a file
. ./test-lib.sh
objpath() {
- echo "$1" | sed -e 's|\(..\)|\1/|'
+ echo "$1" | sed -e 's|\(..\)|\1/|'
}
objck() {
@@ -19,7 +19,6 @@ objck() {
fi
}
-
test_expect_success 'initial setup' '
REAL="$(pwd)/.real" &&
mv .git "$REAL"
@@ -100,4 +99,63 @@ test_expect_success 'check rev-list' '
test "$SHA" = "$(git rev-list HEAD)"
'
+test_expect_success 'setup_git_dir twice in subdir' '
+ git init sgd &&
+ (
+ cd sgd &&
+ git config alias.lsfi ls-files &&
+ mv .git .realgit &&
+ echo "gitdir: .realgit" >.git &&
+ mkdir subdir &&
+ cd subdir &&
+ >foo &&
+ git add foo &&
+ git lsfi >actual &&
+ echo foo >expected &&
+ test_cmp expected actual
+ )
+'
+
+test_expect_success 'enter_repo non-strict mode' '
+ test_create_repo enter_repo &&
+ (
+ cd enter_repo &&
+ test_tick &&
+ test_commit foo &&
+ mv .git .realgit &&
+ echo "gitdir: .realgit" >.git
+ ) &&
+ git ls-remote enter_repo >actual &&
+ cat >expected <<-\EOF &&
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 HEAD
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 refs/heads/master
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 refs/tags/foo
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'enter_repo linked checkout' '
+ (
+ cd enter_repo &&
+ git worktree add ../foo refs/tags/foo
+ ) &&
+ git ls-remote foo >actual &&
+ cat >expected <<-\EOF &&
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 HEAD
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 refs/heads/master
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 refs/tags/foo
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'enter_repo strict mode' '
+ git ls-remote --upload-pack="git upload-pack --strict" foo/.git >actual &&
+ cat >expected <<-\EOF &&
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 HEAD
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 refs/heads/master
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 refs/tags/foo
+ EOF
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index 53bd7fc..f0fbb42 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -5,20 +5,16 @@ test_description=gitattributes
. ./test-lib.sh
attr_check () {
+ path="$1" expect="$2"
- path="$1"
- expect="$2"
-
- git check-attr test -- "$path" >actual &&
+ git $3 check-attr test -- "$path" >actual 2>err &&
echo "$path: test: $2" >expect &&
- test_cmp expect actual
-
+ test_cmp expect actual &&
+ test_line_count = 0 err
}
-
test_expect_success 'setup' '
-
- mkdir -p a/b/d a/c &&
+ mkdir -p a/b/d a/c b &&
(
echo "[attr]notest !test"
echo "f test=f"
@@ -26,6 +22,7 @@ test_expect_success 'setup' '
echo "onoff test -test"
echo "offon -test test"
echo "no notest"
+ echo "A/e/F test=A/e/F"
) >.gitattributes &&
(
echo "g test=a/g" &&
@@ -35,12 +32,43 @@ test_expect_success 'setup' '
echo "h test=a/b/h" &&
echo "d/* test=a/b/d/*"
echo "d/yes notest"
- ) >a/b/.gitattributes
+ ) >a/b/.gitattributes &&
+ (
+ echo "global test=global"
+ ) >"$HOME"/global-gitattributes &&
+ cat <<-EOF >expect-all
+ 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: notest: set
+ no: test: unspecified
+ a/b/d/no: notest: set
+ a/b/d/no: test: a/b/d/*
+ a/b/d/yes: notest: set
+ a/b/d/yes: test: unspecified
+ EOF
+'
+test_expect_success 'command line checks' '
+ test_must_fail git check-attr &&
+ test_must_fail git check-attr -- &&
+ test_must_fail git check-attr test &&
+ test_must_fail git check-attr test -- &&
+ test_must_fail git check-attr -- f &&
+ echo "f" | test_must_fail git check-attr --stdin &&
+ echo "f" | test_must_fail git check-attr --stdin -- f &&
+ echo "f" | test_must_fail git check-attr --stdin test -- f &&
+ test_must_fail git check-attr "" -- f
'
test_expect_success 'attribute test' '
-
attr_check f f &&
attr_check a/f f &&
attr_check a/c/f f &&
@@ -48,77 +76,225 @@ test_expect_success 'attribute test' '
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/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 matching is case sensitive when core.ignorecase=0' '
+
+ test_must_fail attr_check F f "-c core.ignorecase=0" &&
+ test_must_fail attr_check a/F f "-c core.ignorecase=0" &&
+ test_must_fail attr_check a/c/F f "-c core.ignorecase=0" &&
+ test_must_fail attr_check a/G a/g "-c core.ignorecase=0" &&
+ test_must_fail attr_check a/B/g a/b/g "-c core.ignorecase=0" &&
+ test_must_fail attr_check a/b/G a/b/g "-c core.ignorecase=0" &&
+ test_must_fail attr_check a/b/H a/b/h "-c core.ignorecase=0" &&
+ test_must_fail attr_check a/b/D/g "a/b/d/*" "-c core.ignorecase=0" &&
+ test_must_fail attr_check oNoFf unset "-c core.ignorecase=0" &&
+ test_must_fail attr_check oFfOn set "-c core.ignorecase=0" &&
+ attr_check NO unspecified "-c core.ignorecase=0" &&
+ test_must_fail attr_check a/b/D/NO "a/b/d/*" "-c core.ignorecase=0" &&
+ attr_check a/b/d/YES a/b/d/* "-c core.ignorecase=0" &&
+ test_must_fail attr_check a/E/f "A/e/F" "-c core.ignorecase=0"
'
-test_expect_success 'attribute test: read paths from stdin' '
+test_expect_success 'attribute matching is case insensitive when core.ignorecase=1' '
- 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
+ attr_check F f "-c core.ignorecase=1" &&
+ attr_check a/F f "-c core.ignorecase=1" &&
+ attr_check a/c/F f "-c core.ignorecase=1" &&
+ attr_check a/G a/g "-c core.ignorecase=1" &&
+ attr_check a/B/g a/b/g "-c core.ignorecase=1" &&
+ attr_check a/b/G a/b/g "-c core.ignorecase=1" &&
+ attr_check a/b/H a/b/h "-c core.ignorecase=1" &&
+ attr_check a/b/D/g "a/b/d/*" "-c core.ignorecase=1" &&
+ attr_check oNoFf unset "-c core.ignorecase=1" &&
+ attr_check oFfOn set "-c core.ignorecase=1" &&
+ attr_check NO unspecified "-c core.ignorecase=1" &&
+ attr_check a/b/D/NO "a/b/d/*" "-c core.ignorecase=1" &&
+ attr_check a/b/d/YES unspecified "-c core.ignorecase=1" &&
+ attr_check a/E/f "A/e/F" "-c core.ignorecase=1"
+
+'
- sed -e "s/:.*//" < expect | git check-attr --stdin test > actual &&
+test_expect_success CASE_INSENSITIVE_FS 'additional case insensitivity tests' '
+ test_must_fail attr_check a/B/D/g "a/b/d/*" "-c core.ignorecase=0" &&
+ test_must_fail attr_check A/B/D/NO "a/b/d/*" "-c core.ignorecase=0" &&
+ attr_check A/b/h a/b/h "-c core.ignorecase=1" &&
+ attr_check a/B/D/g "a/b/d/*" "-c core.ignorecase=1" &&
+ attr_check A/B/D/NO "a/b/d/*" "-c core.ignorecase=1"
+'
+
+test_expect_success 'unnormalized paths' '
+ attr_check ./f f &&
+ attr_check ./a/g a/g &&
+ attr_check a/./g a/g &&
+ attr_check a/c/../b/g a/b/g
+'
+
+test_expect_success 'relative paths' '
+ (cd a && attr_check ../f f) &&
+ (cd a && attr_check f f) &&
+ (cd a && attr_check i a/i) &&
+ (cd a && attr_check g a/g) &&
+ (cd a && attr_check b/g a/b/g) &&
+ (cd b && attr_check ../a/f f) &&
+ (cd b && attr_check ../a/g a/g) &&
+ (cd b && attr_check ../a/b/g a/b/g)
+'
+
+test_expect_success 'prefixes are not confused with leading directories' '
+ attr_check a_plus/g unspecified &&
+ cat >expect <<-\EOF &&
+ a/g: test: a/g
+ a_plus/g: test: unspecified
+ EOF
+ git check-attr test a/g a_plus/g >actual &&
test_cmp expect actual
'
-test_expect_success 'root subdir attribute test' '
+test_expect_success 'core.attributesfile' '
+ attr_check global unspecified &&
+ git config core.attributesfile "$HOME/global-gitattributes" &&
+ attr_check global global &&
+ git config core.attributesfile "~/global-gitattributes" &&
+ attr_check global global &&
+ echo "global test=precedence" >>.gitattributes &&
+ attr_check global precedence
+'
+
+test_expect_success 'attribute test: read paths from stdin' '
+ grep -v notest <expect-all >expect &&
+ sed -e "s/:.*//" <expect | git check-attr --stdin test >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'attribute test: --all option' '
+ grep -v unspecified <expect-all | sort >specified-all &&
+ sed -e "s/:.*//" <expect-all | uniq >stdin-all &&
+ git check-attr --stdin --all <stdin-all | sort >actual &&
+ test_cmp specified-all actual
+'
+
+test_expect_success 'attribute test: --cached option' '
+ : >empty &&
+ git check-attr --cached --stdin --all <stdin-all | sort >actual &&
+ test_cmp empty actual &&
+ git add .gitattributes a/.gitattributes a/b/.gitattributes &&
+ git check-attr --cached --stdin --all <stdin-all | sort >actual &&
+ test_cmp specified-all 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' '
+test_expect_success 'negative patterns' '
+ echo "!f test=bar" >.gitattributes &&
+ git check-attr test -- '"'"'!f'"'"' 2>errors &&
+ test_i18ngrep "Negative patterns are ignored" errors
+'
- git clone --bare . bare.git &&
- cd bare.git
+test_expect_success 'patterns starting with exclamation' '
+ echo "\!f test=foo" >.gitattributes &&
+ attr_check "!f" foo
+'
+test_expect_success '"**" test' '
+ echo "**/f foo=bar" >.gitattributes &&
+ cat <<\EOF >expect &&
+f: foo: bar
+a/f: foo: bar
+a/b/f: foo: bar
+a/b/c/f: foo: bar
+EOF
+ git check-attr foo -- "f" >actual 2>err &&
+ git check-attr foo -- "a/f" >>actual 2>>err &&
+ git check-attr foo -- "a/b/f" >>actual 2>>err &&
+ git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
+ test_cmp expect actual &&
+ test_line_count = 0 err
'
-test_expect_success 'bare repository: check that .gitattribute is ignored' '
+test_expect_success '"**" with no slashes test' '
+ echo "a**f foo=bar" >.gitattributes &&
+ git check-attr foo -- "f" >actual &&
+ cat <<\EOF >expect &&
+f: foo: unspecified
+af: foo: bar
+axf: foo: bar
+a/f: foo: unspecified
+a/b/f: foo: unspecified
+a/b/c/f: foo: unspecified
+EOF
+ git check-attr foo -- "f" >actual 2>err &&
+ git check-attr foo -- "af" >>actual 2>err &&
+ git check-attr foo -- "axf" >>actual 2>err &&
+ git check-attr foo -- "a/f" >>actual 2>>err &&
+ git check-attr foo -- "a/b/f" >>actual 2>>err &&
+ git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
+ test_cmp expect actual &&
+ test_line_count = 0 err
+'
+test_expect_success 'using --git-dir and --work-tree' '
+ mkdir unreal real &&
+ git init real &&
+ echo "file test=in-real" >real/.gitattributes &&
(
- 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
+ cd unreal &&
+ attr_check file in-real "--git-dir ../real/.git --work-tree ../real"
+ )
+'
+test_expect_success 'setup bare' '
+ git clone --bare . bare.git
'
-test_expect_success 'bare repository: test info/attributes' '
+test_expect_success 'bare repository: check that .gitattribute is ignored' '
+ (
+ cd bare.git &&
+ (
+ 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: check that --cached honors index' '
(
- 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
+ cd bare.git &&
+ GIT_INDEX_FILE=../.git/index \
+ git check-attr --cached --stdin --all <../stdin-all |
+ sort >actual &&
+ test_cmp ../specified-all actual
+ )
+'
+test_expect_success 'bare repository: test info/attributes' '
+ (
+ cd bare.git &&
+ (
+ 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
index 2342ac5..e3137d6 100755
--- a/t/t0004-unwritable.sh
+++ b/t/t0004-unwritable.sh
@@ -15,54 +15,30 @@ test_expect_success setup '
'
-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,SANITY 'write-tree should notice unwritable repository' '
+ test_when_finished "chmod 775 .git/objects .git/objects/??" &&
+ chmod a-w .git/objects .git/objects/?? &&
+ test_must_fail git write-tree
'
-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,SANITY 'commit should notice unwritable repository' '
+ test_when_finished "chmod 775 .git/objects .git/objects/??" &&
+ chmod a-w .git/objects .git/objects/?? &&
+ test_must_fail git commit -m second
'
-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,SANITY 'update-index should notice unwritable repository' '
+ test_when_finished "chmod 775 .git/objects .git/objects/??" &&
+ echo 6O >file &&
+ chmod a-w .git/objects .git/objects/?? &&
+ test_must_fail git update-index file
'
-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_expect_success POSIXPERM,SANITY 'add should notice unwritable repository' '
+ test_when_finished "chmod 775 .git/objects .git/objects/??" &&
+ echo b >file &&
+ chmod a-w .git/objects .git/objects/?? &&
+ test_must_fail git add file
'
test_done
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
index 93e58c0..e7f27eb 100755
--- a/t/t0005-signals.sh
+++ b/t/t0005-signals.sh
@@ -10,8 +10,8 @@ one
EOF
test_expect_success 'sigchain works' '
- test-sigchain >actual
- case "$?" in
+ { test-sigchain >actual; ret=$?; } &&
+ case "$ret" in
143) true ;; # POSIX w/ SIGTERM=15
271) true ;; # ksh w/ SIGTERM=15
3) true ;; # Windows
@@ -20,4 +20,33 @@ test_expect_success 'sigchain works' '
test_cmp expect actual
'
+test_expect_success !MINGW 'signals are propagated using shell convention' '
+ # we use exec here to avoid any sub-shell interpretation
+ # of the exit code
+ git config alias.sigterm "!exec test-sigchain" &&
+ test_expect_code 143 git sigterm
+'
+
+large_git () {
+ for i in $(test_seq 1 100)
+ do
+ git diff --cached --binary || return
+ done
+}
+
+test_expect_success 'create blob' '
+ test-genrandom foo 16384 >file &&
+ git add file
+'
+
+test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
+ OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ test "$OUT" -eq 141
+'
+
+test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
+ OUT=$( ((trap "" PIPE; large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ test "$OUT" -eq 141
+'
+
test_done
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 1d4d0a5..4c8cf58 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -6,25 +6,52 @@ test_description='test date parsing and printing'
# arbitrary reference time: 2009-08-30 19:20:00
TEST_DATE_NOW=1251660000; export TEST_DATE_NOW
-check_show() {
+check_relative() {
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
+ test-date relative $t >actual &&
+ test_i18ncmp 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_relative 5 '5 seconds ago'
+check_relative 300 '5 minutes ago'
+check_relative 18000 '5 hours ago'
+check_relative 432000 '5 days ago'
+check_relative 1728000 '3 weeks ago'
+check_relative 13000000 '5 months ago'
+check_relative 37500000 '1 year, 2 months ago'
+check_relative 55188000 '1 year, 9 months ago'
+check_relative 630000000 '20 years ago'
+check_relative 31449600 '12 months ago'
+check_relative 62985600 '2 years ago'
+
+check_show () {
+ format=$1
+ time=$2
+ expect=$3
+ test_expect_success $4 "show date ($format:$time)" '
+ echo "$time -> $expect" >expect &&
+ test-date show:$format "$time" >actual &&
+ test_cmp expect actual
+ '
+}
+
+# arbitrary but sensible time for examples
+TIME='1466000000 +0200'
+check_show iso8601 "$TIME" '2016-06-15 16:13:20 +0200'
+check_show iso8601-strict "$TIME" '2016-06-15T16:13:20+02:00'
+check_show rfc2822 "$TIME" 'Wed, 15 Jun 2016 16:13:20 +0200'
+check_show short "$TIME" '2016-06-15'
+check_show default "$TIME" 'Wed Jun 15 16:13:20 2016 +0200'
+check_show raw "$TIME" '1466000000 +0200'
+check_show iso-local "$TIME" '2016-06-15 14:13:20 +0000'
+
+# arbitrary time absurdly far in the future
+FUTURE="5758122296 -0400"
+check_show iso "$FUTURE" "2152-06-19 18:24:56 -0400" LONG_IS_64BIT
+check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" LONG_IS_64BIT
check_parse() {
echo "$1 -> $2" >expect
@@ -39,6 +66,12 @@ 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 -0015' '2008-02-14 20:30:45 -0015'
+check_parse '2008-02-14 20:30:45 -5' '2008-02-14 20:30:45 +0000'
+check_parse '2008-02-14 20:30:45 -5:' '2008-02-14 20:30:45 +0000'
+check_parse '2008-02-14 20:30:45 -05' '2008-02-14 20:30:45 -0500'
+check_parse '2008-02-14 20:30:45 -:30' '2008-02-14 20:30:45 +0000'
+check_parse '2008-02-14 20:30:45 -05:00' '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() {
@@ -75,4 +108,7 @@ 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'
+check_approxidate '2008-12-01' '2008-12-01 19:20:00'
+check_approxidate '2009-12-01' '2009-12-01 19:20:00'
+
test_done
diff --git a/t/t0007-git-var.sh b/t/t0007-git-var.sh
new file mode 100755
index 0000000..5868a87
--- /dev/null
+++ b/t/t0007-git-var.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='basic sanity checks for git var'
+. ./test-lib.sh
+
+test_expect_success 'get GIT_AUTHOR_IDENT' '
+ test_tick &&
+ echo "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
+ git var GIT_AUTHOR_IDENT >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'get GIT_COMMITTER_IDENT' '
+ test_tick &&
+ echo "$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE" >expect &&
+ git var GIT_COMMITTER_IDENT >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success !AUTOIDENT 'requested identites are strict' '
+ (
+ sane_unset GIT_COMMITTER_NAME &&
+ sane_unset GIT_COMMITTER_EMAIL &&
+ test_must_fail git var GIT_COMMITTER_IDENT
+ )
+'
+
+# For git var -l, we check only a representative variable;
+# testing the whole output would make our test too brittle with
+# respect to unrelated changes in the test suite's environment.
+test_expect_success 'git var -l lists variables' '
+ git var -l >actual &&
+ echo "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
+ sed -n s/GIT_AUTHOR_IDENT=//p <actual >actual.author &&
+ test_cmp expect actual.author
+'
+
+test_expect_success 'git var -l lists config' '
+ git var -l >actual &&
+ echo false >expect &&
+ sed -n s/core\\.bare=//p <actual >actual.bare &&
+ test_cmp expect actual.bare
+'
+
+test_expect_success 'listing and asking for variables are exclusive' '
+ test_must_fail git var -l GIT_COMMITTER_IDENT
+'
+
+test_done
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
new file mode 100755
index 0000000..b425f3a
--- /dev/null
+++ b/t/t0008-ignores.sh
@@ -0,0 +1,844 @@
+#!/bin/sh
+
+test_description=check-ignore
+
+. ./test-lib.sh
+
+init_vars () {
+ global_excludes="global-excludes"
+}
+
+enable_global_excludes () {
+ init_vars &&
+ git config core.excludesfile "$global_excludes"
+}
+
+expect_in () {
+ dest="$HOME/expected-$1" text="$2"
+ if test -z "$text"
+ then
+ >"$dest" # avoid newline
+ else
+ echo "$text" >"$dest"
+ fi
+}
+
+expect () {
+ expect_in stdout "$1"
+}
+
+expect_from_stdin () {
+ cat >"$HOME/expected-stdout"
+}
+
+test_stderr () {
+ expected="$1"
+ expect_in stderr "$1" &&
+ test_cmp "$HOME/expected-stderr" "$HOME/stderr"
+}
+
+broken_c_unquote () {
+ "$PERL_PATH" -pe 's/^"//; s/\\//; s/"$//; tr/\n/\0/' "$@"
+}
+
+broken_c_unquote_verbose () {
+ "$PERL_PATH" -pe 's/ "/ /; s/\\//; s/"$//; tr/:\t\n/\0/' "$@"
+}
+
+stderr_contains () {
+ regexp="$1"
+ if grep "$regexp" "$HOME/stderr"
+ then
+ return 0
+ else
+ echo "didn't find /$regexp/ in $HOME/stderr"
+ cat "$HOME/stderr"
+ return 1
+ fi
+}
+
+stderr_empty_on_success () {
+ expect_code="$1"
+ if test $expect_code = 0
+ then
+ test_stderr ""
+ else
+ # If we expect failure then stderr might or might not be empty
+ # due to --quiet - the caller can check its contents
+ return 0
+ fi
+}
+
+test_check_ignore () {
+ args="$1" expect_code="${2:-0}" global_args="$3"
+
+ init_vars &&
+ rm -f "$HOME/stdout" "$HOME/stderr" "$HOME/cmd" &&
+ echo git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $no_index_opt $args \
+ >"$HOME/cmd" &&
+ echo "$expect_code" >"$HOME/expected-exit-code" &&
+ test_expect_code "$expect_code" \
+ git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $no_index_opt $args \
+ >"$HOME/stdout" 2>"$HOME/stderr" &&
+ test_cmp "$HOME/expected-stdout" "$HOME/stdout" &&
+ stderr_empty_on_success "$expect_code"
+}
+
+# Runs the same code with 4 different levels of output verbosity:
+#
+# 1. with -q / --quiet
+# 2. with default verbosity
+# 3. with -v / --verbose
+# 4. with -v / --verbose, *and* -n / --non-matching
+#
+# expecting success each time. Takes advantage of the fact that
+# check-ignore --verbose output is the same as normal output except
+# for the extra first column.
+#
+# A parameter is used to determine if the tests are run with the
+# normal case (using the index), or with the --no-index option.
+#
+# Arguments:
+# - (optional) prereqs for this test, e.g. 'SYMLINKS'
+# - test name
+# - output to expect from the fourth verbosity mode (the output
+# from the other verbosity modes is automatically inferred
+# from this value)
+# - code to run (should invoke test_check_ignore)
+# - index option: --index or --no-index
+test_expect_success_multiple () {
+ prereq=
+ if test $# -eq 5
+ then
+ prereq=$1
+ shift
+ fi
+ if test "$4" = "--index"
+ then
+ no_index_opt=
+ else
+ no_index_opt=$4
+ fi
+ testname="$1" expect_all="$2" code="$3"
+
+ expect_verbose=$( echo "$expect_all" | grep -v '^:: ' )
+ expect=$( echo "$expect_verbose" | sed -e 's/.* //' )
+
+ test_expect_success $prereq "$testname${no_index_opt:+ with $no_index_opt}" '
+ expect "$expect" &&
+ eval "$code"
+ '
+
+ # --quiet is only valid when a single pattern is passed
+ if test $( echo "$expect_all" | wc -l ) = 1
+ then
+ for quiet_opt in '-q' '--quiet'
+ do
+ opts="${no_index_opt:+$no_index_opt }$quiet_opt"
+ test_expect_success $prereq "$testname${opts:+ with $opts}" "
+ expect '' &&
+ $code
+ "
+ done
+ quiet_opt=
+ fi
+
+ for verbose_opt in '-v' '--verbose'
+ do
+ for non_matching_opt in '' '-n' '--non-matching'
+ do
+ if test -n "$non_matching_opt"
+ then
+ my_expect="$expect_all"
+ else
+ my_expect="$expect_verbose"
+ fi
+
+ test_code="
+ expect '$my_expect' &&
+ $code
+ "
+ opts="${no_index_opt:+$no_index_opt }$verbose_opt${non_matching_opt:+ $non_matching_opt}"
+ test_expect_success $prereq "$testname${opts:+ with $opts}" "$test_code"
+ done
+ done
+ verbose_opt=
+ non_matching_opt=
+ no_index_opt=
+}
+
+test_expect_success_multi () {
+ test_expect_success_multiple "$@" "--index"
+}
+
+test_expect_success_no_index_multi () {
+ test_expect_success_multiple "$@" "--no-index"
+}
+
+test_expect_success 'setup' '
+ init_vars &&
+ mkdir -p a/b/ignored-dir a/submodule b &&
+ if test_have_prereq SYMLINKS
+ then
+ ln -s b a/symlink
+ fi &&
+ (
+ cd a/submodule &&
+ git init &&
+ echo a >a &&
+ git add a &&
+ git commit -m"commit in submodule"
+ ) &&
+ git add a/submodule &&
+ cat <<-\EOF >.gitignore &&
+ one
+ ignored-*
+ top-level-dir/
+ EOF
+ for dir in . a
+ do
+ : >$dir/not-ignored &&
+ : >$dir/ignored-and-untracked &&
+ : >$dir/ignored-but-in-index
+ done &&
+ git add -f ignored-but-in-index a/ignored-but-in-index &&
+ cat <<-\EOF >a/.gitignore &&
+ two*
+ *three
+ EOF
+ cat <<-\EOF >a/b/.gitignore &&
+ four
+ five
+ # this comment should affect the line numbers
+ six
+ ignored-dir/
+ # and so should this blank line:
+
+ !on*
+ !two
+ EOF
+ echo "seven" >a/b/ignored-dir/.gitignore &&
+ test -n "$HOME" &&
+ cat <<-\EOF >"$global_excludes" &&
+ globalone
+ !globaltwo
+ globalthree
+ EOF
+ cat <<-\EOF >>.git/info/exclude
+ per-repo
+ EOF
+'
+
+############################################################################
+#
+# test invalid inputs
+
+test_expect_success_multi '. corner-case' ':: .' '
+ test_check_ignore . 1
+'
+
+test_expect_success_multi 'empty command line' '' '
+ test_check_ignore "" 128 &&
+ stderr_contains "fatal: no path specified"
+'
+
+test_expect_success_multi '--stdin with empty STDIN' '' '
+ test_check_ignore "--stdin" 1 </dev/null &&
+ test_stderr ""
+'
+
+test_expect_success '-q with multiple args' '
+ expect "" &&
+ test_check_ignore "-q one two" 128 &&
+ stderr_contains "fatal: --quiet is only valid with a single pathname"
+'
+
+test_expect_success '--quiet with multiple args' '
+ expect "" &&
+ test_check_ignore "--quiet one two" 128 &&
+ stderr_contains "fatal: --quiet is only valid with a single pathname"
+'
+
+for verbose_opt in '-v' '--verbose'
+do
+ for quiet_opt in '-q' '--quiet'
+ do
+ test_expect_success "$quiet_opt $verbose_opt" "
+ expect '' &&
+ test_check_ignore '$quiet_opt $verbose_opt foo' 128 &&
+ stderr_contains 'fatal: cannot have both --quiet and --verbose'
+ "
+ done
+done
+
+test_expect_success '--quiet with multiple args' '
+ expect "" &&
+ test_check_ignore "--quiet one two" 128 &&
+ stderr_contains "fatal: --quiet is only valid with a single pathname"
+'
+
+test_expect_success_multi 'erroneous use of --' '' '
+ test_check_ignore "--" 128 &&
+ stderr_contains "fatal: no path specified"
+'
+
+test_expect_success_multi '--stdin with superfluous arg' '' '
+ test_check_ignore "--stdin foo" 128 &&
+ stderr_contains "fatal: cannot specify pathnames with --stdin"
+'
+
+test_expect_success_multi '--stdin -z with superfluous arg' '' '
+ test_check_ignore "--stdin -z foo" 128 &&
+ stderr_contains "fatal: cannot specify pathnames with --stdin"
+'
+
+test_expect_success_multi '-z without --stdin' '' '
+ test_check_ignore "-z" 128 &&
+ stderr_contains "fatal: -z only makes sense with --stdin"
+'
+
+test_expect_success_multi '-z without --stdin and superfluous arg' '' '
+ test_check_ignore "-z foo" 128 &&
+ stderr_contains "fatal: -z only makes sense with --stdin"
+'
+
+test_expect_success_multi 'needs work tree' '' '
+ (
+ cd .git &&
+ test_check_ignore "foo" 128
+ ) &&
+ stderr_contains "fatal: This operation must be run in a work tree"
+'
+
+############################################################################
+#
+# test standard ignores
+
+# First make sure that the presence of a file in the working tree
+# does not impact results, but that the presence of a file in the
+# index does unless the --no-index option is used.
+
+for subdir in '' 'a/'
+do
+ if test -z "$subdir"
+ then
+ where="at top-level"
+ else
+ where="in subdir $subdir"
+ fi
+
+ test_expect_success_multi "non-existent file $where not ignored" \
+ ":: ${subdir}non-existent" \
+ "test_check_ignore '${subdir}non-existent' 1"
+
+ test_expect_success_no_index_multi "non-existent file $where not ignored" \
+ ":: ${subdir}non-existent" \
+ "test_check_ignore '${subdir}non-existent' 1"
+
+ test_expect_success_multi "non-existent file $where ignored" \
+ ".gitignore:1:one ${subdir}one" \
+ "test_check_ignore '${subdir}one'"
+
+ test_expect_success_no_index_multi "non-existent file $where ignored" \
+ ".gitignore:1:one ${subdir}one" \
+ "test_check_ignore '${subdir}one'"
+
+ test_expect_success_multi "existing untracked file $where not ignored" \
+ ":: ${subdir}not-ignored" \
+ "test_check_ignore '${subdir}not-ignored' 1"
+
+ test_expect_success_no_index_multi "existing untracked file $where not ignored" \
+ ":: ${subdir}not-ignored" \
+ "test_check_ignore '${subdir}not-ignored' 1"
+
+ test_expect_success_multi "existing tracked file $where not ignored" \
+ ":: ${subdir}ignored-but-in-index" \
+ "test_check_ignore '${subdir}ignored-but-in-index' 1"
+
+ test_expect_success_no_index_multi "existing tracked file $where shown as ignored" \
+ ".gitignore:2:ignored-* ${subdir}ignored-but-in-index" \
+ "test_check_ignore '${subdir}ignored-but-in-index'"
+
+ test_expect_success_multi "existing untracked file $where ignored" \
+ ".gitignore:2:ignored-* ${subdir}ignored-and-untracked" \
+ "test_check_ignore '${subdir}ignored-and-untracked'"
+
+ test_expect_success_no_index_multi "existing untracked file $where ignored" \
+ ".gitignore:2:ignored-* ${subdir}ignored-and-untracked" \
+ "test_check_ignore '${subdir}ignored-and-untracked'"
+
+ test_expect_success_multi "mix of file types $where" \
+":: ${subdir}non-existent
+.gitignore:1:one ${subdir}one
+:: ${subdir}not-ignored
+:: ${subdir}ignored-but-in-index
+.gitignore:2:ignored-* ${subdir}ignored-and-untracked" \
+ "test_check_ignore '
+ ${subdir}non-existent
+ ${subdir}one
+ ${subdir}not-ignored
+ ${subdir}ignored-but-in-index
+ ${subdir}ignored-and-untracked'
+ "
+
+ test_expect_success_no_index_multi "mix of file types $where" \
+":: ${subdir}non-existent
+.gitignore:1:one ${subdir}one
+:: ${subdir}not-ignored
+.gitignore:2:ignored-* ${subdir}ignored-but-in-index
+.gitignore:2:ignored-* ${subdir}ignored-and-untracked" \
+ "test_check_ignore '
+ ${subdir}non-existent
+ ${subdir}one
+ ${subdir}not-ignored
+ ${subdir}ignored-but-in-index
+ ${subdir}ignored-and-untracked'
+ "
+done
+
+# Having established the above, from now on we mostly test against
+# files which do not exist in the working tree or index.
+
+test_expect_success 'sub-directory local ignore' '
+ expect "a/3-three" &&
+ test_check_ignore "a/3-three a/three-not-this-one"
+'
+
+test_expect_success 'sub-directory local ignore with --verbose' '
+ expect "a/.gitignore:2:*three a/3-three" &&
+ test_check_ignore "--verbose a/3-three a/three-not-this-one"
+'
+
+test_expect_success 'local ignore inside a sub-directory' '
+ expect "3-three" &&
+ (
+ cd a &&
+ test_check_ignore "3-three three-not-this-one"
+ )
+'
+test_expect_success 'local ignore inside a sub-directory with --verbose' '
+ expect "a/.gitignore:2:*three 3-three" &&
+ (
+ cd a &&
+ test_check_ignore "--verbose 3-three three-not-this-one"
+ )
+'
+
+test_expect_success_multi 'nested include' \
+ 'a/b/.gitignore:8:!on* a/b/one' '
+ test_check_ignore "a/b/one"
+'
+
+############################################################################
+#
+# test ignored sub-directories
+
+test_expect_success_multi 'ignored sub-directory' \
+ 'a/b/.gitignore:5:ignored-dir/ a/b/ignored-dir' '
+ test_check_ignore "a/b/ignored-dir"
+'
+
+test_expect_success 'multiple files inside ignored sub-directory' '
+ expect_from_stdin <<-\EOF &&
+ a/b/ignored-dir/foo
+ a/b/ignored-dir/twoooo
+ a/b/ignored-dir/seven
+ EOF
+ test_check_ignore "a/b/ignored-dir/foo a/b/ignored-dir/twoooo a/b/ignored-dir/seven"
+'
+
+test_expect_success 'multiple files inside ignored sub-directory with -v' '
+ expect_from_stdin <<-\EOF &&
+ a/b/.gitignore:5:ignored-dir/ a/b/ignored-dir/foo
+ a/b/.gitignore:5:ignored-dir/ a/b/ignored-dir/twoooo
+ a/b/.gitignore:5:ignored-dir/ a/b/ignored-dir/seven
+ EOF
+ test_check_ignore "-v a/b/ignored-dir/foo a/b/ignored-dir/twoooo a/b/ignored-dir/seven"
+'
+
+test_expect_success 'cd to ignored sub-directory' '
+ expect_from_stdin <<-\EOF &&
+ foo
+ twoooo
+ ../one
+ seven
+ ../../one
+ EOF
+ (
+ cd a/b/ignored-dir &&
+ test_check_ignore "foo twoooo ../one seven ../../one"
+ )
+'
+
+test_expect_success 'cd to ignored sub-directory with -v' '
+ expect_from_stdin <<-\EOF &&
+ a/b/.gitignore:5:ignored-dir/ foo
+ a/b/.gitignore:5:ignored-dir/ twoooo
+ a/b/.gitignore:8:!on* ../one
+ a/b/.gitignore:5:ignored-dir/ seven
+ .gitignore:1:one ../../one
+ EOF
+ (
+ cd a/b/ignored-dir &&
+ test_check_ignore "-v foo twoooo ../one seven ../../one"
+ )
+'
+
+############################################################################
+#
+# test handling of symlinks
+
+test_expect_success_multi SYMLINKS 'symlink' ':: a/symlink' '
+ test_check_ignore "a/symlink" 1
+'
+
+test_expect_success_multi SYMLINKS 'beyond a symlink' '' '
+ test_check_ignore "a/symlink/foo" 128 &&
+ test_stderr "fatal: pathspec '\''a/symlink/foo'\'' is beyond a symbolic link"
+'
+
+test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' '
+ (
+ cd a &&
+ test_check_ignore "symlink/foo" 128
+ ) &&
+ test_stderr "fatal: pathspec '\''symlink/foo'\'' is beyond a symbolic link"
+'
+
+############################################################################
+#
+# test handling of submodules
+
+test_expect_success_multi 'submodule' '' '
+ test_check_ignore "a/submodule/one" 128 &&
+ test_stderr "fatal: Pathspec '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
+'
+
+test_expect_success_multi 'submodule from subdirectory' '' '
+ (
+ cd a &&
+ test_check_ignore "submodule/one" 128
+ ) &&
+ test_stderr "fatal: Pathspec '\''submodule/one'\'' is in submodule '\''a/submodule'\''"
+'
+
+############################################################################
+#
+# test handling of global ignore files
+
+test_expect_success 'global ignore not yet enabled' '
+ expect_from_stdin <<-\EOF &&
+ .git/info/exclude:7:per-repo per-repo
+ a/.gitignore:2:*three a/globalthree
+ .git/info/exclude:7:per-repo a/per-repo
+ EOF
+ test_check_ignore "-v globalone per-repo a/globalthree a/per-repo not-ignored a/globaltwo"
+'
+
+test_expect_success 'global ignore' '
+ enable_global_excludes &&
+ expect_from_stdin <<-\EOF &&
+ globalone
+ per-repo
+ globalthree
+ a/globalthree
+ a/per-repo
+ globaltwo
+ EOF
+ test_check_ignore "globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo"
+'
+
+test_expect_success 'global ignore with -v' '
+ enable_global_excludes &&
+ expect_from_stdin <<-EOF &&
+ $global_excludes:1:globalone globalone
+ .git/info/exclude:7:per-repo per-repo
+ $global_excludes:3:globalthree globalthree
+ a/.gitignore:2:*three a/globalthree
+ .git/info/exclude:7:per-repo a/per-repo
+ $global_excludes:2:!globaltwo globaltwo
+ EOF
+ test_check_ignore "-v globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo"
+'
+
+############################################################################
+#
+# test --stdin
+
+cat <<-\EOF >stdin
+ one
+ not-ignored
+ a/one
+ a/not-ignored
+ a/b/on
+ a/b/one
+ a/b/one one
+ "a/b/one two"
+ "a/b/one\"three"
+ a/b/not-ignored
+ a/b/two
+ a/b/twooo
+ globaltwo
+ a/globaltwo
+ a/b/globaltwo
+ b/globaltwo
+EOF
+cat <<-\EOF >expected-default
+ one
+ a/one
+ a/b/on
+ a/b/one
+ a/b/one one
+ a/b/one two
+ "a/b/one\"three"
+ a/b/two
+ a/b/twooo
+ globaltwo
+ a/globaltwo
+ a/b/globaltwo
+ b/globaltwo
+EOF
+cat <<-EOF >expected-verbose
+ .gitignore:1:one one
+ .gitignore:1:one a/one
+ a/b/.gitignore:8:!on* a/b/on
+ a/b/.gitignore:8:!on* a/b/one
+ a/b/.gitignore:8:!on* a/b/one one
+ a/b/.gitignore:8:!on* a/b/one two
+ a/b/.gitignore:8:!on* "a/b/one\\"three"
+ a/b/.gitignore:9:!two a/b/two
+ a/.gitignore:1:two* a/b/twooo
+ $global_excludes:2:!globaltwo globaltwo
+ $global_excludes:2:!globaltwo a/globaltwo
+ $global_excludes:2:!globaltwo a/b/globaltwo
+ $global_excludes:2:!globaltwo b/globaltwo
+EOF
+
+broken_c_unquote stdin >stdin0
+
+broken_c_unquote expected-default >expected-default0
+
+broken_c_unquote_verbose expected-verbose >expected-verbose0
+
+test_expect_success '--stdin' '
+ expect_from_stdin <expected-default &&
+ test_check_ignore "--stdin" <stdin
+'
+
+test_expect_success '--stdin -q' '
+ expect "" &&
+ test_check_ignore "-q --stdin" <stdin
+'
+
+test_expect_success '--stdin -v' '
+ expect_from_stdin <expected-verbose &&
+ test_check_ignore "-v --stdin" <stdin
+'
+
+for opts in '--stdin -z' '-z --stdin'
+do
+ test_expect_success "$opts" "
+ expect_from_stdin <expected-default0 &&
+ test_check_ignore '$opts' <stdin0
+ "
+
+ test_expect_success "$opts -q" "
+ expect "" &&
+ test_check_ignore '-q $opts' <stdin0
+ "
+
+ test_expect_success "$opts -v" "
+ expect_from_stdin <expected-verbose0 &&
+ test_check_ignore '-v $opts' <stdin0
+ "
+done
+
+cat <<-\EOF >stdin
+ ../one
+ ../not-ignored
+ one
+ not-ignored
+ b/on
+ b/one
+ b/one one
+ "b/one two"
+ "b/one\"three"
+ b/two
+ b/not-ignored
+ b/twooo
+ ../globaltwo
+ globaltwo
+ b/globaltwo
+ ../b/globaltwo
+ c/not-ignored
+EOF
+# N.B. we deliberately end STDIN with a non-matching pattern in order
+# to test that the exit code indicates that one or more of the
+# provided paths is ignored - in other words, that it represents an
+# aggregation of all the results, not just the final result.
+
+cat <<-EOF >expected-all
+ .gitignore:1:one ../one
+ :: ../not-ignored
+ .gitignore:1:one one
+ :: not-ignored
+ a/b/.gitignore:8:!on* b/on
+ a/b/.gitignore:8:!on* b/one
+ a/b/.gitignore:8:!on* b/one one
+ a/b/.gitignore:8:!on* b/one two
+ a/b/.gitignore:8:!on* "b/one\\"three"
+ a/b/.gitignore:9:!two b/two
+ :: b/not-ignored
+ a/.gitignore:1:two* b/twooo
+ $global_excludes:2:!globaltwo ../globaltwo
+ $global_excludes:2:!globaltwo globaltwo
+ $global_excludes:2:!globaltwo b/globaltwo
+ $global_excludes:2:!globaltwo ../b/globaltwo
+ :: c/not-ignored
+EOF
+grep -v '^:: ' expected-all >expected-verbose
+sed -e 's/.* //' expected-verbose >expected-default
+
+broken_c_unquote stdin >stdin0
+
+broken_c_unquote expected-default >expected-default0
+
+broken_c_unquote_verbose expected-verbose >expected-verbose0
+
+test_expect_success '--stdin from subdirectory' '
+ expect_from_stdin <expected-default &&
+ (
+ cd a &&
+ test_check_ignore "--stdin" <../stdin
+ )
+'
+
+test_expect_success '--stdin from subdirectory with -v' '
+ expect_from_stdin <expected-verbose &&
+ (
+ cd a &&
+ test_check_ignore "--stdin -v" <../stdin
+ )
+'
+
+test_expect_success '--stdin from subdirectory with -v -n' '
+ expect_from_stdin <expected-all &&
+ (
+ cd a &&
+ test_check_ignore "--stdin -v -n" <../stdin
+ )
+'
+
+for opts in '--stdin -z' '-z --stdin'
+do
+ test_expect_success "$opts from subdirectory" '
+ expect_from_stdin <expected-default0 &&
+ (
+ cd a &&
+ test_check_ignore "'"$opts"'" <../stdin0
+ )
+ '
+
+ test_expect_success "$opts from subdirectory with -v" '
+ expect_from_stdin <expected-verbose0 &&
+ (
+ cd a &&
+ test_check_ignore "'"$opts"' -v" <../stdin0
+ )
+ '
+done
+
+test_expect_success PIPE 'streaming support for --stdin' '
+ mkfifo in out &&
+ (git check-ignore -n -v --stdin <in >out &) &&
+
+ # We cannot just "echo >in" because check-ignore would get EOF
+ # after echo exited; instead we open the descriptor in our
+ # shell, and then echo to the fd. We make sure to close it at
+ # the end, so that the subprocess does get EOF and dies
+ # properly.
+ #
+ # Similarly, we must keep "out" open so that check-ignore does
+ # not ever get SIGPIPE trying to write to us. Not only would that
+ # produce incorrect results, but then there would be no writer on the
+ # other end of the pipe, and we would potentially block forever trying
+ # to open it.
+ exec 9>in &&
+ exec 8<out &&
+ test_when_finished "exec 9>&-" &&
+ test_when_finished "exec 8<&-" &&
+ echo >&9 one &&
+ read response <&8 &&
+ echo "$response" | grep "^\.gitignore:1:one one" &&
+ echo >&9 two &&
+ read response <&8 &&
+ echo "$response" | grep "^:: two"
+'
+
+############################################################################
+#
+# test whitespace handling
+
+test_expect_success 'trailing whitespace is ignored' '
+ mkdir whitespace &&
+ >whitespace/trailing &&
+ >whitespace/untracked &&
+ echo "whitespace/trailing " >ignore &&
+ cat >expect <<EOF &&
+whitespace/untracked
+EOF
+ : >err.expect &&
+ git ls-files -o -X ignore whitespace >actual 2>err &&
+ test_cmp expect actual &&
+ test_cmp err.expect err
+'
+
+test_expect_success !MINGW 'quoting allows trailing whitespace' '
+ rm -rf whitespace &&
+ mkdir whitespace &&
+ >"whitespace/trailing " &&
+ >whitespace/untracked &&
+ echo "whitespace/trailing\\ \\ " >ignore &&
+ echo whitespace/untracked >expect &&
+ : >err.expect &&
+ git ls-files -o -X ignore whitespace >actual 2>err &&
+ test_cmp expect actual &&
+ test_cmp err.expect err
+'
+
+test_expect_success !MINGW,!CYGWIN 'correct handling of backslashes' '
+ rm -rf whitespace &&
+ mkdir whitespace &&
+ >"whitespace/trailing 1 " &&
+ >"whitespace/trailing 2 \\\\" &&
+ >"whitespace/trailing 3 \\\\" &&
+ >"whitespace/trailing 4 \\ " &&
+ >"whitespace/trailing 5 \\ \\ " &&
+ >"whitespace/trailing 6 \\a\\" &&
+ >whitespace/untracked &&
+ sed -e "s/Z$//" >ignore <<-\EOF &&
+ whitespace/trailing 1 \ Z
+ whitespace/trailing 2 \\\\Z
+ whitespace/trailing 3 \\\\ Z
+ whitespace/trailing 4 \\\ Z
+ whitespace/trailing 5 \\ \\\ Z
+ whitespace/trailing 6 \\a\\Z
+ EOF
+ echo whitespace/untracked >expect &&
+ >err.expect &&
+ git ls-files -o -X ignore whitespace >actual 2>err &&
+ test_cmp expect actual &&
+ test_cmp err.expect err
+'
+
+test_expect_success 'info/exclude trumps core.excludesfile' '
+ echo >>global-excludes usually-ignored &&
+ echo >>.git/info/exclude "!usually-ignored" &&
+ >usually-ignored &&
+ echo "?? usually-ignored" >expect &&
+
+ git status --porcelain usually-ignored >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0009-prio-queue.sh b/t/t0009-prio-queue.sh
new file mode 100755
index 0000000..94045c3
--- /dev/null
+++ b/t/t0009-prio-queue.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='basic tests for priority queue implementation'
+. ./test-lib.sh
+
+cat >expect <<'EOF'
+1
+2
+3
+4
+5
+5
+6
+7
+8
+9
+10
+EOF
+test_expect_success 'basic ordering' '
+ test-prio-queue 2 6 3 10 9 5 7 4 5 8 1 dump >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+2
+3
+4
+1
+5
+6
+EOF
+test_expect_success 'mixed put and get' '
+ test-prio-queue 6 2 4 get 5 3 get get 1 dump >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+1
+2
+NULL
+1
+2
+NULL
+EOF
+test_expect_success 'notice empty queue' '
+ test-prio-queue 1 2 get get get 1 2 get get get >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0010-racy-git.sh b/t/t0010-racy-git.sh
index e45a9e4..5657c5a 100755
--- a/t/t0010-racy-git.sh
+++ b/t/t0010-racy-git.sh
@@ -14,7 +14,7 @@ do
git update-index --add infocom
echo xyzzy >infocom
- files=`git diff-files -p`
+ files=$(git diff-files -p)
test_expect_success \
"Racy GIT trial #$trial part A" \
'test "" != "$files"'
@@ -23,7 +23,7 @@ do
echo xyzzy >cornerstone
git update-index --add cornerstone
- files=`git diff-files -p`
+ files=$(git diff-files -p)
test_expect_success \
"Racy GIT trial #$trial part B" \
'test "" != "$files"'
diff --git a/t/t0011-hashmap.sh b/t/t0011-hashmap.sh
new file mode 100755
index 0000000..9c217d9
--- /dev/null
+++ b/t/t0011-hashmap.sh
@@ -0,0 +1,253 @@
+#!/bin/sh
+
+test_description='test hashmap and string hash functions'
+. ./test-lib.sh
+
+test_hashmap() {
+ echo "$1" | test-hashmap $3 > actual &&
+ echo "$2" > expect &&
+ test_cmp expect actual
+}
+
+test_expect_success 'hash functions' '
+
+test_hashmap "hash key1" "2215982743 2215982743 116372151 116372151" &&
+test_hashmap "hash key2" "2215982740 2215982740 116372148 116372148" &&
+test_hashmap "hash fooBarFrotz" "1383912807 1383912807 3189766727 3189766727" &&
+test_hashmap "hash foobarfrotz" "2862305959 2862305959 3189766727 3189766727"
+
+'
+
+test_expect_success 'put' '
+
+test_hashmap "put key1 value1
+put key2 value2
+put fooBarFrotz value3
+put foobarfrotz value4
+size" "NULL
+NULL
+NULL
+NULL
+64 4"
+
+'
+
+test_expect_success 'put (case insensitive)' '
+
+test_hashmap "put key1 value1
+put key2 value2
+put fooBarFrotz value3
+size" "NULL
+NULL
+NULL
+64 3" ignorecase
+
+'
+
+test_expect_success 'replace' '
+
+test_hashmap "put key1 value1
+put key1 value2
+put fooBarFrotz value3
+put fooBarFrotz value4
+size" "NULL
+value1
+NULL
+value3
+64 2"
+
+'
+
+test_expect_success 'replace (case insensitive)' '
+
+test_hashmap "put key1 value1
+put Key1 value2
+put fooBarFrotz value3
+put foobarfrotz value4
+size" "NULL
+value1
+NULL
+value3
+64 2" ignorecase
+
+'
+
+test_expect_success 'get' '
+
+test_hashmap "put key1 value1
+put key2 value2
+put fooBarFrotz value3
+put foobarfrotz value4
+get key1
+get key2
+get fooBarFrotz
+get notInMap" "NULL
+NULL
+NULL
+NULL
+value1
+value2
+value3
+NULL"
+
+'
+
+test_expect_success 'get (case insensitive)' '
+
+test_hashmap "put key1 value1
+put key2 value2
+put fooBarFrotz value3
+get Key1
+get keY2
+get foobarfrotz
+get notInMap" "NULL
+NULL
+NULL
+value1
+value2
+value3
+NULL" ignorecase
+
+'
+
+test_expect_success 'add' '
+
+test_hashmap "add key1 value1
+add key1 value2
+add fooBarFrotz value3
+add fooBarFrotz value4
+get key1
+get fooBarFrotz
+get notInMap" "value2
+value1
+value4
+value3
+NULL"
+
+'
+
+test_expect_success 'add (case insensitive)' '
+
+test_hashmap "add key1 value1
+add Key1 value2
+add fooBarFrotz value3
+add foobarfrotz value4
+get key1
+get Foobarfrotz
+get notInMap" "value2
+value1
+value4
+value3
+NULL" ignorecase
+
+'
+
+test_expect_success 'remove' '
+
+test_hashmap "put key1 value1
+put key2 value2
+put fooBarFrotz value3
+remove key1
+remove key2
+remove notInMap
+size" "NULL
+NULL
+NULL
+value1
+value2
+NULL
+64 1"
+
+'
+
+test_expect_success 'remove (case insensitive)' '
+
+test_hashmap "put key1 value1
+put key2 value2
+put fooBarFrotz value3
+remove Key1
+remove keY2
+remove notInMap
+size" "NULL
+NULL
+NULL
+value1
+value2
+NULL
+64 1" ignorecase
+
+'
+
+test_expect_success 'iterate' '
+
+test_hashmap "put key1 value1
+put key2 value2
+put fooBarFrotz value3
+iterate" "NULL
+NULL
+NULL
+key2 value2
+key1 value1
+fooBarFrotz value3"
+
+'
+
+test_expect_success 'iterate (case insensitive)' '
+
+test_hashmap "put key1 value1
+put key2 value2
+put fooBarFrotz value3
+iterate" "NULL
+NULL
+NULL
+fooBarFrotz value3
+key2 value2
+key1 value1" ignorecase
+
+'
+
+test_expect_success 'grow / shrink' '
+
+ rm -f in &&
+ rm -f expect &&
+ for n in $(test_seq 51)
+ do
+ echo put key$n value$n >> in &&
+ echo NULL >> expect
+ done &&
+ echo size >> in &&
+ echo 64 51 >> expect &&
+ echo put key52 value52 >> in &&
+ echo NULL >> expect &&
+ echo size >> in &&
+ echo 256 52 >> expect &&
+ for n in $(test_seq 12)
+ do
+ echo remove key$n >> in &&
+ echo value$n >> expect
+ done &&
+ echo size >> in &&
+ echo 256 40 >> expect &&
+ echo remove key40 >> in &&
+ echo value40 >> expect &&
+ echo size >> in &&
+ echo 64 39 >> expect &&
+ cat in | test-hashmap > out &&
+ test_cmp expect out
+
+'
+
+test_expect_success 'string interning' '
+
+test_hashmap "intern value1
+intern Value1
+intern value2
+intern value2
+" "value1
+Value1
+value2
+value2"
+
+'
+
+test_done
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index 234a94f..f94120a 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -8,6 +8,13 @@ has_cr() {
tr '\015' Q <"$1" | grep Q >/dev/null
}
+# add or remove CRs to disk file in-place
+# usage: munge_cr <append|remove> <file>
+munge_cr () {
+ "${1}_cr" <"$2" >tmp &&
+ mv tmp "$2"
+}
+
test_expect_success setup '
git config core.autocrlf false &&
@@ -20,17 +27,15 @@ test_expect_success setup '
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` &&
+ 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.
+ patched=$(git hash-object --stdin <one) &&
+ git read-tree --reset -u HEAD
'
test_expect_success 'safecrlf: autocrlf=input, all CRLF' '
@@ -81,6 +86,14 @@ test_expect_success 'safecrlf: print warning only once' '
test $(git add doublewarn 2>&1 | grep "CRLF will be replaced by LF" | wc -l) = 1
'
+
+test_expect_success 'safecrlf: git diff demotes safecrlf=true to warn' '
+ git config core.autocrlf input &&
+ git config core.safecrlf true &&
+ git diff HEAD
+'
+
+
test_expect_success 'switch off autocrlf, safecrlf, reset HEAD' '
git config core.autocrlf false &&
git config core.safecrlf false &&
@@ -92,22 +105,11 @@ 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
- }
+ munge_cr append one &&
+ munge_cr append dir/two &&
+ git update-index -- one dir/two &&
+ differs=$(git diff-index --cached HEAD) &&
+ verbose test -z "$differs"
'
@@ -116,22 +118,11 @@ 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
- }
+ munge_cr append one &&
+ munge_cr append dir/two &&
+ git update-index -- one dir/two &&
+ differs=$(git diff-index --cached HEAD) &&
+ verbose test -z "$differs"
'
@@ -140,23 +131,13 @@ 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
- }
+ munge_cr remove one &&
+ munge_cr remove dir/two &&
+ git update-index -- one dir/two &&
+ test "$one" = $(git hash-object --stdin <one) &&
+ test "$two" = $(git hash-object --stdin <dir/two) &&
+ differs=$(git diff-index --cached HEAD) &&
+ verbose test -z "$differs"
'
test_expect_success 'checkout with autocrlf=input' '
@@ -164,25 +145,13 @@ 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_must_fail has_cr one &&
+ test_must_fail has_cr two &&
+ git update-index -- one dir/two &&
+ test "$one" = $(git hash-object --stdin <one) &&
+ test "$two" = $(git hash-object --stdin <dir/two) &&
+ differs=$(git diff-index --cached HEAD) &&
+ verbose test -z "$differs"
'
test_expect_success 'apply patch (autocrlf=input)' '
@@ -192,10 +161,7 @@ test_expect_success 'apply patch (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
- }
+ verbose test "$patched" = "$(git hash-object --stdin <one)"
'
test_expect_success 'apply patch --cached (autocrlf=input)' '
@@ -205,10 +171,7 @@ test_expect_success 'apply patch --cached (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
- }
+ verbose test "$patched" = $(git rev-parse :one)
'
test_expect_success 'apply patch --index (autocrlf=input)' '
@@ -218,11 +181,8 @@ test_expect_success 'apply patch --index (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
- }
+ verbose test "$patched" = $(git rev-parse :one) &&
+ verbose test "$patched" = $(git hash-object --stdin <one)
'
test_expect_success 'apply patch (autocrlf=true)' '
@@ -232,10 +192,7 @@ test_expect_success 'apply patch (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
- }
+ verbose test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
'
test_expect_success 'apply patch --cached (autocrlf=true)' '
@@ -245,10 +202,7 @@ test_expect_success 'apply patch --cached (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
- }
+ verbose test "$patched" = $(git rev-parse :one)
'
test_expect_success 'apply patch --index (autocrlf=true)' '
@@ -258,11 +212,8 @@ test_expect_success 'apply patch --index (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
- }
+ verbose test "$patched" = $(git rev-parse :one) &&
+ verbose test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
'
test_expect_success '.gitattributes says two is binary' '
@@ -272,29 +223,9 @@ test_expect_success '.gitattributes says two is binary' '
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_must_fail has_cr dir/two &&
+ verbose has_cr one &&
+ test_must_fail has_cr three
'
test_expect_success '.gitattributes says two is input' '
@@ -303,13 +234,7 @@ test_expect_success '.gitattributes says two is input' '
echo "two crlf=input" >.gitattributes &&
git read-tree --reset -u HEAD &&
- if has_cr dir/two
- then
- echo "Huh?"
- false
- else
- : happy
- fi
+ test_must_fail has_cr dir/two
'
test_expect_success '.gitattributes says two and three are text' '
@@ -318,21 +243,8 @@ test_expect_success '.gitattributes says two and three are text' '
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
+ verbose has_cr dir/two &&
+ verbose has_cr three
'
test_expect_success 'in-tree .gitattributes (1)' '
@@ -344,17 +256,8 @@ test_expect_success 'in-tree .gitattributes (1)' '
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_must_fail has_cr one &&
+ verbose has_cr three
'
test_expect_success 'in-tree .gitattributes (2)' '
@@ -363,17 +266,8 @@ test_expect_success 'in-tree .gitattributes (2)' '
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_must_fail has_cr one &&
+ verbose has_cr three
'
test_expect_success 'in-tree .gitattributes (3)' '
@@ -383,17 +277,8 @@ test_expect_success 'in-tree .gitattributes (3)' '
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_must_fail has_cr one &&
+ verbose has_cr three
'
test_expect_success 'in-tree .gitattributes (4)' '
@@ -403,17 +288,8 @@ test_expect_success 'in-tree .gitattributes (4)' '
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_must_fail has_cr one &&
+ verbose has_cr three
'
test_expect_success 'checkout with existing .gitattributes' '
@@ -439,7 +315,7 @@ test_expect_success 'checkout when deleting .gitattributes' '
git rm .gitattributes &&
echo "contentsQ" | q_to_cr > .file2 &&
git add .file2 &&
- git commit -m third
+ git commit -m third &&
git checkout master~1 &&
git checkout master &&
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index 828e35b..7bac2bc 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -66,31 +66,206 @@ test_expect_success expanded_in_repo '
echo "\$Id:NoSpaceAtEitherEnd\$"
echo "\$Id: NoTerminatingSymbol"
echo "\$Id: Foreign Commit With Spaces \$"
- echo "\$Id: NoTerminatingSymbolAtEOF"
- } > expanded-keywords &&
+ } >expanded-keywords.0 &&
+
+ {
+ cat expanded-keywords.0 &&
+ printf "\$Id: NoTerminatingSymbolAtEOF"
+ } >expanded-keywords &&
+ cat expanded-keywords >expanded-keywords-crlf &&
+ git add expanded-keywords expanded-keywords-crlf &&
+ git commit -m "File with keywords expanded" &&
+ id=$(git rev-parse --verify :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: $id \$"
+ echo "\$Id: $id \$"
+ echo "\$Id: $id \$"
+ echo "\$Id: $id \$"
+ echo "\$Id: $id \$"
+ echo "\$Id: $id \$"
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" &&
+ } >expected-output.0 &&
+ {
+ cat expected-output.0 &&
+ printf "\$Id: NoTerminatingSymbolAtEOF"
+ } >expected-output &&
+ {
+ append_cr <expected-output.0 &&
+ printf "\$Id: NoTerminatingSymbolAtEOF"
+ } >expected-output-crlf &&
+ {
+ echo "expanded-keywords ident"
+ echo "expanded-keywords-crlf ident text eol=crlf"
+ } >>.gitattributes &&
- echo "expanded-keywords ident" >> .gitattributes &&
+ rm -f expanded-keywords expanded-keywords-crlf &&
- rm -f expanded-keywords &&
git checkout -- expanded-keywords &&
- cat expanded-keywords &&
- cmp expanded-keywords expected-output
+ test_cmp expanded-keywords expected-output &&
+
+ git checkout -- expanded-keywords-crlf &&
+ test_cmp expanded-keywords-crlf expected-output-crlf
+'
+
+# The use of %f in a filter definition is expanded to the path to
+# the filename being smudged or cleaned. It must be shell escaped.
+# First, set up some interesting file names and pet them in
+# .gitattributes.
+test_expect_success 'filter shell-escaped filenames' '
+ cat >argc.sh <<-EOF &&
+ #!$SHELL_PATH
+ cat >/dev/null
+ echo argc: \$# "\$@"
+ EOF
+ normal=name-no-magic &&
+ special="name with '\''sq'\'' and \$x" &&
+ echo some test text >"$normal" &&
+ echo some test text >"$special" &&
+ git add "$normal" "$special" &&
+ git commit -q -m "add files" &&
+ echo "name* filter=argc" >.gitattributes &&
+
+ # delete the files and check them out again, using a smudge filter
+ # that will count the args and echo the command-line back to us
+ git config filter.argc.smudge "sh ./argc.sh %f" &&
+ rm "$normal" "$special" &&
+ git checkout -- "$normal" "$special" &&
+
+ # make sure argc.sh counted the right number of args
+ echo "argc: 1 $normal" >expect &&
+ test_cmp expect "$normal" &&
+ echo "argc: 1 $special" >expect &&
+ test_cmp expect "$special" &&
+
+ # do the same thing, but with more args in the filter expression
+ git config filter.argc.smudge "sh ./argc.sh %f --my-extra-arg" &&
+ rm "$normal" "$special" &&
+ git checkout -- "$normal" "$special" &&
+
+ # make sure argc.sh counted the right number of args
+ echo "argc: 2 $normal --my-extra-arg" >expect &&
+ test_cmp expect "$normal" &&
+ echo "argc: 2 $special --my-extra-arg" >expect &&
+ test_cmp expect "$special" &&
+ :
+'
+
+test_expect_success 'required filter should filter data' '
+ git config filter.required.smudge ./rot13.sh &&
+ git config filter.required.clean ./rot13.sh &&
+ git config filter.required.required true &&
+
+ echo "*.r filter=required" >.gitattributes &&
+
+ cat test.o >test.r &&
+ git add test.r &&
+
+ rm -f test.r &&
+ git checkout -- test.r &&
+ cmp test.o test.r &&
+
+ ./rot13.sh <test.o >expected &&
+ git cat-file blob :test.r >actual &&
+ cmp expected actual
+'
+
+test_expect_success 'required filter smudge failure' '
+ git config filter.failsmudge.smudge false &&
+ git config filter.failsmudge.clean cat &&
+ git config filter.failsmudge.required true &&
+
+ echo "*.fs filter=failsmudge" >.gitattributes &&
+
+ echo test >test.fs &&
+ git add test.fs &&
+ rm -f test.fs &&
+ test_must_fail git checkout -- test.fs
+'
+
+test_expect_success 'required filter clean failure' '
+ git config filter.failclean.smudge cat &&
+ git config filter.failclean.clean false &&
+ git config filter.failclean.required true &&
+
+ echo "*.fc filter=failclean" >.gitattributes &&
+
+ echo test >test.fc &&
+ test_must_fail git add test.fc
+'
+
+test_expect_success 'filtering large input to small output should use little memory' '
+ git config filter.devnull.clean "cat >/dev/null" &&
+ git config filter.devnull.required true &&
+ for i in $(test_seq 1 30); do printf "%1048576d" 1; done >30MB &&
+ echo "30MB filter=devnull" >.gitattributes &&
+ GIT_MMAP_LIMIT=1m GIT_ALLOC_LIMIT=1m git add 30MB
+'
+
+test_expect_success 'filter that does not read is fine' '
+ test-genrandom foo $((128 * 1024 + 1)) >big &&
+ echo "big filter=epipe" >.gitattributes &&
+ git config filter.epipe.clean "echo xyzzy" &&
+ git add big &&
+ git cat-file blob :big >actual &&
+ echo xyzzy >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success EXPENSIVE 'filter large file' '
+ git config filter.largefile.smudge cat &&
+ git config filter.largefile.clean cat &&
+ for i in $(test_seq 1 2048); do printf "%1048576d" 1; done >2GB &&
+ echo "2GB filter=largefile" >.gitattributes &&
+ git add 2GB 2>err &&
+ ! test -s err &&
+ rm -f 2GB &&
+ git checkout -- 2GB 2>err &&
+ ! test -s err
+'
+
+test_expect_success "filter: clean empty file" '
+ git config filter.in-repo-header.clean "echo cleaned && cat" &&
+ git config filter.in-repo-header.smudge "sed 1d" &&
+
+ echo "empty-in-worktree filter=in-repo-header" >>.gitattributes &&
+ >empty-in-worktree &&
+
+ echo cleaned >expected &&
+ git add empty-in-worktree &&
+ git show :empty-in-worktree >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success "filter: smudge empty file" '
+ git config filter.empty-in-repo.clean "cat >/dev/null" &&
+ git config filter.empty-in-repo.smudge "echo smudged && cat" &&
+
+ echo "empty-in-repo filter=empty-in-repo" >>.gitattributes &&
+ echo dead data walking >empty-in-repo &&
+ git add empty-in-repo &&
+
+ echo smudged >expected &&
+ git checkout-index --prefix=filtered- empty-in-repo &&
+ test_cmp expected filtered-empty-in-repo
+'
+
+test_expect_success 'disable filter with empty override' '
+ test_config_global filter.disable.smudge false &&
+ test_config_global filter.disable.clean false &&
+ test_config filter.disable.smudge false &&
+ test_config filter.disable.clean false &&
+
+ echo "*.disable filter=disable" >.gitattributes &&
+
+ echo test >test.disable &&
+ git -c filter.disable.clean= add test.disable 2>err &&
+ test_must_be_empty err &&
+ rm -f test.disable &&
+ git -c filter.disable.smudge= checkout -- test.disable 2>err &&
+ test_must_be_empty err
'
test_done
diff --git a/t/t0023-crlf-am.sh b/t/t0023-crlf-am.sh
index aaed725..f9bbb91 100755
--- a/t/t0023-crlf-am.sh
+++ b/t/t0023-crlf-am.sh
@@ -11,7 +11,7 @@ Date: Thu, 23 Aug 2007 13:00:00 +0200
Subject: test1
---
- foo | 1 +
+ foo | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 foo
diff --git a/t/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh
index c7d0324..4e9fa3c 100755
--- a/t/t0024-crlf-archive.sh
+++ b/t/t0024-crlf-archive.sh
@@ -3,11 +3,10 @@
test_description='respect crlf in git archive'
. ./test-lib.sh
-UNZIP=${UNZIP:-unzip}
test_expect_success setup '
- git config core.autocrlf true
+ git config core.autocrlf true &&
printf "CRLF line ending\r\nAnd another\r\n" > sample &&
git add sample &&
@@ -20,24 +19,17 @@ test_expect_success setup '
test_expect_success 'tar archive' '
git archive --format=tar HEAD |
- ( mkdir untarred && cd untarred && "$TAR" -xf - )
+ ( 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 ) &&
+ ( mkdir unzipped && cd unzipped && "$GIT_UNZIP" ../test.zip ) &&
test_cmp sample unzipped/sample
diff --git a/t/t0025-crlf-auto.sh b/t/t0025-crlf-auto.sh
index f5f67a6..c164b46 100755
--- a/t/t0025-crlf-auto.sh
+++ b/t/t0025-crlf-auto.sh
@@ -12,144 +12,144 @@ test_expect_success setup '
git config core.autocrlf false &&
- for w in Hello world how are you; do echo $w; done >one &&
- for w in I am very very fine thank you; do echo ${w}Q; done | q_to_cr >two &&
- for w in Oh here is a QNUL byte how alarming; do echo ${w}; done | q_to_nul >three &&
+ for w in Hello world how are you; do echo $w; done >LFonly &&
+ for w in I am very very fine thank you; do echo ${w}Q; done | q_to_cr >CRLFonly &&
+ for w in Oh here is a QNUL byte how alarming; do echo ${w}; done | q_to_nul >LFwithNUL &&
git add . &&
git commit -m initial &&
- one=`git rev-parse HEAD:one` &&
- two=`git rev-parse HEAD:two` &&
- three=`git rev-parse HEAD:three` &&
+ LFonly=$(git rev-parse HEAD:LFonly) &&
+ CRLFonly=$(git rev-parse HEAD:CRLFonly) &&
+ LFwithNUL=$(git rev-parse HEAD:LFwithNUL) &&
echo happy.
'
test_expect_success 'default settings cause no changes' '
- rm -f .gitattributes tmp one two three &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
git read-tree --reset -u HEAD &&
- ! has_cr one &&
- has_cr two &&
- onediff=`git diff one` &&
- twodiff=`git diff two` &&
- threediff=`git diff three` &&
- test -z "$onediff" -a -z "$twodiff" -a -z "$threediff"
+ ! has_cr LFonly &&
+ has_cr CRLFonly &&
+ LFonlydiff=$(git diff LFonly) &&
+ CRLFonlydiff=$(git diff CRLFonly) &&
+ LFwithNULdiff=$(git diff LFwithNUL) &&
+ test -z "$LFonlydiff" -a -z "$CRLFonlydiff" -a -z "$LFwithNULdiff"
'
test_expect_success 'crlf=true causes a CRLF file to be normalized' '
# Backwards compatibility check
- rm -f .gitattributes tmp one two three &&
- echo "two crlf" > .gitattributes &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
+ echo "CRLFonly crlf" > .gitattributes &&
git read-tree --reset -u HEAD &&
# Note, "normalized" means that git will normalize it if added
- has_cr two &&
- twodiff=`git diff two` &&
- test -n "$twodiff"
+ has_cr CRLFonly &&
+ CRLFonlydiff=$(git diff CRLFonly) &&
+ test -n "$CRLFonlydiff"
'
test_expect_success 'text=true causes a CRLF file to be normalized' '
- rm -f .gitattributes tmp one two three &&
- echo "two text" > .gitattributes &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
+ echo "CRLFonly text" > .gitattributes &&
git read-tree --reset -u HEAD &&
# Note, "normalized" means that git will normalize it if added
- has_cr two &&
- twodiff=`git diff two` &&
- test -n "$twodiff"
+ has_cr CRLFonly &&
+ CRLFonlydiff=$(git diff CRLFonly) &&
+ test -n "$CRLFonlydiff"
'
test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=false' '
- rm -f .gitattributes tmp one two three &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
git config core.autocrlf false &&
- echo "one eol=crlf" > .gitattributes &&
+ echo "LFonly eol=crlf" > .gitattributes &&
git read-tree --reset -u HEAD &&
- has_cr one &&
- onediff=`git diff one` &&
- test -z "$onediff"
+ has_cr LFonly &&
+ LFonlydiff=$(git diff LFonly) &&
+ test -z "$LFonlydiff"
'
test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=input' '
- rm -f .gitattributes tmp one two three &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
git config core.autocrlf input &&
- echo "one eol=crlf" > .gitattributes &&
+ echo "LFonly eol=crlf" > .gitattributes &&
git read-tree --reset -u HEAD &&
- has_cr one &&
- onediff=`git diff one` &&
- test -z "$onediff"
+ has_cr LFonly &&
+ LFonlydiff=$(git diff LFonly) &&
+ test -z "$LFonlydiff"
'
test_expect_success 'eol=lf gives a normalized file LFs with autocrlf=true' '
- rm -f .gitattributes tmp one two three &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
git config core.autocrlf true &&
- echo "one eol=lf" > .gitattributes &&
+ echo "LFonly eol=lf" > .gitattributes &&
git read-tree --reset -u HEAD &&
- ! has_cr one &&
- onediff=`git diff one` &&
- test -z "$onediff"
+ ! has_cr LFonly &&
+ LFonlydiff=$(git diff LFonly) &&
+ test -z "$LFonlydiff"
'
test_expect_success 'autocrlf=true does not normalize CRLF files' '
- rm -f .gitattributes tmp one two three &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
git config core.autocrlf true &&
git read-tree --reset -u HEAD &&
- has_cr one &&
- has_cr two &&
- onediff=`git diff one` &&
- twodiff=`git diff two` &&
- threediff=`git diff three` &&
- test -z "$onediff" -a -z "$twodiff" -a -z "$threediff"
+ has_cr LFonly &&
+ has_cr CRLFonly &&
+ LFonlydiff=$(git diff LFonly) &&
+ CRLFonlydiff=$(git diff CRLFonly) &&
+ LFwithNULdiff=$(git diff LFwithNUL) &&
+ test -z "$LFonlydiff" -a -z "$CRLFonlydiff" -a -z "$LFwithNULdiff"
'
test_expect_success 'text=auto, autocrlf=true _does_ normalize CRLF files' '
- rm -f .gitattributes tmp one two three &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
git config core.autocrlf true &&
echo "* text=auto" > .gitattributes &&
git read-tree --reset -u HEAD &&
- has_cr one &&
- has_cr two &&
- onediff=`git diff one` &&
- twodiff=`git diff two` &&
- threediff=`git diff three` &&
- test -z "$onediff" -a -n "$twodiff" -a -z "$threediff"
+ has_cr LFonly &&
+ has_cr CRLFonly &&
+ LFonlydiff=$(git diff LFonly) &&
+ CRLFonlydiff=$(git diff CRLFonly) &&
+ LFwithNULdiff=$(git diff LFwithNUL) &&
+ test -z "$LFonlydiff" -a -n "$CRLFonlydiff" -a -z "$LFwithNULdiff"
'
test_expect_success 'text=auto, autocrlf=true does not normalize binary files' '
- rm -f .gitattributes tmp one two three &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
git config core.autocrlf true &&
echo "* text=auto" > .gitattributes &&
git read-tree --reset -u HEAD &&
- ! has_cr three &&
- threediff=`git diff three` &&
- test -z "$threediff"
+ ! has_cr LFwithNUL &&
+ LFwithNULdiff=$(git diff LFwithNUL) &&
+ test -z "$LFwithNULdiff"
'
test_expect_success 'eol=crlf _does_ normalize binary files' '
- rm -f .gitattributes tmp one two three &&
- echo "three eol=crlf" > .gitattributes &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
+ echo "LFwithNUL eol=crlf" > .gitattributes &&
git read-tree --reset -u HEAD &&
- has_cr three &&
- threediff=`git diff three` &&
- test -z "$threediff"
+ has_cr LFwithNUL &&
+ LFwithNULdiff=$(git diff LFwithNUL) &&
+ test -z "$LFwithNULdiff"
'
test_done
diff --git a/t/t0026-eol-config.sh b/t/t0026-eol-config.sh
index f37ac8f..c5203e2 100755
--- a/t/t0026-eol-config.sh
+++ b/t/t0026-eol-config.sh
@@ -12,7 +12,7 @@ test_expect_success setup '
git config core.autocrlf false &&
- echo "one text" > .gitattributes
+ 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 &&
@@ -20,8 +20,8 @@ test_expect_success setup '
git commit -m initial &&
- one=`git rev-parse HEAD:one` &&
- two=`git rev-parse HEAD:two` &&
+ one=$(git rev-parse HEAD:one) &&
+ two=$(git rev-parse HEAD:two) &&
echo happy.
'
@@ -34,9 +34,9 @@ test_expect_success 'eol=lf puts LFs in normalized file' '
! has_cr one &&
! has_cr two &&
- onediff=`git diff one` &&
- twodiff=`git diff two` &&
- test -z "$onediff" -a -z "$twodiff"
+ onediff=$(git diff one) &&
+ twodiff=$(git diff two) &&
+ test -z "$onediff" && test -z "$twodiff"
'
test_expect_success 'eol=crlf puts CRLFs in normalized file' '
@@ -47,9 +47,9 @@ test_expect_success 'eol=crlf puts CRLFs in normalized file' '
has_cr one &&
! has_cr two &&
- onediff=`git diff one` &&
- twodiff=`git diff two` &&
- test -z "$onediff" -a -z "$twodiff"
+ onediff=$(git diff one) &&
+ twodiff=$(git diff two) &&
+ test -z "$onediff" && test -z "$twodiff"
'
test_expect_success 'autocrlf=true overrides eol=lf' '
@@ -61,9 +61,9 @@ test_expect_success 'autocrlf=true overrides eol=lf' '
has_cr one &&
has_cr two &&
- onediff=`git diff one` &&
- twodiff=`git diff two` &&
- test -z "$onediff" -a -z "$twodiff"
+ onediff=$(git diff one) &&
+ twodiff=$(git diff two) &&
+ test -z "$onediff" && test -z "$twodiff"
'
test_expect_success 'autocrlf=true overrides unset eol' '
@@ -75,9 +75,29 @@ test_expect_success 'autocrlf=true overrides unset eol' '
has_cr one &&
has_cr two &&
- onediff=`git diff one` &&
- twodiff=`git diff two` &&
- test -z "$onediff" -a -z "$twodiff"
+ onediff=$(git diff one) &&
+ twodiff=$(git diff two) &&
+ test -z "$onediff" && test -z "$twodiff"
+'
+
+test_expect_success NATIVE_CRLF 'eol native is crlf' '
+
+ rm -rf native_eol && mkdir native_eol &&
+ (
+ cd native_eol &&
+ printf "*.txt text\n" >.gitattributes &&
+ printf "one\r\ntwo\r\nthree\r\n" >filedos.txt &&
+ printf "one\ntwo\nthree\n" >fileunix.txt &&
+ git init &&
+ git config core.autocrlf false &&
+ git config core.eol native &&
+ git add filedos.txt fileunix.txt &&
+ git commit -m "first" &&
+ rm file*.txt &&
+ git reset --hard HEAD &&
+ has_cr filedos.txt &&
+ has_cr fileunix.txt
+ )
'
test_done
diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh
new file mode 100755
index 0000000..9372589
--- /dev/null
+++ b/t/t0027-auto-crlf.sh
@@ -0,0 +1,553 @@
+#!/bin/sh
+
+test_description='CRLF conversion all combinations'
+
+. ./test-lib.sh
+
+if ! test_have_prereq EXPENSIVE
+then
+ skip_all="EXPENSIVE not set"
+ test_done
+fi
+
+compare_files () {
+ tr '\015\000' QN <"$1" >"$1".expect &&
+ tr '\015\000' QN <"$2" | tr -d 'Z' >"$2".actual &&
+ test_cmp "$1".expect "$2".actual &&
+ rm "$1".expect "$2".actual
+}
+
+compare_ws_file () {
+ pfx=$1
+ exp=$2.expect
+ act=$pfx.actual.$3
+ tr '\015\000abcdef0123456789' QN00000000000000000 <"$2" >"$exp" &&
+ tr '\015\000abcdef0123456789' QN00000000000000000 <"$3" >"$act" &&
+ test_cmp "$exp" "$act" &&
+ rm "$exp" "$act"
+}
+
+create_gitattributes () {
+ {
+ while test "$#" != 0
+ do
+ case "$1" in
+ auto) echo '*.txt text=auto' ;;
+ ident) echo '*.txt ident' ;;
+ text) echo '*.txt text' ;;
+ -text) echo '*.txt -text' ;;
+ crlf) echo '*.txt eol=crlf' ;;
+ lf) echo '*.txt eol=lf' ;;
+ "") ;;
+ *)
+ echo >&2 invalid attribute: "$1"
+ exit 1
+ ;;
+ esac &&
+ shift
+ done
+ } >.gitattributes
+}
+
+create_NNO_files () {
+ for crlf in false true input
+ do
+ for attr in "" auto text -text
+ do
+ for aeol in "" lf crlf
+ do
+ pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf}
+ cp CRLF_mix_LF ${pfx}_LF.txt &&
+ cp CRLF_mix_LF ${pfx}_CRLF.txt &&
+ cp CRLF_mix_LF ${pfx}_CRLF_mix_LF.txt &&
+ cp CRLF_mix_LF ${pfx}_LF_mix_CR.txt &&
+ cp CRLF_mix_LF ${pfx}_CRLF_nul.txt
+ done
+ done
+ done
+}
+
+check_warning () {
+ case "$1" in
+ LF_CRLF) echo "warning: LF will be replaced by CRLF" >"$2".expect ;;
+ CRLF_LF) echo "warning: CRLF will be replaced by LF" >"$2".expect ;;
+ '') >"$2".expect ;;
+ *) echo >&2 "Illegal 1": "$1" ; return false ;;
+ esac
+ grep "will be replaced by" "$2" | sed -e "s/\(.*\) in [^ ]*$/\1/" | uniq >"$2".actual
+ test_cmp "$2".expect "$2".actual
+}
+
+commit_check_warn () {
+ crlf=$1
+ attr=$2
+ lfname=$3
+ crlfname=$4
+ lfmixcrlf=$5
+ lfmixcr=$6
+ crlfnul=$7
+ pfx=crlf_${crlf}_attr_${attr}
+ create_gitattributes "$attr" &&
+ for f in LF CRLF LF_mix_CR CRLF_mix_LF LF_nul CRLF_nul
+ do
+ fname=${pfx}_$f.txt &&
+ cp $f $fname &&
+ git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err"
+ done &&
+ git commit -m "core.autocrlf $crlf" &&
+ check_warning "$lfname" ${pfx}_LF.err &&
+ check_warning "$crlfname" ${pfx}_CRLF.err &&
+ check_warning "$lfmixcrlf" ${pfx}_CRLF_mix_LF.err &&
+ check_warning "$lfmixcr" ${pfx}_LF_mix_CR.err &&
+ check_warning "$crlfnul" ${pfx}_CRLF_nul.err
+}
+
+commit_chk_wrnNNO () {
+ attr=$1 ; shift
+ aeol=$1 ; shift
+ crlf=$1 ; shift
+ lfwarn=$1 ; shift
+ crlfwarn=$1 ; shift
+ lfmixcrlf=$1 ; shift
+ lfmixcr=$1 ; shift
+ crlfnul=$1 ; shift
+ pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf}
+ #Commit files on top of existing file
+ create_gitattributes "$attr" $aeol &&
+ for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+ do
+ fname=${pfx}_$f.txt &&
+ cp $f $fname &&
+ printf Z >>"$fname" &&
+ git -c core.autocrlf=$crlf add $fname 2>/dev/null &&
+ git -c core.autocrlf=$crlf commit -m "commit_$fname" $fname >"${pfx}_$f.err" 2>&1
+ done
+
+ test_expect_success "commit NNO files crlf=$crlf attr=$attr LF" '
+ check_warning "$lfwarn" ${pfx}_LF.err
+ '
+ test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf CRLF" '
+ check_warning "$crlfwarn" ${pfx}_CRLF.err
+ '
+
+ test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf CRLF_mix_LF" '
+ check_warning "$lfmixcrlf" ${pfx}_CRLF_mix_LF.err
+ '
+
+ test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf LF_mix_cr" '
+ check_warning "$lfmixcr" ${pfx}_LF_mix_CR.err
+ '
+
+ test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf CRLF_nul" '
+ check_warning "$crlfnul" ${pfx}_CRLF_nul.err
+ '
+}
+
+stats_ascii () {
+ case "$1" in
+ LF)
+ echo lf
+ ;;
+ CRLF)
+ echo crlf
+ ;;
+ CRLF_mix_LF)
+ echo mixed
+ ;;
+ LF_mix_CR|CRLF_nul|LF_nul|CRLF_mix_CR)
+ echo "-text"
+ ;;
+ *)
+ echo error_invalid $1
+ ;;
+ esac
+
+}
+
+
+# contruct the attr/ returned by git ls-files --eol
+# Take none (=empty), one or two args
+# convert.c: eol=XX overrides text=auto
+attr_ascii () {
+ case $1,$2 in
+ -text,*) echo "-text" ;;
+ text,) echo "text" ;;
+ text,lf) echo "text eol=lf" ;;
+ text,crlf) echo "text eol=crlf" ;;
+ auto,) echo "text=auto" ;;
+ auto,lf) echo "text eol=lf" ;;
+ auto,crlf) echo "text eol=crlf" ;;
+ lf,) echo "text eol=lf" ;;
+ crlf,) echo "text eol=crlf" ;;
+ ,) echo "" ;;
+ *) echo invalid_attr "$1,$2" ;;
+ esac
+}
+
+check_files_in_repo () {
+ crlf=$1
+ attr=$2
+ lfname=$3
+ crlfname=$4
+ lfmixcrlf=$5
+ lfmixcr=$6
+ crlfnul=$7
+ pfx=crlf_${crlf}_attr_${attr}_ &&
+ compare_files $lfname ${pfx}LF.txt &&
+ compare_files $crlfname ${pfx}CRLF.txt &&
+ compare_files $lfmixcrlf ${pfx}CRLF_mix_LF.txt &&
+ compare_files $lfmixcr ${pfx}LF_mix_CR.txt &&
+ compare_files $crlfnul ${pfx}CRLF_nul.txt
+}
+
+check_in_repo_NNO () {
+ attr=$1 ; shift
+ aeol=$1 ; shift
+ crlf=$1 ; shift
+ lfname=$1 ; shift
+ crlfname=$1 ; shift
+ lfmixcrlf=$1 ; shift
+ lfmixcr=$1 ; shift
+ crlfnul=$1 ; shift
+ pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf}
+ test_expect_success "compare_files $lfname ${pfx}_LF.txt" '
+ compare_files $lfname ${pfx}_LF.txt
+ '
+ test_expect_success "compare_files $crlfname ${pfx}_CRLF.txt" '
+ compare_files $crlfname ${pfx}_CRLF.txt
+ '
+ test_expect_success "compare_files $lfmixcrlf ${pfx}_CRLF_mix_LF.txt" '
+ compare_files $lfmixcrlf ${pfx}_CRLF_mix_LF.txt
+ '
+ test_expect_success "compare_files $lfmixcr ${pfx}_LF_mix_CR.txt" '
+ compare_files $lfmixcr ${pfx}_LF_mix_CR.txt
+ '
+ test_expect_success "compare_files $crlfnul ${pfx}_CRLF_nul.txt" '
+ compare_files $crlfnul ${pfx}_CRLF_nul.txt
+ '
+}
+
+checkout_files () {
+ attr=$1 ; shift
+ ident=$1; shift
+ aeol=$1 ; shift
+ crlf=$1 ; shift
+ ceol=$1 ; shift
+ lfname=$1 ; shift
+ crlfname=$1 ; shift
+ lfmixcrlf=$1 ; shift
+ lfmixcr=$1 ; shift
+ crlfnul=$1 ; shift
+ create_gitattributes "$attr" $ident $aeol &&
+ git config core.autocrlf $crlf &&
+ pfx=eol_${ceol}_crlf_${crlf}_attr_${attr}_ &&
+ for f in LF CRLF LF_mix_CR CRLF_mix_LF LF_nul
+ do
+ rm crlf_false_attr__$f.txt &&
+ if test -z "$ceol"; then
+ git checkout crlf_false_attr__$f.txt
+ else
+ git -c core.eol=$ceol checkout crlf_false_attr__$f.txt
+ fi
+ done
+
+ test_expect_success "ls-files --eol attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol" '
+ test_when_finished "rm expect actual" &&
+ sort <<-EOF >expect &&
+ i/crlf w/$(stats_ascii $crlfname) attr/$(attr_ascii $attr $aeol) crlf_false_attr__CRLF.txt
+ i/mixed w/$(stats_ascii $lfmixcrlf) attr/$(attr_ascii $attr $aeol) crlf_false_attr__CRLF_mix_LF.txt
+ i/lf w/$(stats_ascii $lfname) attr/$(attr_ascii $attr $aeol) crlf_false_attr__LF.txt
+ i/-text w/$(stats_ascii $lfmixcr) attr/$(attr_ascii $attr $aeol) crlf_false_attr__LF_mix_CR.txt
+ i/-text w/$(stats_ascii $crlfnul) attr/$(attr_ascii $attr $aeol) crlf_false_attr__CRLF_nul.txt
+ i/-text w/$(stats_ascii $crlfnul) attr/$(attr_ascii $attr $aeol) crlf_false_attr__LF_nul.txt
+ EOF
+ git ls-files --eol crlf_false_attr__* |
+ sed -e "s/ / /g" -e "s/ */ /g" |
+ sort >actual &&
+ test_cmp expect actual
+ '
+ test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=LF" "
+ compare_ws_file $pfx $lfname crlf_false_attr__LF.txt
+ "
+ test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=CRLF" "
+ compare_ws_file $pfx $crlfname crlf_false_attr__CRLF.txt
+ "
+ test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=CRLF_mix_LF" "
+ compare_ws_file $pfx $lfmixcrlf crlf_false_attr__CRLF_mix_LF.txt
+ "
+ test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=LF_mix_CR" "
+ compare_ws_file $pfx $lfmixcr crlf_false_attr__LF_mix_CR.txt
+ "
+ test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=LF_nul" "
+ compare_ws_file $pfx $crlfnul crlf_false_attr__LF_nul.txt
+ "
+}
+
+# Test control characters
+# NUL SOH CR EOF==^Z
+test_expect_success 'ls-files --eol -o Text/Binary' '
+ test_when_finished "rm expect actual TeBi_*" &&
+ STRT=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA &&
+ STR=$STRT$STRT$STRT$STRT &&
+ printf "${STR}BBB\001" >TeBi_127_S &&
+ printf "${STR}BBBB\001">TeBi_128_S &&
+ printf "${STR}BBB\032" >TeBi_127_E &&
+ printf "\032${STR}BBB" >TeBi_E_127 &&
+ printf "${STR}BBBB\000">TeBi_128_N &&
+ printf "${STR}BBB\012">TeBi_128_L &&
+ printf "${STR}BBB\015">TeBi_127_C &&
+ printf "${STR}BB\015\012" >TeBi_126_CL &&
+ printf "${STR}BB\015\012\015" >TeBi_126_CLC &&
+ sort <<-\EOF >expect &&
+ i/ w/-text TeBi_127_S
+ i/ w/none TeBi_128_S
+ i/ w/none TeBi_127_E
+ i/ w/-text TeBi_E_127
+ i/ w/-text TeBi_128_N
+ i/ w/lf TeBi_128_L
+ i/ w/-text TeBi_127_C
+ i/ w/crlf TeBi_126_CL
+ i/ w/-text TeBi_126_CLC
+ EOF
+ git ls-files --eol -o |
+ sed -n -e "/TeBi_/{s!attr/[ ]*!!g
+ s! ! !g
+ s! *! !g
+ p
+ }" | sort >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup master' '
+ echo >.gitattributes &&
+ git checkout -b master &&
+ git add .gitattributes &&
+ git commit -m "add .gitattributes" "" &&
+ printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONE\nLINETWO\nLINETHREE" >LF &&
+ printf "\$Id: 0000000000000000000000000000000000000000 \$\r\nLINEONE\r\nLINETWO\r\nLINETHREE" >CRLF &&
+ printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONE\r\nLINETWO\nLINETHREE" >CRLF_mix_LF &&
+ printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONE\nLINETWO\rLINETHREE" >LF_mix_CR &&
+ printf "\$Id: 0000000000000000000000000000000000000000 \$\r\nLINEONE\r\nLINETWO\rLINETHREE" >CRLF_mix_CR &&
+ printf "\$Id: 0000000000000000000000000000000000000000 \$\r\nLINEONEQ\r\nLINETWO\r\nLINETHREE" | q_to_nul >CRLF_nul &&
+ printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONEQ\nLINETWO\nLINETHREE" | q_to_nul >LF_nul &&
+ create_NNO_files CRLF_mix_LF CRLF_mix_LF CRLF_mix_LF CRLF_mix_LF CRLF_mix_LF &&
+ git -c core.autocrlf=false add NNO_*.txt &&
+ git commit -m "mixed line endings" &&
+ test_tick
+'
+
+
+
+warn_LF_CRLF="LF will be replaced by CRLF"
+warn_CRLF_LF="CRLF will be replaced by LF"
+
+# WILC stands for "Warn if (this OS) converts LF into CRLF".
+# WICL: Warn if CRLF becomes LF
+# WAMIX: Mixed line endings: either CRLF->LF or LF->CRLF
+if test_have_prereq NATIVE_CRLF
+then
+ WILC=LF_CRLF
+ WICL=
+ WAMIX=LF_CRLF
+else
+ WILC=
+ WICL=CRLF_LF
+ WAMIX=CRLF_LF
+fi
+
+# attr LF CRLF CRLFmixLF LFmixCR CRLFNUL
+test_expect_success 'commit files empty attr' '
+ commit_check_warn false "" "" "" "" "" "" &&
+ commit_check_warn true "" "LF_CRLF" "" "LF_CRLF" "" "" &&
+ commit_check_warn input "" "" "CRLF_LF" "CRLF_LF" "" ""
+'
+
+test_expect_success 'commit files attr=auto' '
+ commit_check_warn false "auto" "$WILC" "$WICL" "$WAMIX" "" "" &&
+ commit_check_warn true "auto" "LF_CRLF" "" "LF_CRLF" "" "" &&
+ commit_check_warn input "auto" "" "CRLF_LF" "CRLF_LF" "" ""
+'
+
+test_expect_success 'commit files attr=text' '
+ commit_check_warn false "text" "$WILC" "$WICL" "$WAMIX" "$WILC" "$WICL" &&
+ commit_check_warn true "text" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "" &&
+ commit_check_warn input "text" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF"
+'
+
+test_expect_success 'commit files attr=-text' '
+ commit_check_warn false "-text" "" "" "" "" "" &&
+ commit_check_warn true "-text" "" "" "" "" "" &&
+ commit_check_warn input "-text" "" "" "" "" ""
+'
+
+test_expect_success 'commit files attr=lf' '
+ commit_check_warn false "lf" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF" &&
+ commit_check_warn true "lf" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF" &&
+ commit_check_warn input "lf" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF"
+'
+
+test_expect_success 'commit files attr=crlf' '
+ commit_check_warn false "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "" &&
+ commit_check_warn true "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "" &&
+ commit_check_warn input "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" ""
+'
+
+# attr LF CRLF CRLFmixLF LF_mix_CR CRLFNUL
+commit_chk_wrnNNO "" "" false "" "" "" "" ""
+commit_chk_wrnNNO "" "" true LF_CRLF "" "" "" ""
+commit_chk_wrnNNO "" "" input "" "" "" "" ""
+
+commit_chk_wrnNNO "auto" "" false "$WILC" "$WICL" "$WAMIX" "" ""
+commit_chk_wrnNNO "auto" "" true LF_CRLF "" LF_CRLF "" ""
+commit_chk_wrnNNO "auto" "" input "" CRLF_LF CRLF_LF "" ""
+
+for crlf in true false input
+do
+ commit_chk_wrnNNO -text "" $crlf "" "" "" "" ""
+ commit_chk_wrnNNO -text lf $crlf "" "" "" "" ""
+ commit_chk_wrnNNO -text crlf $crlf "" "" "" "" ""
+ commit_chk_wrnNNO "" lf $crlf "" CRLF_LF CRLF_LF "" CRLF_LF
+ commit_chk_wrnNNO "" crlf $crlf LF_CRLF "" LF_CRLF LF_CRLF ""
+ commit_chk_wrnNNO auto lf $crlf "" CRLF_LF CRLF_LF "" CRLF_LF
+ commit_chk_wrnNNO auto crlf $crlf LF_CRLF "" LF_CRLF LF_CRLF ""
+ commit_chk_wrnNNO text lf $crlf "" CRLF_LF CRLF_LF "" CRLF_LF
+ commit_chk_wrnNNO text crlf $crlf LF_CRLF "" LF_CRLF LF_CRLF ""
+done
+
+commit_chk_wrnNNO "text" "" false "$WILC" "$WICL" "$WAMIX" "$WILC" "$WICL"
+commit_chk_wrnNNO "text" "" true LF_CRLF "" LF_CRLF LF_CRLF ""
+commit_chk_wrnNNO "text" "" input "" CRLF_LF CRLF_LF "" CRLF_LF
+
+test_expect_success 'create files cleanup' '
+ rm -f *.txt &&
+ git -c core.autocrlf=false reset --hard
+'
+
+test_expect_success 'commit empty gitattribues' '
+ check_files_in_repo false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo true "" LF LF LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo input "" LF LF LF LF_mix_CR CRLF_nul
+'
+
+test_expect_success 'commit text=auto' '
+ check_files_in_repo false "auto" LF LF LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo true "auto" LF LF LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo input "auto" LF LF LF LF_mix_CR CRLF_nul
+'
+
+test_expect_success 'commit text' '
+ check_files_in_repo false "text" LF LF LF LF_mix_CR LF_nul &&
+ check_files_in_repo true "text" LF LF LF LF_mix_CR LF_nul &&
+ check_files_in_repo input "text" LF LF LF LF_mix_CR LF_nul
+'
+
+test_expect_success 'commit -text' '
+ check_files_in_repo false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+'
+
+for crlf in true false input
+do
+ # attr aeol LF CRLF CRLF_mix_LF LF_mix_CR CRLFNUL
+ check_in_repo_NNO "" "" $crlf LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+ check_in_repo_NNO -text "" $crlf LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+ check_in_repo_NNO -text lf $crlf LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+ check_in_repo_NNO -text crlf $crlf LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+ check_in_repo_NNO auto "" $crlf LF LF LF LF_mix_CR CRLF_nul
+ check_in_repo_NNO auto lf $crlf LF LF LF LF_mix_CR LF_nul
+ check_in_repo_NNO auto crlf $crlf LF LF LF LF_mix_CR LF_nul
+ check_in_repo_NNO text "" $crlf LF LF LF LF_mix_CR LF_nul
+ check_in_repo_NNO text lf $crlf LF LF LF LF_mix_CR LF_nul
+ check_in_repo_NNO text crlf $crlf LF LF LF LF_mix_CR LF_nul
+done
+################################################################################
+# Check how files in the repo are changed when they are checked out
+# How to read the table below:
+# - checkout_files will check multiple files with a combination of settings
+# and attributes (core.autocrlf=input is forbidden with core.eol=crlf)
+#
+# - parameter $1 : text in .gitattributs "" (empty) | auto | text | -text
+# - parameter $2 : ident "" | i (i == ident)
+# - parameter $3 : eol in .gitattributs "" (empty) | lf | crlf
+# - parameter $4 : core.autocrlf false | true | input
+# - parameter $5 : core.eol "" | lf | crlf | "native"
+# - parameter $6 : reference for a file with only LF in the repo
+# - parameter $7 : reference for a file with only CRLF in the repo
+# - parameter $8 : reference for a file with mixed LF and CRLF in the repo
+# - parameter $9 : reference for a file with LF and CR in the repo
+# - parameter $10 : reference for a file with CRLF and a NUL (should be handled as binary when auto)
+
+if test_have_prereq NATIVE_CRLF
+then
+MIX_CRLF_LF=CRLF
+MIX_LF_CR=CRLF_mix_CR
+NL=CRLF
+LFNUL=CRLF_nul
+else
+MIX_CRLF_LF=CRLF_mix_LF
+MIX_LF_CR=LF_mix_CR
+NL=LF
+LFNUL=LF_nul
+fi
+export CRLF_MIX_LF_CR MIX NL
+
+# Same handling with and without ident
+for id in "" ident
+do
+ for ceol in lf crlf native
+ do
+ for crlf in true false input
+ do
+ # -text overrides core.autocrlf and core.eol
+ # text and eol=crlf or eol=lf override core.autocrlf and core.eol
+ checkout_files -text "$id" "" "$crlf" "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ checkout_files -text "$id" "lf" "$crlf" "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ checkout_files -text "$id" "crlf" "$crlf" "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ # text
+ checkout_files text "$id" "lf" "$crlf" "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ checkout_files text "$id" "crlf" "$crlf" "$ceol" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+ # currently the same as text, eol=XXX
+ checkout_files auto "$id" "lf" "$crlf" "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ checkout_files auto "$id" "crlf" "$crlf" "$ceol" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+ done
+
+ # core.autocrlf false, different core.eol
+ checkout_files "" "$id" "" false "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ # core.autocrlf true
+ checkout_files "" "$id" "" true "$ceol" CRLF CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ # text: core.autocrlf = true overrides core.eol
+ checkout_files auto "$id" "" true "$ceol" CRLF CRLF CRLF LF_mix_CR LF_nul
+ checkout_files text "$id" "" true "$ceol" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+ # text: core.autocrlf = input overrides core.eol
+ checkout_files text "$id" "" input "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ checkout_files auto "$id" "" input "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ # text=auto + eol=XXX
+ done
+ # text: core.autocrlf=false uses core.eol
+ checkout_files text "$id" "" false crlf CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+ checkout_files text "$id" "" false lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ # text: core.autocrlf=false and core.eol unset(or native) uses native eol
+ checkout_files text "$id" "" false "" $NL CRLF $MIX_CRLF_LF $MIX_LF_CR $LFNUL
+ checkout_files text "$id" "" false native $NL CRLF $MIX_CRLF_LF $MIX_LF_CR $LFNUL
+ # auto: core.autocrlf=false and core.eol unset(or native) uses native eol
+ checkout_files auto "$id" "" false "" $NL CRLF $MIX_CRLF_LF LF_mix_CR LF_nul
+ checkout_files auto "$id" "" false native $NL CRLF $MIX_CRLF_LF LF_mix_CR LF_nul
+done
+
+# Should be the last test case: remove some files from the worktree
+test_expect_success 'ls-files --eol -d -z' '
+ rm crlf_false_attr__CRLF.txt crlf_false_attr__CRLF_mix_LF.txt crlf_false_attr__LF.txt .gitattributes &&
+ cat >expect <<-\EOF &&
+ i/crlf w/ crlf_false_attr__CRLF.txt
+ i/lf w/ .gitattributes
+ i/lf w/ crlf_false_attr__LF.txt
+ i/mixed w/ crlf_false_attr__CRLF_mix_LF.txt
+ EOF
+ git ls-files --eol -d |
+ sed -e "s!attr/[^ ]*!!g" -e "s/ / /g" -e "s/ */ /g" |
+ sort >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh
index ccb0a3c..29e91d8 100755
--- a/t/t0030-stripspace.sh
+++ b/t/t0030-stripspace.sh
@@ -225,22 +225,22 @@ test_expect_success \
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
+ 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 $(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 \
@@ -397,4 +397,45 @@ test_expect_success 'strip comments, too' '
test -z "$(echo "# comment" | git stripspace -s)"
'
+test_expect_success 'strip comments with changed comment char' '
+ test ! -z "$(echo "; comment" | git -c core.commentchar=";" stripspace)" &&
+ test -z "$(echo "; comment" | git -c core.commentchar=";" stripspace -s)"
+'
+
+test_expect_success '-c with single line' '
+ printf "# foo\n" >expect &&
+ printf "foo" | git stripspace -c >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '-c with single line followed by empty line' '
+ printf "# foo\n#\n" >expect &&
+ printf "foo\n\n" | git stripspace -c >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '-c with newline only' '
+ printf "#\n" >expect &&
+ printf "\n" | git stripspace -c >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--comment-lines with single line' '
+ printf "# foo\n" >expect &&
+ printf "foo" | git stripspace -c >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '-c with changed comment char' '
+ printf "; foo\n" >expect &&
+ printf "foo" | git -c core.commentchar=";" stripspace -c >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'avoid SP-HT sequence in commented line' '
+ printf "#\tone\n#\n# two\n" >expect &&
+ printf "\tone\n\ntwo\n" | git stripspace -c >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index 2092450..db5f60d 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -7,19 +7,23 @@ test_description='our own option parser'
. ./test-lib.sh
-cat > expect << EOF
+cat >expect <<\EOF
usage: test-parse-options <options>
- -b, --boolean get a boolean
+ --yes get a boolean
+ -D, --no-doubt begins with 'no-'
+ -B, --no-fear be brave
+ -b, --boolean increment by one
-4, --or4 bitwise-or boolean with ...0100
--neg-or4 same as --no-or4
-i, --integer <n> get a integer
-j <n> get a integer, too
+ -m, --magnitude <n> get a magnitude
--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>
+ -F, --file <file> set file to <file>
String options
-s, --string <string>
@@ -27,7 +31,7 @@ String options
--string2 <str> get another string
--st <st> get another string (pervert ordering)
-o <str> get another string
- --default-string set string to default
+ --list <str> add str to list
Magic arguments
--quux means --quux
@@ -41,73 +45,135 @@ Standard options
-v, --verbose be verbose
-n, --dry-run dry run
-q, --quiet be quiet
+ --expect <string> expected output in the variable dump
EOF
test_expect_success 'test help' '
- test_must_fail test-parse-options -h > output 2> output.err &&
- test ! -s output.err &&
- test_cmp expect output
+ test_must_fail test-parse-options -h >output 2>output.err &&
+ test_must_be_empty output.err &&
+ test_i18ncmp expect output
'
mv expect expect.err
-cat > expect << EOF
+check () {
+ what="$1" &&
+ shift &&
+ expect="$1" &&
+ shift &&
+ test-parse-options --expect="$what $expect" "$@"
+}
+
+check_unknown_i18n() {
+ case "$1" in
+ --*)
+ echo error: unknown option \`${1#--}\' >expect ;;
+ -*)
+ echo error: unknown switch \`${1#-}\' >expect ;;
+ esac &&
+ cat expect.err >>expect &&
+ test_must_fail test-parse-options $* >output 2>output.err &&
+ test_must_be_empty output &&
+ test_i18ncmp expect output.err
+}
+
+test_expect_success 'OPT_BOOL() #1' 'check boolean: 1 --yes'
+test_expect_success 'OPT_BOOL() #2' 'check boolean: 1 --no-doubt'
+test_expect_success 'OPT_BOOL() #3' 'check boolean: 1 -D'
+test_expect_success 'OPT_BOOL() #4' 'check boolean: 1 --no-fear'
+test_expect_success 'OPT_BOOL() #5' 'check boolean: 1 -B'
+
+test_expect_success 'OPT_BOOL() is idempotent #1' 'check boolean: 1 --yes --yes'
+test_expect_success 'OPT_BOOL() is idempotent #2' 'check boolean: 1 -DB'
+
+test_expect_success 'OPT_BOOL() negation #1' 'check boolean: 0 -D --no-yes'
+test_expect_success 'OPT_BOOL() negation #2' 'check boolean: 0 -D --no-no-doubt'
+
+test_expect_success 'OPT_BOOL() no negation #1' 'check_unknown_i18n --fear'
+test_expect_success 'OPT_BOOL() no negation #2' 'check_unknown_i18n --no-no-fear'
+
+test_expect_success 'OPT_BOOL() positivation' 'check boolean: 0 -D --doubt'
+
+test_expect_success 'OPT_INT() negative' 'check integer: -2345 -i -2345'
+
+test_expect_success 'OPT_MAGNITUDE() simple' '
+ check magnitude: 2345678 -m 2345678
+'
+
+test_expect_success 'OPT_MAGNITUDE() kilo' '
+ check magnitude: 239616 -m 234k
+'
+
+test_expect_success 'OPT_MAGNITUDE() mega' '
+ check magnitude: 104857600 -m 100m
+'
+
+test_expect_success 'OPT_MAGNITUDE() giga' '
+ check magnitude: 1073741824 -m 1g
+'
+
+test_expect_success 'OPT_MAGNITUDE() 3giga' '
+ check magnitude: 3221225472 -m 3g
+'
+
+cat >expect <<\EOF
boolean: 2
integer: 1729
+magnitude: 16384
timestamp: 0
string: 123
abbrev: 7
verbose: 2
-quiet: no
+quiet: 0
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-parse-options -s123 -b -i 1729 -m 16k -b -vv -n -F my.file \
+ >output 2>output.err &&
test_cmp expect output &&
- test ! -s output.err
+ test_must_be_empty output.err
'
-cat > expect << EOF
+cat >expect <<\EOF
boolean: 2
integer: 1729
+magnitude: 16384
timestamp: 0
string: 321
abbrev: 10
verbose: 2
-quiet: no
+quiet: 0
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-parse-options --boolean --integer 1729 --magnitude 16k \
+ --boolean --string2=321 --verbose --verbose --no-dry-run \
+ --abbrev=10 --file fi.le --obsolete \
+ >output 2>output.err &&
+ test_must_be_empty 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
+ test_expect_code 129 test-parse-options -s &&
+ test_expect_code 129 test-parse-options --string &&
+ test_expect_code 129 test-parse-options --file
'
-cat > expect << EOF
+cat >expect <<\EOF
boolean: 1
integer: 13
+magnitude: 0
timestamp: 0
string: 123
abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
dry run: no
file: (not set)
arg 00: a1
@@ -117,223 +183,369 @@ 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 &&
+ >output 2>output.err &&
+ test_must_be_empty output.err &&
test_cmp expect output
'
-cat > expect << EOF
+cat >expect <<\EOF
boolean: 0
integer: 2
+magnitude: 0
timestamp: 0
string: (not set)
abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
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-parse-options --int 2 --boolean --no-bo >output 2>output.err &&
+ test_must_be_empty output.err &&
test_cmp expect output
'
test_expect_success 'unambiguously abbreviated option with "="' '
- test-parse-options --int=2 > output 2> output.err &&
- test ! -s output.err &&
+ test-parse-options --int=2 >output 2>output.err &&
+ test_must_be_empty output.err &&
test_cmp expect output
'
test_expect_success 'ambiguously abbreviated option' '
- test-parse-options --strin 123;
- test $? = 129
+ test_expect_code 129 test-parse-options --strin 123
'
-cat > expect << EOF
+cat >expect <<\EOF
boolean: 0
integer: 0
+magnitude: 0
timestamp: 0
string: 123
abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
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-parse-options --st 123 >output 2>output.err &&
+ test_must_be_empty output.err &&
test_cmp expect output
'
-cat > typo.err << EOF
-error: did you mean \`--boolean\` (with two dashes ?)
+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_must_fail test-parse-options -boolean >output 2>output.err &&
+ test_must_be_empty output &&
test_cmp typo.err output.err
'
-cat > expect <<EOF
+cat >typo.err <<\EOF
+error: did you mean `--ambiguous` (with two dashes ?)
+EOF
+
+test_expect_success 'detect possible typos' '
+ test_must_fail test-parse-options -ambiguous >output 2>output.err &&
+ test_must_be_empty output &&
+ test_cmp typo.err output.err
+'
+
+cat >expect <<\EOF
boolean: 0
integer: 0
+magnitude: 0
timestamp: 0
string: (not set)
abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
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
+ test-parse-options --quux >output 2>output.err &&
+ test_must_be_empty output.err &&
+ test_cmp expect output
'
-cat > expect <<EOF
+cat >expect <<\EOF
boolean: 0
integer: 0
+magnitude: 0
timestamp: 1
-string: default
+string: (not set)
abbrev: 7
-verbose: 0
-quiet: yes
+verbose: -1
+quiet: 1
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_expect_success 'OPT_DATE() works' '
+ test-parse-options -t "1970-01-01 00:00:01 +0000" \
+ foo -q >output 2>output.err &&
+ test_must_be_empty output.err &&
test_cmp expect output
'
-cat > expect <<EOF
+cat >expect <<\EOF
Callback: "four", 0
boolean: 5
integer: 4
+magnitude: 0
timestamp: 0
string: (not set)
abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
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-parse-options --length=four -b -4 >output 2>output.err &&
+ test_must_be_empty output.err &&
test_cmp expect output
'
-cat > expect <<EOF
-Callback: "not set", 1
-EOF
+>expect
test_expect_success 'OPT_CALLBACK() and callback errors work' '
- test_must_fail test-parse-options --no-length > output 2> output.err &&
- test_cmp expect output &&
- test_cmp expect.err output.err
+ test_must_fail test-parse-options --no-length >output 2>output.err &&
+ test_i18ncmp expect output &&
+ test_i18ncmp expect.err output.err
'
-cat > expect <<EOF
+cat >expect <<\EOF
boolean: 1
integer: 23
+magnitude: 0
timestamp: 0
string: (not set)
abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
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-parse-options --set23 -bbbbb --no-or4 >output 2>output.err &&
+ test_must_be_empty output.err &&
test_cmp expect output
'
test_expect_success 'OPT_NEGBIT() and OPT_SET_INT() work' '
- test-parse-options --set23 -bbbbb --neg-or4 > output 2> output.err &&
- test ! -s output.err &&
+ test-parse-options --set23 -bbbbb --neg-or4 >output 2>output.err &&
+ test_must_be_empty output.err &&
test_cmp expect output
'
-cat > expect <<EOF
+cat >expect <<\EOF
boolean: 6
integer: 0
+magnitude: 0
timestamp: 0
string: (not set)
abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
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-parse-options -bb --or4 >output 2>output.err &&
+ test_must_be_empty output.err &&
test_cmp expect output
'
test_expect_success 'OPT_NEGBIT() works' '
- test-parse-options -bb --no-neg-or4 > output 2> output.err &&
- test ! -s output.err &&
+ test-parse-options -bb --no-neg-or4 >output 2>output.err &&
+ test_must_be_empty 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_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' '
+ test-parse-options + + + + + + >output 2>output.err &&
+ test_must_be_empty output.err &&
test_cmp expect output
'
-cat > expect <<EOF
+cat >expect <<\EOF
boolean: 0
integer: 12345
+magnitude: 0
timestamp: 0
string: (not set)
abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
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-parse-options -12345 >output 2>output.err &&
+ test_must_be_empty output.err &&
test_cmp expect output
'
-cat >expect <<EOF
+cat >expect <<\EOF
boolean: 0
integer: 0
+magnitude: 0
timestamp: 0
string: (not set)
abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
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_must_be_empty output.err &&
+ test_cmp expect output
+'
+
+cat >>expect <<\EOF
+list: foo
+list: bar
+list: baz
+EOF
+test_expect_success '--list keeps list of strings' '
+ test-parse-options --list foo --list=bar --list=baz >output &&
+ test_cmp expect output
+'
+
+test_expect_success '--no-list resets list' '
+ test-parse-options --list=other --list=irrelevant --list=options \
+ --no-list --list=foo --list=bar --list=baz >output &&
+ test_cmp expect output
+'
+
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: -1
+quiet: 3
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'multiple quiet levels' '
+ test-parse-options -q -q -q >output 2>output.err &&
+ test_must_be_empty output.err &&
+ test_cmp expect output
+'
+
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 3
+quiet: 0
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'multiple verbose levels' '
+ test-parse-options -v -v -v >output 2>output.err &&
+ test_must_be_empty output.err &&
+ test_cmp expect output
+'
+
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: -1
+quiet: 0
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success '--no-quiet sets --quiet to 0' '
+ test-parse-options --no-quiet >output 2>output.err &&
+ test_must_be_empty output.err &&
+ test_cmp expect output
+'
+
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: -1
+quiet: 0
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success '--no-quiet resets multiple -q to 0' '
+ test-parse-options -q -q -q --no-quiet >output 2>output.err &&
+ test_must_be_empty output.err &&
+ test_cmp expect output
+'
+
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: 0
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success '--no-verbose sets verbose to 0' '
+ test-parse-options --no-verbose >output 2>output.err &&
+ test_must_be_empty output.err &&
+ test_cmp expect output
+'
+
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: 0
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success '--no-verbose resets multiple verbose to 0' '
+ test-parse-options -v -v -v --no-verbose >output 2>output.err &&
+ test_must_be_empty output.err &&
test_cmp expect output
'
diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh
index 41df6bc..b29d749 100755
--- a/t/t0050-filesystem.sh
+++ b/t/t0050-filesystem.sh
@@ -4,81 +4,58 @@ 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' '
+auml=$(printf '\303\244')
+aumlcdiar=$(printf '\141\314\210')
+if test_have_prereq CASE_INSENSITIVE_FS
+then
+ say "will test on a case insensitive filesystem"
+ test_case=test_expect_failure
+else
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
- }
-'
+fi
-test "$case_insensitive" &&
- say "will test on a case insensitive filesystem"
-test "$unibad" &&
+if test_have_prereq UTF8_NFD_TO_NFC
+then
say "will test on a unicode corrupting filesystem"
-test "$no_symlinks" &&
+ test_unicode=test_expect_failure
+else
+ test_unicode=test_expect_success
+fi
+
+test_have_prereq SYMLINKS ||
say "will test on a filesystem lacking symbolic links"
-if test "$case_insensitive"
+if test_have_prereq CASE_INSENSITIVE_FS
then
test_expect_success "detection of case insensitive filesystem during repo init" '
-
test $(git config --bool core.ignorecase) = true
'
else
test_expect_success "detection of case insensitive filesystem during repo init" '
-
- test_must_fail git config --bool core.ignorecase >/dev/null ||
- test $(git config --bool core.ignorecase) = false
+ {
+ test_must_fail git config --bool core.ignorecase >/dev/null ||
+ test $(git config --bool core.ignorecase) = false
+ }
'
fi
-if test "$no_symlinks"
+if test_have_prereq SYMLINKS
then
test_expect_success "detection of filesystem w/o symlink support during repo init" '
-
- v=$(git config --bool core.symlinks) &&
- test "$v" = false
+ {
+ test_must_fail git config --bool core.symlinks ||
+ test "$(git config --bool core.symlinks)" = true
+ }
'
else
test_expect_success "detection of filesystem w/o symlink support during repo init" '
-
- test_must_fail git config --bool core.symlinks ||
- test "$(git config --bool core.symlinks)" = true
+ v=$(git config --bool core.symlinks) &&
+ test "$v" = false
'
fi
test_expect_success "setup case tests" '
-
git config core.ignorecase true &&
touch camelcase &&
git add camelcase &&
@@ -89,29 +66,23 @@ test_expect_success "setup case tests" '
git mv tmp CamelCase &&
git commit -m "rename" &&
git checkout -f master
-
'
-$test_case 'rename (case change)' '
-
+test_expect_success 'rename (case change)' '
git mv camelcase CamelCase &&
git commit -m "rename"
-
'
-$test_case 'merge (case change)' '
-
+test_expect_success 'merge (case change)' '
rm -f CamelCase &&
rm -f camelcase &&
git reset --hard initial &&
git merge topic
-
'
-test_expect_failure 'add (with different case)' '
-
+test_expect_failure CASE_INSENSITIVE_FS 'add (with different case)' '
git reset --hard initial &&
rm camelcase &&
echo 1 >CamelCase &&
@@ -119,37 +90,31 @@ test_expect_failure 'add (with different case)' '
camel=$(git ls-files | grep -i camelcase) &&
test $(echo "$camel" | wc -l) = 1 &&
test "z$(git cat-file blob :$camel)" = z1
-
'
test_expect_success "setup unicode normalization tests" '
-
- test_create_repo unicode &&
- cd unicode &&
- touch "$aumlcdiar" &&
- git add "$aumlcdiar" &&
- git commit -m initial
- git tag initial &&
- git checkout -b topic &&
- git mv $aumlcdiar tmp &&
- git mv tmp "$auml" &&
- git commit -m rename &&
- git checkout -f master
-
+ test_create_repo unicode &&
+ cd unicode &&
+ git config core.precomposeunicode false &&
+ touch "$aumlcdiar" &&
+ git add "$aumlcdiar" &&
+ git commit -m initial &&
+ git tag initial &&
+ git checkout -b topic &&
+ git mv $aumlcdiar tmp &&
+ git mv tmp "$auml" &&
+ git commit -m rename &&
+ git checkout -f master
'
$test_unicode 'rename (silent unicode normalization)' '
-
- git mv "$aumlcdiar" "$auml" &&
- git commit -m rename
-
+ git mv "$aumlcdiar" "$auml" &&
+ git commit -m rename
'
$test_unicode 'merge (silent unicode normalization)' '
-
- git reset --hard initial &&
- git merge topic
-
+ git reset --hard initial &&
+ git merge topic
'
test_done
diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh
new file mode 100755
index 0000000..2630e75
--- /dev/null
+++ b/t/t0056-git-C.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+test_description='"-C <path>" option and its effects on other path-related options'
+
+. ./test-lib.sh
+
+test_expect_success '"git -C <path>" runs git from the directory <path>' '
+ test_create_repo dir1 &&
+ echo 1 >dir1/a.txt &&
+ msg="initial in dir1" &&
+ (cd dir1 && git add a.txt && git commit -m "$msg") &&
+ echo "$msg" >expected &&
+ git -C dir1 log --format=%s >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '"git -C <path>" with an empty <path> is a no-op' '
+ (
+ mkdir -p dir1/subdir &&
+ cd dir1/subdir &&
+ git -C "" rev-parse --show-prefix >actual &&
+ echo subdir/ >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'Multiple -C options: "-C dir1 -C dir2" is equivalent to "-C dir1/dir2"' '
+ test_create_repo dir1/dir2 &&
+ echo 1 >dir1/dir2/b.txt &&
+ git -C dir1/dir2 add b.txt &&
+ msg="initial in dir1/dir2" &&
+ echo "$msg" >expected &&
+ git -C dir1/dir2 commit -m "$msg" &&
+ git -C dir1 -C dir2 log --format=%s >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Effect on --git-dir option: "-C c --git-dir=a.git" is equivalent to "--git-dir c/a.git"' '
+ mkdir c &&
+ mkdir c/a &&
+ mkdir c/a.git &&
+ (cd c/a.git && git init --bare) &&
+ echo 1 >c/a/a.txt &&
+ git --git-dir c/a.git --work-tree=c/a add a.txt &&
+ git --git-dir c/a.git --work-tree=c/a commit -m "initial" &&
+ git --git-dir=c/a.git log -1 --format=%s >expected &&
+ git -C c --git-dir=a.git log -1 --format=%s >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Order should not matter: "--git-dir=a.git -C c" is equivalent to "-C c --git-dir=a.git"' '
+ git -C c --git-dir=a.git log -1 --format=%s >expected &&
+ git --git-dir=a.git -C c log -1 --format=%s >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Effect on --work-tree option: "-C c/a.git --work-tree=../a" is equivalent to "--work-tree=c/a --git-dir=c/a.git"' '
+ rm c/a/a.txt &&
+ git --git-dir=c/a.git --work-tree=c/a status >expected &&
+ git -C c/a.git --work-tree=../a status >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Order should not matter: "--work-tree=../a -C c/a.git" is equivalent to "-C c/a.git --work-tree=../a"' '
+ git -C c/a.git --work-tree=../a status >expected &&
+ git --work-tree=../a -C c/a.git status >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Effect on --git-dir and --work-tree options - "-C c --git-dir=a.git --work-tree=a" is equivalent to "--git-dir=c/a.git --work-tree=c/a"' '
+ git --git-dir=c/a.git --work-tree=c/a status >expected &&
+ git -C c --git-dir=a.git --work-tree=a status >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Order should not matter: "-C c --git-dir=a.git --work-tree=a" is equivalent to "--git-dir=a.git -C c --work-tree=a"' '
+ git -C c --git-dir=a.git --work-tree=a status >expected &&
+ git --git-dir=a.git -C c --work-tree=a status >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Order should not matter: "-C c --git-dir=a.git --work-tree=a" is equivalent to "--git-dir=a.git --work-tree=a -C c"' '
+ git -C c --git-dir=a.git --work-tree=a status >expected &&
+ git --git-dir=a.git --work-tree=a -C c status >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Relative followed by fullpath: "-C ./here -C /there" is equivalent to "-C /there"' '
+ echo "initial in dir1/dir2" >expected &&
+ git -C dir1 -C "$(pwd)/dir1/dir2" log --format=%s >actual &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 53cf1f8..bf2deee 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -8,8 +8,30 @@ test_description='Test various path utilities'
. ./test-lib.sh
norm_path() {
+ expected=$(test-path-utils print_path "$2")
test_expect_success $3 "normalize path: $1 => $2" \
- "test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'"
+ "test \"\$(test-path-utils normalize_path_copy '$1')\" = '$expected'"
+}
+
+relative_path() {
+ expected=$(test-path-utils print_path "$3")
+ test_expect_success $4 "relative path: $1 $2 => $3" \
+ "test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'"
+}
+
+test_submodule_relative_url() {
+ test_expect_success "test_submodule_relative_url: $1 $2 $3 => $4" "
+ actual=\$(git submodule--helper resolve-relative-url-test '$1' '$2' '$3') &&
+ test \"\$actual\" = '$4'
+ "
+}
+
+test_git_path() {
+ test_expect_success "git-path $1 $2 => $3" "
+ $1 git rev-parse --git-path $2 >actual &&
+ echo $3 >expect &&
+ test_cmp expect actual
+ "
}
# On Windows, we are using MSYS's bash, which mangles the paths.
@@ -21,12 +43,21 @@ if test $rootoff = 2; then
rootoff= # we are on Unix
else
rootoff=$(($rootoff-1))
+ # In MSYS2, the root directory "/" is translated into a Windows
+ # directory *with* trailing slash. Let's test for that and adjust
+ # our expected longest ancestor length accordingly.
+ case "$(test-path-utils print_path /)" in
+ */) rootslash=1;;
+ *) rootslash=0;;
+ esac
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-$rootslash))
+ test $expected -lt 0 ||
expected=$(($expected+$rootoff))
fi
test_expect_success "longest ancestor: $1 $2 => $expected" \
@@ -34,8 +65,8 @@ ancestor() {
test \"\$actual\" = '$expected'"
}
-# Absolute path tests must be skipped on Windows because due to path mangling
-# the test program never sees a POSIX-style absolute path
+# Some absolute path tests should be skipped on Windows due to path mangling
+# on POSIX-style absolute paths
case $(uname -s) in
*MINGW*)
;;
@@ -44,6 +75,9 @@ case $(uname -s) in
;;
esac
+test_expect_success basename 'test-path-utils basename'
+test_expect_success dirname 'test-path-utils dirname'
+
norm_path "" ""
norm_path . ""
norm_path ./ ""
@@ -68,75 +102,246 @@ norm_path d1/s1//../s2/../../d2 d2
norm_path d1/.../d2 d1/.../d2
norm_path d1/..././../d2 d1/d2
-norm_path / / POSIX
+norm_path / /
norm_path // / POSIX
norm_path /// / POSIX
-norm_path /. / POSIX
+norm_path /. /
norm_path /./ / POSIX
norm_path /./.. ++failed++ POSIX
-norm_path /../. ++failed++ POSIX
+norm_path /../. ++failed++
norm_path /./../.// ++failed++ POSIX
norm_path /dir/.. / POSIX
norm_path /dir/sub/../.. / POSIX
norm_path /dir/sub/../../.. ++failed++ POSIX
-norm_path /dir /dir POSIX
-norm_path /dir// /dir/ POSIX
-norm_path /./dir /dir POSIX
-norm_path /dir/. /dir/ POSIX
-norm_path /dir///./ /dir/ POSIX
-norm_path /dir//sub/.. /dir/ POSIX
-norm_path /dir/sub/../ /dir/ POSIX
+norm_path /dir /dir
+norm_path /dir// /dir/
+norm_path /./dir /dir
+norm_path /dir/. /dir/
+norm_path /dir///./ /dir/
+norm_path /dir//sub/.. /dir/
+norm_path /dir/sub/../ /dir/
norm_path //dir/sub/../. /dir/ POSIX
-norm_path /dir/s1/../s2/ /dir/s2/ POSIX
-norm_path /d1/s1///s2/..//../s3/ /d1/s3/ POSIX
-norm_path /d1/s1//../s2/../../d2 /d2 POSIX
-norm_path /d1/.../d2 /d1/.../d2 POSIX
-norm_path /d1/..././../d2 /d1/d2 POSIX
+norm_path /dir/s1/../s2/ /dir/s2/
+norm_path /d1/s1///s2/..//../s3/ /d1/s3/
+norm_path /d1/s1//../s2/../../d2 /d2
+norm_path /d1/.../d2 /d1/.../d2
+norm_path /d1/..././../d2 /d1/d2
-ancestor / "" -1
ancestor / / -1
-ancestor /foo "" -1
-ancestor /foo : -1
-ancestor /foo ::. -1
-ancestor /foo ::..:: -1
ancestor /foo / 0
ancestor /foo /fo -1
ancestor /foo /foo -1
-ancestor /foo /foo/ -1
ancestor /foo /bar -1
-ancestor /foo /bar/ -1
ancestor /foo /foo/bar -1
-ancestor /foo /foo:/bar/ -1
-ancestor /foo /foo/:/bar/ -1
-ancestor /foo /foo::/bar/ -1
-ancestor /foo /:/foo:/bar/ 0
-ancestor /foo /foo:/:/bar/ 0
-ancestor /foo /:/bar/:/foo 0
-ancestor /foo/bar "" -1
+ancestor /foo /foo:/bar -1
+ancestor /foo /:/foo:/bar 0
+ancestor /foo /foo:/:/bar 0
+ancestor /foo /:/bar:/foo 0
ancestor /foo/bar / 0
ancestor /foo/bar /fo -1
-ancestor /foo/bar foo -1
ancestor /foo/bar /foo 4
-ancestor /foo/bar /foo/ 4
ancestor /foo/bar /foo/ba -1
ancestor /foo/bar /:/fo 0
ancestor /foo/bar /foo:/foo/ba 4
ancestor /foo/bar /bar -1
-ancestor /foo/bar /bar/ -1
-ancestor /foo/bar /fo: -1
-ancestor /foo/bar :/fo -1
-ancestor /foo/bar /foo:/bar/ 4
-ancestor /foo/bar /:/foo:/bar/ 4
-ancestor /foo/bar /foo:/:/bar/ 4
-ancestor /foo/bar /:/bar/:/fo 0
-ancestor /foo/bar /:/bar/ 0
-ancestor /foo/bar .:/foo/. 4
-ancestor /foo/bar .:/foo/.:.: 4
-ancestor /foo/bar /foo/./:.:/bar 4
-ancestor /foo/bar .:/bar -1
+ancestor /foo/bar /fo -1
+ancestor /foo/bar /foo:/bar 4
+ancestor /foo/bar /:/foo:/bar 4
+ancestor /foo/bar /foo:/:/bar 4
+ancestor /foo/bar /:/bar:/fo 0
+ancestor /foo/bar /:/bar 0
+ancestor /foo/bar /foo 4
+ancestor /foo/bar /foo:/bar 4
+ancestor /foo/bar /bar -1
test_expect_success 'strip_path_suffix' '
test c:/msysgit = $(test-path-utils strip_path_suffix \
c:/msysgit/libexec//git-core libexec/git-core)
'
+
+test_expect_success 'absolute path rejects the empty string' '
+ test_must_fail test-path-utils absolute_path ""
+'
+
+test_expect_success 'real path rejects the empty string' '
+ test_must_fail test-path-utils real_path ""
+'
+
+test_expect_success POSIX 'real path works on absolute paths 1' '
+ nopath="hopefully-absent-path" &&
+ test "/" = "$(test-path-utils real_path "/")" &&
+ test "/$nopath" = "$(test-path-utils real_path "/$nopath")"
+'
+
+test_expect_success 'real path works on absolute paths 2' '
+ nopath="hopefully-absent-path" &&
+ # Find an existing top-level directory for the remaining tests:
+ d=$(pwd -P | sed -e "s|^\([^/]*/[^/]*\)/.*|\1|") &&
+ test "$d" = "$(test-path-utils real_path "$d")" &&
+ test "$d/$nopath" = "$(test-path-utils real_path "$d/$nopath")"
+'
+
+test_expect_success POSIX 'real path removes extra leading slashes' '
+ nopath="hopefully-absent-path" &&
+ test "/" = "$(test-path-utils real_path "///")" &&
+ test "/$nopath" = "$(test-path-utils real_path "///$nopath")" &&
+ # Find an existing top-level directory for the remaining tests:
+ d=$(pwd -P | sed -e "s|^\([^/]*/[^/]*\)/.*|\1|") &&
+ test "$d" = "$(test-path-utils real_path "//$d")" &&
+ test "$d/$nopath" = "$(test-path-utils real_path "//$d/$nopath")"
+'
+
+test_expect_success 'real path removes other extra slashes' '
+ nopath="hopefully-absent-path" &&
+ # Find an existing top-level directory for the remaining tests:
+ d=$(pwd -P | sed -e "s|^\([^/]*/[^/]*\)/.*|\1|") &&
+ test "$d" = "$(test-path-utils real_path "$d///")" &&
+ test "$d/$nopath" = "$(test-path-utils real_path "$d///$nopath")"
+'
+
+test_expect_success SYMLINKS 'real path works on symlinks' '
+ mkdir first &&
+ ln -s ../.git first/.git &&
+ mkdir second &&
+ ln -s ../first second/other &&
+ mkdir third &&
+ dir="$(cd .git; pwd -P)" &&
+ dir2=third/../second/other/.git &&
+ test "$dir" = "$(test-path-utils real_path $dir2)" &&
+ file="$dir"/index &&
+ test "$file" = "$(test-path-utils real_path $dir2/index)" &&
+ basename=blub &&
+ test "$dir/$basename" = "$(cd .git && test-path-utils real_path "$basename")" &&
+ ln -s ../first/file .git/syml &&
+ sym="$(cd first; pwd -P)"/file &&
+ test "$sym" = "$(test-path-utils real_path "$dir2/syml")"
+'
+
+test_expect_success SYMLINKS 'prefix_path works with absolute paths to work tree symlinks' '
+ ln -s target symlink &&
+ test "$(test-path-utils prefix_path prefix "$(pwd)/symlink")" = "symlink"
+'
+
+test_expect_success 'prefix_path works with only absolute path to work tree' '
+ echo "" >expected &&
+ test-path-utils prefix_path prefix "$(pwd)" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'prefix_path rejects absolute path to dir with same beginning as work tree' '
+ test_must_fail test-path-utils prefix_path prefix "$(pwd)a"
+'
+
+test_expect_success SYMLINKS 'prefix_path works with absolute path to a symlink to work tree having same beginning as work tree' '
+ git init repo &&
+ ln -s repo repolink &&
+ test "a" = "$(cd repo && test-path-utils prefix_path prefix "$(pwd)/../repolink/a")"
+'
+
+relative_path /foo/a/b/c/ /foo/a/b/ c/
+relative_path /foo/a/b/c/ /foo/a/b c/
+relative_path /foo/a//b//c/ ///foo/a/b// c/ POSIX
+relative_path /foo/a/b /foo/a/b ./
+relative_path /foo/a/b/ /foo/a/b ./
+relative_path /foo/a /foo/a/b ../
+relative_path / /foo/a/b/ ../../../
+relative_path /foo/a/c /foo/a/b/ ../c
+relative_path /foo/a/c /foo/a/b ../c
+relative_path /foo/x/y /foo/a/b/ ../../x/y
+relative_path /foo/a/b "<empty>" /foo/a/b
+relative_path /foo/a/b "<null>" /foo/a/b
+relative_path foo/a/b/c/ foo/a/b/ c/
+relative_path foo/a/b/c/ foo/a/b c/
+relative_path foo/a/b//c foo/a//b c
+relative_path foo/a/b/ foo/a/b/ ./
+relative_path foo/a/b/ foo/a/b ./
+relative_path foo/a foo/a/b ../
+relative_path foo/x/y foo/a/b ../../x/y
+relative_path foo/a/c foo/a/b ../c
+relative_path foo/a/b /foo/x/y foo/a/b
+relative_path /foo/a/b foo/x/y /foo/a/b
+relative_path d:/a/b D:/a/c ../b MINGW
+relative_path C:/a/b D:/a/c C:/a/b MINGW
+relative_path foo/a/b "<empty>" foo/a/b
+relative_path foo/a/b "<null>" foo/a/b
+relative_path "<empty>" /foo/a/b ./
+relative_path "<empty>" "<empty>" ./
+relative_path "<empty>" "<null>" ./
+relative_path "<null>" "<empty>" ./
+relative_path "<null>" "<null>" ./
+relative_path "<null>" /foo/a/b ./
+
+test_git_path A=B info/grafts .git/info/grafts
+test_git_path GIT_GRAFT_FILE=foo info/grafts foo
+test_git_path GIT_GRAFT_FILE=foo info/////grafts foo
+test_git_path GIT_INDEX_FILE=foo index foo
+test_git_path GIT_INDEX_FILE=foo index/foo .git/index/foo
+test_git_path GIT_INDEX_FILE=foo index2 .git/index2
+test_expect_success 'setup fake objects directory foo' 'mkdir foo'
+test_git_path GIT_OBJECT_DIRECTORY=foo objects foo
+test_git_path GIT_OBJECT_DIRECTORY=foo objects/foo foo/foo
+test_git_path GIT_OBJECT_DIRECTORY=foo objects2 .git/objects2
+test_expect_success 'setup common repository' 'git --git-dir=bar init'
+test_git_path GIT_COMMON_DIR=bar index .git/index
+test_git_path GIT_COMMON_DIR=bar HEAD .git/HEAD
+test_git_path GIT_COMMON_DIR=bar logs/HEAD .git/logs/HEAD
+test_git_path GIT_COMMON_DIR=bar logs/refs/bisect/foo .git/logs/refs/bisect/foo
+test_git_path GIT_COMMON_DIR=bar logs/refs/bisec/foo bar/logs/refs/bisec/foo
+test_git_path GIT_COMMON_DIR=bar logs/refs/bisec bar/logs/refs/bisec
+test_git_path GIT_COMMON_DIR=bar logs/refs/bisectfoo bar/logs/refs/bisectfoo
+test_git_path GIT_COMMON_DIR=bar objects bar/objects
+test_git_path GIT_COMMON_DIR=bar objects/bar bar/objects/bar
+test_git_path GIT_COMMON_DIR=bar info/exclude bar/info/exclude
+test_git_path GIT_COMMON_DIR=bar info/grafts bar/info/grafts
+test_git_path GIT_COMMON_DIR=bar info/sparse-checkout .git/info/sparse-checkout
+test_git_path GIT_COMMON_DIR=bar info//sparse-checkout .git/info//sparse-checkout
+test_git_path GIT_COMMON_DIR=bar remotes/bar bar/remotes/bar
+test_git_path GIT_COMMON_DIR=bar branches/bar bar/branches/bar
+test_git_path GIT_COMMON_DIR=bar logs/refs/heads/master bar/logs/refs/heads/master
+test_git_path GIT_COMMON_DIR=bar refs/heads/master bar/refs/heads/master
+test_git_path GIT_COMMON_DIR=bar refs/bisect/foo .git/refs/bisect/foo
+test_git_path GIT_COMMON_DIR=bar hooks/me bar/hooks/me
+test_git_path GIT_COMMON_DIR=bar config bar/config
+test_git_path GIT_COMMON_DIR=bar packed-refs bar/packed-refs
+test_git_path GIT_COMMON_DIR=bar shallow bar/shallow
+
+# In the tests below, the distinction between $PWD and $(pwd) is important:
+# on Windows, $PWD is POSIX style (/c/foo), $(pwd) has drive letter (c:/foo).
+
+test_submodule_relative_url "../" "../foo" "../submodule" "../../submodule"
+test_submodule_relative_url "../" "../foo/bar" "../submodule" "../../foo/submodule"
+test_submodule_relative_url "../" "../foo/submodule" "../submodule" "../../foo/submodule"
+test_submodule_relative_url "../" "./foo" "../submodule" "../submodule"
+test_submodule_relative_url "../" "./foo/bar" "../submodule" "../foo/submodule"
+test_submodule_relative_url "../../../" "../foo/bar" "../sub/a/b/c" "../../../../foo/sub/a/b/c"
+test_submodule_relative_url "../" "$PWD/addtest" "../repo" "$(pwd)/repo"
+test_submodule_relative_url "../" "foo/bar" "../submodule" "../foo/submodule"
+test_submodule_relative_url "../" "foo" "../submodule" "../submodule"
+
+test_submodule_relative_url "(null)" "../foo/bar" "../sub/a/b/c" "../foo/sub/a/b/c"
+test_submodule_relative_url "(null)" "../foo/bar" "../submodule" "../foo/submodule"
+test_submodule_relative_url "(null)" "../foo/submodule" "../submodule" "../foo/submodule"
+test_submodule_relative_url "(null)" "../foo" "../submodule" "../submodule"
+test_submodule_relative_url "(null)" "./foo/bar" "../submodule" "foo/submodule"
+test_submodule_relative_url "(null)" "./foo" "../submodule" "submodule"
+test_submodule_relative_url "(null)" "//somewhere else/repo" "../subrepo" "//somewhere else/subrepo"
+test_submodule_relative_url "(null)" "$PWD/subsuper_update_r" "../subsubsuper_update_r" "$(pwd)/subsubsuper_update_r"
+test_submodule_relative_url "(null)" "$PWD/super_update_r2" "../subsuper_update_r" "$(pwd)/subsuper_update_r"
+test_submodule_relative_url "(null)" "$PWD/." "../." "$(pwd)/."
+test_submodule_relative_url "(null)" "$PWD" "./." "$(pwd)/."
+test_submodule_relative_url "(null)" "$PWD/addtest" "../repo" "$(pwd)/repo"
+test_submodule_relative_url "(null)" "$PWD" "./å äö" "$(pwd)/å äö"
+test_submodule_relative_url "(null)" "$PWD/." "../submodule" "$(pwd)/submodule"
+test_submodule_relative_url "(null)" "$PWD/submodule" "../submodule" "$(pwd)/submodule"
+test_submodule_relative_url "(null)" "$PWD/home2/../remote" "../bundle1" "$(pwd)/home2/../bundle1"
+test_submodule_relative_url "(null)" "$PWD/submodule_update_repo" "./." "$(pwd)/submodule_update_repo/."
+test_submodule_relative_url "(null)" "file:///tmp/repo" "../subrepo" "file:///tmp/subrepo"
+test_submodule_relative_url "(null)" "foo/bar" "../submodule" "foo/submodule"
+test_submodule_relative_url "(null)" "foo" "../submodule" "submodule"
+test_submodule_relative_url "(null)" "helper:://hostname/repo" "../subrepo" "helper:://hostname/subrepo"
+test_submodule_relative_url "(null)" "ssh://hostname/repo" "../subrepo" "ssh://hostname/subrepo"
+test_submodule_relative_url "(null)" "ssh://hostname:22/repo" "../subrepo" "ssh://hostname:22/subrepo"
+test_submodule_relative_url "(null)" "user@host:path/to/repo" "../subrepo" "user@host:path/to/subrepo"
+test_submodule_relative_url "(null)" "user@host:repo" "../subrepo" "user@host:subrepo"
+
test_done
diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh
index 10b26e4..12228b4 100755
--- a/t/t0061-run-command.sh
+++ b/t/t0061-run-command.sh
@@ -7,8 +7,97 @@ test_description='Test run command'
. ./test-lib.sh
+cat >hello-script <<-EOF
+ #!$SHELL_PATH
+ cat hello-script
+EOF
+>empty
+
test_expect_success 'start_command reports ENOENT' '
test-run-command start-command-ENOENT ./does-not-exist
'
+test_expect_success 'run_command can run a command' '
+ cat hello-script >hello.sh &&
+ chmod +x hello.sh &&
+ test-run-command run-command ./hello.sh >actual 2>err &&
+
+ test_cmp hello-script actual &&
+ test_cmp empty err
+'
+
+test_expect_success POSIXPERM 'run_command reports EACCES' '
+ cat hello-script >hello.sh &&
+ chmod -x hello.sh &&
+ test_must_fail test-run-command run-command ./hello.sh 2>err &&
+
+ grep "fatal: cannot exec.*hello.sh" err
+'
+
+test_expect_success POSIXPERM,SANITY 'unreadable directory in PATH' '
+ mkdir local-command &&
+ test_when_finished "chmod u+rwx local-command && rm -fr local-command" &&
+ git config alias.nitfol "!echo frotz" &&
+ chmod a-rx local-command &&
+ (
+ PATH=./local-command:$PATH &&
+ git nitfol >actual
+ ) &&
+ echo frotz >expect &&
+ test_cmp expect actual
+'
+
+cat >expect <<-EOF
+preloaded output of a child
+Hello
+World
+preloaded output of a child
+Hello
+World
+preloaded output of a child
+Hello
+World
+preloaded output of a child
+Hello
+World
+EOF
+
+test_expect_success 'run_command runs in parallel with more jobs available than tasks' '
+ test-run-command run-command-parallel 5 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'run_command runs in parallel with as many jobs as tasks' '
+ test-run-command run-command-parallel 4 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'run_command runs in parallel with more tasks than jobs available' '
+ test-run-command run-command-parallel 3 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<-EOF
+preloaded output of a child
+asking for a quick stop
+preloaded output of a child
+asking for a quick stop
+preloaded output of a child
+asking for a quick stop
+EOF
+
+test_expect_success 'run_command is asked to abort gracefully' '
+ test-run-command run-command-abort 3 false 2>actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<-EOF
+no further jobs available
+EOF
+
+test_expect_success 'run_command outputs ' '
+ test-run-command run-command-no-jobs 3 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh
new file mode 100755
index 0000000..113c728
--- /dev/null
+++ b/t/t0062-revision-walking.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Heiko Voigt
+#
+
+test_description='Test revision walking api'
+
+. ./test-lib.sh
+
+cat >run_twice_expected <<-EOF
+1st
+ > add b
+ > add a
+2nd
+ > add b
+ > add a
+EOF
+
+test_expect_success 'setup' '
+ echo a > a &&
+ git add a &&
+ git commit -m "add a" &&
+ echo b > b &&
+ git add b &&
+ git commit -m "add b"
+'
+
+test_expect_success 'revision walking can be done twice' '
+ test-revision-walking run-twice >run_twice_actual &&
+ test_cmp run_twice_expected run_twice_actual
+'
+
+test_done
diff --git a/t/t0063-string-list.sh b/t/t0063-string-list.sh
new file mode 100755
index 0000000..dbfc05e
--- /dev/null
+++ b/t/t0063-string-list.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Michael Haggerty
+#
+
+test_description='Test string list functionality'
+
+. ./test-lib.sh
+
+test_split () {
+ cat >expected &&
+ test_expect_success "split $1 at $2, max $3" "
+ test-string-list split '$1' '$2' '$3' >actual &&
+ test_cmp expected actual &&
+ test-string-list split_in_place '$1' '$2' '$3' >actual &&
+ test_cmp expected actual
+ "
+}
+
+test_split "foo:bar:baz" ":" "-1" <<EOF
+3
+[0]: "foo"
+[1]: "bar"
+[2]: "baz"
+EOF
+
+test_split "foo:bar:baz" ":" "0" <<EOF
+1
+[0]: "foo:bar:baz"
+EOF
+
+test_split "foo:bar:baz" ":" "1" <<EOF
+2
+[0]: "foo"
+[1]: "bar:baz"
+EOF
+
+test_split "foo:bar:baz" ":" "2" <<EOF
+3
+[0]: "foo"
+[1]: "bar"
+[2]: "baz"
+EOF
+
+test_split "foo:bar:" ":" "-1" <<EOF
+3
+[0]: "foo"
+[1]: "bar"
+[2]: ""
+EOF
+
+test_split "" ":" "-1" <<EOF
+1
+[0]: ""
+EOF
+
+test_split ":" ":" "-1" <<EOF
+2
+[0]: ""
+[1]: ""
+EOF
+
+test_expect_success "test filter_string_list" '
+ test "x-" = "x$(test-string-list filter - y)" &&
+ test "x-" = "x$(test-string-list filter no y)" &&
+ test yes = "$(test-string-list filter yes y)" &&
+ test yes = "$(test-string-list filter no:yes y)" &&
+ test yes = "$(test-string-list filter yes:no y)" &&
+ test y1:y2 = "$(test-string-list filter y1:y2 y)" &&
+ test y2:y1 = "$(test-string-list filter y2:y1 y)" &&
+ test "x-" = "x$(test-string-list filter x1:x2 y)"
+'
+
+test_expect_success "test remove_duplicates" '
+ test "x-" = "x$(test-string-list remove_duplicates -)" &&
+ test "x" = "x$(test-string-list remove_duplicates "")" &&
+ test a = "$(test-string-list remove_duplicates a)" &&
+ test a = "$(test-string-list remove_duplicates a:a)" &&
+ test a = "$(test-string-list remove_duplicates a:a:a:a:a)" &&
+ test a:b = "$(test-string-list remove_duplicates a:b)" &&
+ test a:b = "$(test-string-list remove_duplicates a:a:b)" &&
+ test a:b = "$(test-string-list remove_duplicates a:b:b)" &&
+ test a:b:c = "$(test-string-list remove_duplicates a:b:c)" &&
+ test a:b:c = "$(test-string-list remove_duplicates a:a:b:c)" &&
+ test a:b:c = "$(test-string-list remove_duplicates a:b:b:c)" &&
+ test a:b:c = "$(test-string-list remove_duplicates a:b:c:c)" &&
+ test a:b:c = "$(test-string-list remove_duplicates a:a:b:b:c:c)" &&
+ test a:b:c = "$(test-string-list remove_duplicates a:a:a:b:b:b:c:c:c)"
+'
+
+test_done
diff --git a/t/t0064-sha1-array.sh b/t/t0064-sha1-array.sh
new file mode 100755
index 0000000..50b31ff
--- /dev/null
+++ b/t/t0064-sha1-array.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+test_description='basic tests for the SHA1 array implementation'
+. ./test-lib.sh
+
+echo20 () {
+ prefix="${1:+$1 }"
+ shift
+ while test $# -gt 0
+ do
+ echo "$prefix$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1"
+ shift
+ done
+}
+
+test_expect_success 'ordered enumeration' '
+ echo20 "" 44 55 88 aa >expect &&
+ {
+ echo20 append 88 44 aa 55 &&
+ echo for_each_unique
+ } | test-sha1-array >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'ordered enumeration with duplicate suppression' '
+ echo20 "" 44 55 88 aa >expect &&
+ {
+ echo20 append 88 44 aa 55 &&
+ echo20 append 88 44 aa 55 &&
+ echo for_each_unique
+ } | test-sha1-array >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'lookup' '
+ {
+ echo20 append 88 44 aa 55 &&
+ echo20 lookup 55
+ } | test-sha1-array >actual &&
+ n=$(cat actual) &&
+ test "$n" -eq 1
+'
+
+test_expect_success 'lookup non-existing entry' '
+ {
+ echo20 append 88 44 aa 55 &&
+ echo20 lookup 33
+ } | test-sha1-array >actual &&
+ n=$(cat actual) &&
+ test "$n" -lt 0
+'
+
+test_expect_success 'lookup with duplicates' '
+ {
+ echo20 append 88 44 aa 55 &&
+ echo20 append 88 44 aa 55 &&
+ echo20 lookup 55
+ } | test-sha1-array >actual &&
+ n=$(cat actual) &&
+ test "$n" -ge 2 &&
+ test "$n" -le 3
+'
+
+test_expect_success 'lookup non-existing entry with duplicates' '
+ {
+ echo20 append 88 44 aa 55 &&
+ echo20 append 88 44 aa 55 &&
+ echo20 lookup 66
+ } | test-sha1-array >actual &&
+ n=$(cat actual) &&
+ test "$n" -lt 0
+'
+
+test_expect_success 'lookup with almost duplicate values' '
+ {
+ echo "append 5555555555555555555555555555555555555555" &&
+ echo "append 555555555555555555555555555555555555555f" &&
+ echo20 lookup 55
+ } | test-sha1-array >actual &&
+ n=$(cat actual) &&
+ test "$n" -eq 0
+'
+
+test_expect_success 'lookup with single duplicate value' '
+ {
+ echo20 append 55 55 &&
+ echo20 lookup 55
+ } | test-sha1-array >actual &&
+ n=$(cat actual) &&
+ test "$n" -ge 0 &&
+ test "$n" -le 1
+'
+
+test_done
diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh
index 680d7d6..5ed69a6 100755
--- a/t/t0070-fundamental.sh
+++ b/t/t0070-fundamental.sh
@@ -12,4 +12,26 @@ test_expect_success 'character classes (isspace, isalpha etc.)' '
test-ctype
'
+test_expect_success 'mktemp to nonexistent directory prints filename' '
+ test_must_fail test-mktemp doesnotexist/testXXXXXX 2>err &&
+ grep "doesnotexist/test" err
+'
+
+test_expect_success POSIXPERM,SANITY 'mktemp to unwritable directory prints filename' '
+ mkdir cannotwrite &&
+ chmod -w cannotwrite &&
+ test_when_finished "chmod +w cannotwrite" &&
+ test_must_fail test-mktemp cannotwrite/testXXXXXX 2>err &&
+ grep "cannotwrite/test" err
+'
+
+test_expect_success 'git_mkstemps_mode does not fail if fd 0 is not open' '
+ git commit --allow-empty -m message <&-
+'
+
+test_expect_success 'check for a bug in the regex routines' '
+ # if this test fails, re-build git with NO_REGEX=1
+ test-regex
+'
+
test_done
diff --git a/t/t0081-line-buffer.sh b/t/t0081-line-buffer.sh
new file mode 100755
index 0000000..ce92e6a
--- /dev/null
+++ b/t/t0081-line-buffer.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+test_description="Test the svn importer's input handling routines.
+
+These tests provide some simple checks that the line_buffer API
+behaves as advertised.
+
+While at it, check that input of newlines and null bytes are handled
+correctly.
+"
+. ./test-lib.sh
+
+test_expect_success 'hello world' '
+ echo ">HELLO" >expect &&
+ test-line-buffer <<-\EOF >actual &&
+ binary 6
+ HELLO
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success '0-length read, send along greeting' '
+ echo ">HELLO" >expect &&
+ test-line-buffer <<-\EOF >actual &&
+ binary 0
+ copy 6
+ HELLO
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success !MINGW 'read from file descriptor' '
+ rm -f input &&
+ echo hello >expect &&
+ echo hello >input &&
+ echo copy 6 |
+ test-line-buffer "&4" 4<input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'skip, copy null byte' '
+ echo Q | q_to_nul >expect &&
+ q_to_nul <<-\EOF | test-line-buffer >actual &&
+ skip 2
+ Q
+ copy 2
+ Q
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'read null byte' '
+ echo ">QhelloQ" | q_to_nul >expect &&
+ q_to_nul <<-\EOF | test-line-buffer >actual &&
+ binary 8
+ QhelloQ
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'long reads are truncated' '
+ echo ">foo" >expect &&
+ test-line-buffer <<-\EOF >actual &&
+ binary 5
+ foo
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'long copies are truncated' '
+ printf "%s\n" ">" foo >expect &&
+ test-line-buffer <<-\EOF >actual &&
+ binary 1
+
+ copy 5
+ foo
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'long binary reads are truncated' '
+ echo ">foo" >expect &&
+ test-line-buffer <<-\EOF >actual &&
+ binary 5
+ foo
+ EOF
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh
new file mode 100755
index 0000000..adfd4f0
--- /dev/null
+++ b/t/t0090-cache-tree.sh
@@ -0,0 +1,255 @@
+#!/bin/sh
+
+test_description="Test whether cache-tree is properly updated
+
+Tests whether various commands properly update and/or rewrite the
+cache-tree extension.
+"
+ . ./test-lib.sh
+
+cmp_cache_tree () {
+ test-dump-cache-tree | sed -e '/#(ref)/d' >actual &&
+ sed "s/$_x40/SHA/" <actual >filtered &&
+ test_cmp "$1" filtered
+}
+
+# We don't bother with actually checking the SHA1:
+# test-dump-cache-tree already verifies that all existing data is
+# correct.
+generate_expected_cache_tree_rec () {
+ dir="$1${1:+/}" &&
+ parent="$2" &&
+ # ls-files might have foo/bar, foo/bar/baz, and foo/bar/quux
+ # We want to count only foo because it's the only direct child
+ subtrees=$(git ls-files|grep /|cut -d / -f 1|uniq) &&
+ subtree_count=$(echo "$subtrees"|awk -v c=0 '$1 != "" {++c} END {print c}') &&
+ entries=$(git ls-files|wc -l) &&
+ printf "SHA $dir (%d entries, %d subtrees)\n" "$entries" "$subtree_count" &&
+ for subtree in $subtrees
+ do
+ cd "$subtree"
+ generate_expected_cache_tree_rec "$dir$subtree" "$dir" || return 1
+ cd ..
+ done &&
+ dir=$parent
+}
+
+generate_expected_cache_tree () {
+ (
+ generate_expected_cache_tree_rec
+ )
+}
+
+test_cache_tree () {
+ generate_expected_cache_tree >expect &&
+ cmp_cache_tree expect
+}
+
+test_invalid_cache_tree () {
+ printf "invalid %s ()\n" "" "$@" >expect &&
+ test-dump-cache-tree |
+ sed -n -e "s/[0-9]* subtrees//" -e '/#(ref)/d' -e '/^invalid /p' >actual &&
+ test_cmp expect actual
+}
+
+test_no_cache_tree () {
+ : >expect &&
+ cmp_cache_tree expect
+}
+
+test_expect_success 'initial commit has cache-tree' '
+ test_commit foo &&
+ test_cache_tree
+'
+
+test_expect_success 'read-tree HEAD establishes cache-tree' '
+ git read-tree HEAD &&
+ test_cache_tree
+'
+
+test_expect_success 'git-add invalidates cache-tree' '
+ test_when_finished "git reset --hard; git read-tree HEAD" &&
+ echo "I changed this file" >foo &&
+ git add foo &&
+ test_invalid_cache_tree
+'
+
+test_expect_success 'git-add in subdir invalidates cache-tree' '
+ test_when_finished "git reset --hard; git read-tree HEAD" &&
+ mkdir dirx &&
+ echo "I changed this file" >dirx/foo &&
+ git add dirx/foo &&
+ test_invalid_cache_tree
+'
+
+cat >before <<\EOF
+SHA (3 entries, 2 subtrees)
+SHA dir1/ (1 entries, 0 subtrees)
+SHA dir2/ (1 entries, 0 subtrees)
+EOF
+
+cat >expect <<\EOF
+invalid (2 subtrees)
+invalid dir1/ (0 subtrees)
+SHA dir2/ (1 entries, 0 subtrees)
+EOF
+
+test_expect_success 'git-add in subdir does not invalidate sibling cache-tree' '
+ git tag no-children &&
+ test_when_finished "git reset --hard no-children; git read-tree HEAD" &&
+ mkdir dir1 dir2 &&
+ test_commit dir1/a &&
+ test_commit dir2/b &&
+ echo "I changed this file" >dir1/a &&
+ cmp_cache_tree before &&
+ echo "I changed this file" >dir1/a &&
+ git add dir1/a &&
+ cmp_cache_tree expect
+'
+
+test_expect_success 'update-index invalidates cache-tree' '
+ test_when_finished "git reset --hard; git read-tree HEAD" &&
+ echo "I changed this file" >foo &&
+ git update-index --add foo &&
+ test_invalid_cache_tree
+'
+
+test_expect_success 'write-tree establishes cache-tree' '
+ test-scrap-cache-tree &&
+ git write-tree &&
+ test_cache_tree
+'
+
+test_expect_success 'test-scrap-cache-tree works' '
+ git read-tree HEAD &&
+ test-scrap-cache-tree &&
+ test_no_cache_tree
+'
+
+test_expect_success 'second commit has cache-tree' '
+ test_commit bar &&
+ test_cache_tree
+'
+
+test_expect_success PERL 'commit --interactive gives cache-tree on partial commit' '
+ cat <<-\EOT >foo.c &&
+ int foo()
+ {
+ return 42;
+ }
+ int bar()
+ {
+ return 42;
+ }
+ EOT
+ git add foo.c &&
+ test_invalid_cache_tree &&
+ git commit -m "add a file" &&
+ test_cache_tree &&
+ cat <<-\EOT >foo.c &&
+ int foo()
+ {
+ return 43;
+ }
+ int bar()
+ {
+ return 44;
+ }
+ EOT
+ (echo p; echo 1; echo; echo s; echo n; echo y; echo q) |
+ git commit --interactive -m foo &&
+ test_cache_tree
+'
+
+test_expect_success 'commit in child dir has cache-tree' '
+ mkdir dir &&
+ >dir/child.t &&
+ git add dir/child.t &&
+ git commit -m dir/child.t &&
+ test_cache_tree
+'
+
+test_expect_success 'reset --hard gives cache-tree' '
+ test-scrap-cache-tree &&
+ git reset --hard &&
+ test_cache_tree
+'
+
+test_expect_success 'reset --hard without index gives cache-tree' '
+ rm -f .git/index &&
+ git reset --hard &&
+ test_cache_tree
+'
+
+test_expect_success 'checkout gives cache-tree' '
+ git tag current &&
+ git checkout HEAD^ &&
+ test_cache_tree
+'
+
+test_expect_success 'checkout -b gives cache-tree' '
+ git checkout current &&
+ git checkout -b prev HEAD^ &&
+ test_cache_tree
+'
+
+test_expect_success 'checkout -B gives cache-tree' '
+ git checkout current &&
+ git checkout -B prev HEAD^ &&
+ test_cache_tree
+'
+
+test_expect_success 'merge --ff-only maintains cache-tree' '
+ git checkout current &&
+ git checkout -b changes &&
+ test_commit llamas &&
+ test_commit pachyderm &&
+ test_cache_tree &&
+ git checkout current &&
+ test_cache_tree &&
+ git merge --ff-only changes &&
+ test_cache_tree
+'
+
+test_expect_success 'merge maintains cache-tree' '
+ git checkout current &&
+ git checkout -b changes2 &&
+ test_commit alpacas &&
+ test_cache_tree &&
+ git checkout current &&
+ test_commit struthio &&
+ test_cache_tree &&
+ git merge changes2 &&
+ test_cache_tree
+'
+
+test_expect_success 'partial commit gives cache-tree' '
+ git checkout -b partial no-children &&
+ test_commit one &&
+ test_commit two &&
+ echo "some change" >one.t &&
+ git add one.t &&
+ echo "some other change" >two.t &&
+ git commit two.t -m partial &&
+ test_cache_tree
+'
+
+test_expect_success 'no phantom error when switching trees' '
+ mkdir newdir &&
+ >newdir/one &&
+ git add newdir/one &&
+ git checkout 2>errors &&
+ ! test -s errors
+'
+
+test_expect_success 'switching trees does not invalidate shared index' '
+ git update-index --split-index &&
+ >split &&
+ git add split &&
+ test-dump-split-index .git/index | grep -v ^own >before &&
+ git commit -m "as-is" &&
+ test-dump-split-index .git/index | grep -v ^own >after &&
+ test_cmp before after
+'
+
+test_done
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
index 315b9b3..e0a6940 100755
--- a/t/t0100-previous.sh
+++ b/t/t0100-previous.sh
@@ -27,6 +27,7 @@ test_expect_success 'merge @{-1}' '
test_commit B &&
git checkout A &&
test_commit C &&
+ test_commit D &&
git branch -f master B &&
git branch -f other &&
git checkout other &&
@@ -35,14 +36,24 @@ test_expect_success 'merge @{-1}' '
git cat-file commit HEAD | grep "Merge branch '\''other'\''"
'
-test_expect_success 'merge @{-1} when there is not enough switches yet' '
+test_expect_success 'merge @{-1}~1' '
+ git checkout master &&
+ git reset --hard B &&
+ git checkout other &&
+ git checkout master &&
+ git merge @{-1}~1 &&
+ git cat-file commit HEAD >actual &&
+ grep "Merge branch '\''other'\''" actual
+'
+
+test_expect_success 'merge @{-100} before checking out that many branches yet' '
git reflog expire --expire=now &&
git checkout -f master &&
git reset --hard B &&
git branch -f other C &&
git checkout other &&
git checkout master &&
- test_must_fail git merge @{-12}
+ test_must_fail git merge @{-100}
'
test_done
diff --git a/t/t0110-urlmatch-normalization.sh b/t/t0110-urlmatch-normalization.sh
new file mode 100755
index 0000000..410d576
--- /dev/null
+++ b/t/t0110-urlmatch-normalization.sh
@@ -0,0 +1,180 @@
+#!/bin/sh
+
+test_description='urlmatch URL normalization'
+. ./test-lib.sh
+
+# The base name of the test url files
+tu="$TEST_DIRECTORY/t0110/url"
+
+# Note that only file: URLs should be allowed without a host
+
+test_expect_success 'url scheme' '
+ ! test-urlmatch-normalization "" &&
+ ! test-urlmatch-normalization "_" &&
+ ! test-urlmatch-normalization "scheme" &&
+ ! test-urlmatch-normalization "scheme:" &&
+ ! test-urlmatch-normalization "scheme:/" &&
+ ! test-urlmatch-normalization "scheme://" &&
+ ! test-urlmatch-normalization "file" &&
+ ! test-urlmatch-normalization "file:" &&
+ ! test-urlmatch-normalization "file:/" &&
+ test-urlmatch-normalization "file://" &&
+ ! test-urlmatch-normalization "://acme.co" &&
+ ! test-urlmatch-normalization "x_test://acme.co" &&
+ ! test-urlmatch-normalization "-test://acme.co" &&
+ ! test-urlmatch-normalization "0test://acme.co" &&
+ ! test-urlmatch-normalization "+test://acme.co" &&
+ ! test-urlmatch-normalization ".test://acme.co" &&
+ ! test-urlmatch-normalization "schem%6e://" &&
+ test-urlmatch-normalization "x-Test+v1.0://acme.co" &&
+ test "$(test-urlmatch-normalization -p "AbCdeF://x.Y")" = "abcdef://x.y/"
+'
+
+test_expect_success 'url authority' '
+ ! test-urlmatch-normalization "scheme://user:pass@" &&
+ ! test-urlmatch-normalization "scheme://?" &&
+ ! test-urlmatch-normalization "scheme://#" &&
+ ! test-urlmatch-normalization "scheme:///" &&
+ ! test-urlmatch-normalization "scheme://:" &&
+ ! test-urlmatch-normalization "scheme://:555" &&
+ test-urlmatch-normalization "file://user:pass@" &&
+ test-urlmatch-normalization "file://?" &&
+ test-urlmatch-normalization "file://#" &&
+ test-urlmatch-normalization "file:///" &&
+ test-urlmatch-normalization "file://:" &&
+ ! test-urlmatch-normalization "file://:555" &&
+ test-urlmatch-normalization "scheme://user:pass@host" &&
+ test-urlmatch-normalization "scheme://@host" &&
+ test-urlmatch-normalization "scheme://%00@host" &&
+ ! test-urlmatch-normalization "scheme://%%@host" &&
+ ! test-urlmatch-normalization "scheme://host_" &&
+ test-urlmatch-normalization "scheme://user:pass@host/" &&
+ test-urlmatch-normalization "scheme://@host/" &&
+ test-urlmatch-normalization "scheme://host/" &&
+ test-urlmatch-normalization "scheme://host?x" &&
+ test-urlmatch-normalization "scheme://host#x" &&
+ test-urlmatch-normalization "scheme://host/@" &&
+ test-urlmatch-normalization "scheme://host?@x" &&
+ test-urlmatch-normalization "scheme://host#@x" &&
+ test-urlmatch-normalization "scheme://[::1]" &&
+ test-urlmatch-normalization "scheme://[::1]/" &&
+ ! test-urlmatch-normalization "scheme://hos%41/" &&
+ test-urlmatch-normalization "scheme://[invalid....:/" &&
+ test-urlmatch-normalization "scheme://invalid....:]/" &&
+ ! test-urlmatch-normalization "scheme://invalid....:[/" &&
+ ! test-urlmatch-normalization "scheme://invalid....:["
+'
+
+test_expect_success 'url port checks' '
+ test-urlmatch-normalization "xyz://q@some.host:" &&
+ test-urlmatch-normalization "xyz://q@some.host:456/" &&
+ ! test-urlmatch-normalization "xyz://q@some.host:0" &&
+ ! test-urlmatch-normalization "xyz://q@some.host:0000000" &&
+ test-urlmatch-normalization "xyz://q@some.host:0000001?" &&
+ test-urlmatch-normalization "xyz://q@some.host:065535#" &&
+ test-urlmatch-normalization "xyz://q@some.host:65535" &&
+ ! test-urlmatch-normalization "xyz://q@some.host:65536" &&
+ ! test-urlmatch-normalization "xyz://q@some.host:99999" &&
+ ! test-urlmatch-normalization "xyz://q@some.host:100000" &&
+ ! test-urlmatch-normalization "xyz://q@some.host:100001" &&
+ test-urlmatch-normalization "http://q@some.host:80" &&
+ test-urlmatch-normalization "https://q@some.host:443" &&
+ test-urlmatch-normalization "http://q@some.host:80/" &&
+ test-urlmatch-normalization "https://q@some.host:443?" &&
+ ! test-urlmatch-normalization "http://q@:8008" &&
+ ! test-urlmatch-normalization "http://:8080" &&
+ ! test-urlmatch-normalization "http://:" &&
+ test-urlmatch-normalization "xyz://q@some.host:456/" &&
+ test-urlmatch-normalization "xyz://[::1]:456/" &&
+ test-urlmatch-normalization "xyz://[::1]:/" &&
+ ! test-urlmatch-normalization "xyz://[::1]:000/" &&
+ ! test-urlmatch-normalization "xyz://[::1]:0%300/" &&
+ ! test-urlmatch-normalization "xyz://[::1]:0x80/" &&
+ ! test-urlmatch-normalization "xyz://[::1]:4294967297/" &&
+ ! test-urlmatch-normalization "xyz://[::1]:030f/"
+'
+
+test_expect_success 'url port normalization' '
+ test "$(test-urlmatch-normalization -p "http://x:800")" = "http://x:800/" &&
+ test "$(test-urlmatch-normalization -p "http://x:0800")" = "http://x:800/" &&
+ test "$(test-urlmatch-normalization -p "http://x:00000800")" = "http://x:800/" &&
+ test "$(test-urlmatch-normalization -p "http://x:065535")" = "http://x:65535/" &&
+ test "$(test-urlmatch-normalization -p "http://x:1")" = "http://x:1/" &&
+ test "$(test-urlmatch-normalization -p "http://x:80")" = "http://x/" &&
+ test "$(test-urlmatch-normalization -p "http://x:080")" = "http://x/" &&
+ test "$(test-urlmatch-normalization -p "http://x:000000080")" = "http://x/" &&
+ test "$(test-urlmatch-normalization -p "https://x:443")" = "https://x/" &&
+ test "$(test-urlmatch-normalization -p "https://x:0443")" = "https://x/" &&
+ test "$(test-urlmatch-normalization -p "https://x:000000443")" = "https://x/"
+'
+
+test_expect_success 'url general escapes' '
+ ! test-urlmatch-normalization "http://x.y?%fg" &&
+ test "$(test-urlmatch-normalization -p "X://W/%7e%41^%3a")" = "x://w/~A%5E%3A" &&
+ test "$(test-urlmatch-normalization -p "X://W/:/?#[]@")" = "x://w/:/?#[]@" &&
+ test "$(test-urlmatch-normalization -p "X://W/$&()*+,;=")" = "x://w/$&()*+,;=" &&
+ test "$(test-urlmatch-normalization -p "X://W/'\''")" = "x://w/'\''" &&
+ test "$(test-urlmatch-normalization -p "X://W?'\!'")" = "x://w/?'\!'"
+'
+
+test_expect_success !MINGW 'url high-bit escapes' '
+ test "$(test-urlmatch-normalization -p "$(cat "$tu-1")")" = "x://q/%01%02%03%04%05%06%07%08%0E%0F%10%11%12" &&
+ test "$(test-urlmatch-normalization -p "$(cat "$tu-2")")" = "x://q/%13%14%15%16%17%18%19%1B%1C%1D%1E%1F%7F" &&
+ test "$(test-urlmatch-normalization -p "$(cat "$tu-3")")" = "x://q/%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F" &&
+ test "$(test-urlmatch-normalization -p "$(cat "$tu-4")")" = "x://q/%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F" &&
+ test "$(test-urlmatch-normalization -p "$(cat "$tu-5")")" = "x://q/%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF" &&
+ test "$(test-urlmatch-normalization -p "$(cat "$tu-6")")" = "x://q/%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF" &&
+ test "$(test-urlmatch-normalization -p "$(cat "$tu-7")")" = "x://q/%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF" &&
+ test "$(test-urlmatch-normalization -p "$(cat "$tu-8")")" = "x://q/%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF" &&
+ test "$(test-urlmatch-normalization -p "$(cat "$tu-9")")" = "x://q/%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF" &&
+ test "$(test-urlmatch-normalization -p "$(cat "$tu-10")")" = "x://q/%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF"
+'
+
+test_expect_success 'url utf-8 escapes' '
+ test "$(test-urlmatch-normalization -p "$(cat "$tu-11")")" = "x://q/%C2%80%DF%BF%E0%A0%80%EF%BF%BD%F0%90%80%80%F0%AF%BF%BD"
+'
+
+test_expect_success 'url username/password escapes' '
+ test "$(test-urlmatch-normalization -p "x://%41%62(^):%70+d@foo")" = "x://Ab(%5E):p+d@foo/"
+'
+
+test_expect_success 'url normalized lengths' '
+ test "$(test-urlmatch-normalization -l "Http://%4d%65:%4d^%70@The.Host")" = 25 &&
+ test "$(test-urlmatch-normalization -l "http://%41:%42@x.y/%61/")" = 17 &&
+ test "$(test-urlmatch-normalization -l "http://@x.y/^")" = 15
+'
+
+test_expect_success 'url . and .. segments' '
+ test "$(test-urlmatch-normalization -p "x://y/.")" = "x://y/" &&
+ test "$(test-urlmatch-normalization -p "x://y/./")" = "x://y/" &&
+ test "$(test-urlmatch-normalization -p "x://y/a/.")" = "x://y/a" &&
+ test "$(test-urlmatch-normalization -p "x://y/a/./")" = "x://y/a/" &&
+ test "$(test-urlmatch-normalization -p "x://y/.?")" = "x://y/?" &&
+ test "$(test-urlmatch-normalization -p "x://y/./?")" = "x://y/?" &&
+ test "$(test-urlmatch-normalization -p "x://y/a/.?")" = "x://y/a?" &&
+ test "$(test-urlmatch-normalization -p "x://y/a/./?")" = "x://y/a/?" &&
+ test "$(test-urlmatch-normalization -p "x://y/a/./b/.././../c")" = "x://y/c" &&
+ test "$(test-urlmatch-normalization -p "x://y/a/./b/../.././c/")" = "x://y/c/" &&
+ test "$(test-urlmatch-normalization -p "x://y/a/./b/.././../c/././.././.")" = "x://y/" &&
+ ! test-urlmatch-normalization "x://y/a/./b/.././../c/././.././.." &&
+ test "$(test-urlmatch-normalization -p "x://y/a/./?/././..")" = "x://y/a/?/././.." &&
+ test "$(test-urlmatch-normalization -p "x://y/%2e/")" = "x://y/" &&
+ test "$(test-urlmatch-normalization -p "x://y/%2E/")" = "x://y/" &&
+ test "$(test-urlmatch-normalization -p "x://y/a/%2e./")" = "x://y/" &&
+ test "$(test-urlmatch-normalization -p "x://y/b/.%2E/")" = "x://y/" &&
+ test "$(test-urlmatch-normalization -p "x://y/c/%2e%2E/")" = "x://y/"
+'
+
+# http://@foo specifies an empty user name but does not specify a password
+# http://foo specifies neither a user name nor a password
+# So they should not be equivalent
+test_expect_success 'url equivalents' '
+ test-urlmatch-normalization "httP://x" "Http://X/" &&
+ test-urlmatch-normalization "Http://%4d%65:%4d^%70@The.Host" "hTTP://Me:%4D^p@the.HOST:80/" &&
+ ! test-urlmatch-normalization "https://@x.y/^" "httpS://x.y:443/^" &&
+ test-urlmatch-normalization "https://@x.y/^" "httpS://@x.y:0443/^" &&
+ test-urlmatch-normalization "https://@x.y/^/../abc" "httpS://@x.y:0443/abc" &&
+ test-urlmatch-normalization "https://@x.y/^/.." "httpS://@x.y:0443/"
+'
+
+test_done
diff --git a/t/t0110/README b/t/t0110/README
new file mode 100644
index 0000000..ad4a50e
--- /dev/null
+++ b/t/t0110/README
@@ -0,0 +1,9 @@
+The url data files in this directory contain URLs with characters
+in the range 0x01-0x1f and 0x7f-0xff to test the proper normalization
+of unprintable characters.
+
+A select few characters in the 0x01-0x1f range are skipped to help
+avoid problems running the test itself.
+
+The urls are in test files in this directory rather than being
+embedded in the test script for portability.
diff --git a/t/t0110/url-1 b/t/t0110/url-1
new file mode 100644
index 0000000..519019c
--- /dev/null
+++ b/t/t0110/url-1
@@ -0,0 +1 @@
+x://q/
diff --git a/t/t0110/url-10 b/t/t0110/url-10
new file mode 100644
index 0000000..b9965de
--- /dev/null
+++ b/t/t0110/url-10
@@ -0,0 +1 @@
+x://q/ðñòóôõö÷øùúûüýþÿ
diff --git a/t/t0110/url-11 b/t/t0110/url-11
new file mode 100644
index 0000000..f0a50f1
--- /dev/null
+++ b/t/t0110/url-11
@@ -0,0 +1 @@
+x://q/€߿ࠀ�ð€€ð¯¿½
diff --git a/t/t0110/url-2 b/t/t0110/url-2
new file mode 100644
index 0000000..43334b0
--- /dev/null
+++ b/t/t0110/url-2
@@ -0,0 +1 @@
+x://q/
diff --git a/t/t0110/url-3 b/t/t0110/url-3
new file mode 100644
index 0000000..7378c7b
--- /dev/null
+++ b/t/t0110/url-3
@@ -0,0 +1 @@
+x://q/€‚ƒ„…†‡ˆ‰Š‹ŒŽ
diff --git a/t/t0110/url-4 b/t/t0110/url-4
new file mode 100644
index 0000000..220b198
--- /dev/null
+++ b/t/t0110/url-4
@@ -0,0 +1 @@
+x://q/‘’“”•–—˜™š›œžŸ
diff --git a/t/t0110/url-5 b/t/t0110/url-5
new file mode 100644
index 0000000..1ccd927
--- /dev/null
+++ b/t/t0110/url-5
@@ -0,0 +1 @@
+x://q/ ¡¢£¤¥¦§¨©ª«¬­®¯
diff --git a/t/t0110/url-6 b/t/t0110/url-6
new file mode 100644
index 0000000..e8283aa
--- /dev/null
+++ b/t/t0110/url-6
@@ -0,0 +1 @@
+x://q/°±²³´µ¶·¸¹º»¼½¾¿
diff --git a/t/t0110/url-7 b/t/t0110/url-7
new file mode 100644
index 0000000..fa7c10b
--- /dev/null
+++ b/t/t0110/url-7
@@ -0,0 +1 @@
+x://q/ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ
diff --git a/t/t0110/url-8 b/t/t0110/url-8
new file mode 100644
index 0000000..79a0ba8
--- /dev/null
+++ b/t/t0110/url-8
@@ -0,0 +1 @@
+x://q/ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß
diff --git a/t/t0110/url-9 b/t/t0110/url-9
new file mode 100644
index 0000000..8b44bec
--- /dev/null
+++ b/t/t0110/url-9
@@ -0,0 +1 @@
+x://q/àáâãäåæçèéêëìíîï
diff --git a/t/t0200-gettext-basic.sh b/t/t0200-gettext-basic.sh
new file mode 100755
index 0000000..8853d8a
--- /dev/null
+++ b/t/t0200-gettext-basic.sh
@@ -0,0 +1,108 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+
+test_description='Gettext support for Git'
+
+. ./lib-gettext.sh
+
+test_expect_success "sanity: \$GIT_INTERNAL_GETTEXT_SH_SCHEME is set (to $GIT_INTERNAL_GETTEXT_SH_SCHEME)" '
+ test -n "$GIT_INTERNAL_GETTEXT_SH_SCHEME"
+'
+
+test_expect_success 'sanity: $TEXTDOMAIN is git' '
+ test $TEXTDOMAIN = "git"
+'
+
+test_expect_success 'xgettext sanity: Perl _() strings are not extracted' '
+ ! grep "A Perl string xgettext will not get" "$GIT_PO_PATH"/is.po
+'
+
+test_expect_success 'xgettext sanity: Comment extraction with --add-comments' '
+ grep "TRANSLATORS: This is a test" "$TEST_DIRECTORY"/t0200/* | wc -l >expect &&
+ grep "TRANSLATORS: This is a test" "$GIT_PO_PATH"/is.po | wc -l >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'xgettext sanity: Comment extraction with --add-comments stops at statements' '
+ ! grep "This is a phony" "$GIT_PO_PATH"/is.po &&
+ ! grep "the above comment" "$GIT_PO_PATH"/is.po
+'
+
+test_expect_success GETTEXT 'sanity: $TEXTDOMAINDIR exists without NO_GETTEXT=YesPlease' '
+ test -d "$TEXTDOMAINDIR" &&
+ test "$TEXTDOMAINDIR" = "$GIT_TEXTDOMAINDIR"
+'
+
+test_expect_success GETTEXT 'sanity: Icelandic locale was compiled' '
+ test -f "$TEXTDOMAINDIR/is/LC_MESSAGES/git.mo"
+'
+
+# TODO: When we have more locales, generalize this to test them
+# all. Maybe we'll need a dir->locale map for that.
+test_expect_success GETTEXT_LOCALE 'sanity: gettext("") metadata is OK' '
+ # Return value may be non-zero
+ LANGUAGE=is LC_ALL="$is_IS_locale" gettext "" >zero-expect &&
+ grep "Project-Id-Version: Git" zero-expect &&
+ grep "Git Mailing List <git@vger.kernel.org>" zero-expect &&
+ grep "Content-Type: text/plain; charset=UTF-8" zero-expect &&
+ grep "Content-Transfer-Encoding: 8bit" zero-expect
+'
+
+test_expect_success GETTEXT_LOCALE 'sanity: gettext(unknown) is passed through' '
+ printf "This is not a translation string" >expect &&
+ gettext "This is not a translation string" >actual &&
+ eval_gettext "This is not a translation string" >actual &&
+ test_cmp expect actual
+'
+
+# xgettext from C
+test_expect_success GETTEXT_LOCALE 'xgettext: C extraction of _() and N_() strings' '
+ printf "TILRAUN: C tilraunastrengur" >expect &&
+ printf "\n" >>expect &&
+ printf "Sjá '\''git help SKIPUN'\'' til að sjá hjálp fyrir tiltekna skipun." >>expect &&
+ LANGUAGE=is LC_ALL="$is_IS_locale" gettext "TEST: A C test string" >actual &&
+ printf "\n" >>actual &&
+ LANGUAGE=is LC_ALL="$is_IS_locale" gettext "See '\''git help COMMAND'\'' for more information on a specific command." >>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GETTEXT_LOCALE 'xgettext: C extraction with %s' '
+ printf "TILRAUN: C tilraunastrengur %%s" >expect &&
+ LANGUAGE=is LC_ALL="$is_IS_locale" gettext "TEST: A C test string %s" >actual &&
+ test_cmp expect actual
+'
+
+# xgettext from Shell
+test_expect_success GETTEXT_LOCALE 'xgettext: Shell extraction' '
+ printf "TILRAUN: Skeljartilraunastrengur" >expect &&
+ LANGUAGE=is LC_ALL="$is_IS_locale" gettext "TEST: A Shell test string" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GETTEXT_LOCALE 'xgettext: Shell extraction with $variable' '
+ printf "TILRAUN: Skeljartilraunastrengur með breytunni a var i able" >x-expect &&
+ LANGUAGE=is LC_ALL="$is_IS_locale" variable="a var i able" eval_gettext "TEST: A Shell test \$variable" >x-actual &&
+ test_cmp x-expect x-actual
+'
+
+# xgettext from Perl
+test_expect_success GETTEXT_LOCALE 'xgettext: Perl extraction' '
+ printf "TILRAUN: Perl tilraunastrengur" >expect &&
+ LANGUAGE=is LC_ALL="$is_IS_locale" gettext "TEST: A Perl test string" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GETTEXT_LOCALE 'xgettext: Perl extraction with %s' '
+ printf "TILRAUN: Perl tilraunastrengur með breytunni %%s" >expect &&
+ LANGUAGE=is LC_ALL="$is_IS_locale" gettext "TEST: A Perl test variable %s" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GETTEXT_LOCALE 'sanity: Some gettext("") data for real locale' '
+ LANGUAGE=is LC_ALL="$is_IS_locale" gettext "" >real-locale &&
+ test -s real-locale
+'
+
+test_done
diff --git a/t/t0200/test.c b/t/t0200/test.c
new file mode 100644
index 0000000..584d45c
--- /dev/null
+++ b/t/t0200/test.c
@@ -0,0 +1,23 @@
+/* This is a phony C program that's only here to test xgettext message extraction */
+
+const char help[] =
+ /* TRANSLATORS: This is a test. You don't need to translate it. */
+ N_("See 'git help COMMAND' for more information on a specific command.");
+
+int main(void)
+{
+ /* TRANSLATORS: This is a test. You don't need to translate it. */
+ puts(_("TEST: A C test string"));
+
+ /* TRANSLATORS: This is a test. You don't need to translate it. */
+ printf(_("TEST: A C test string %s"), "variable");
+
+ /* TRANSLATORS: This is a test. You don't need to translate it. */
+ printf(_("TEST: Hello World!"));
+
+ /* TRANSLATORS: This is a test. You don't need to translate it. */
+ printf(_("TEST: Old English Runes"));
+
+ /* TRANSLATORS: This is a test. You don't need to translate it. */
+ printf(_("TEST: ‘single’ and “double†quotes"));
+}
diff --git a/t/t0200/test.perl b/t/t0200/test.perl
new file mode 100644
index 0000000..36fba34
--- /dev/null
+++ b/t/t0200/test.perl
@@ -0,0 +1,14 @@
+# This is a phony Perl program that's only here to test xgettext
+# message extraction
+
+# so the above comment won't be folded into the next one by xgettext
+1;
+
+# TRANSLATORS: This is a test. You don't need to translate it.
+print __("TEST: A Perl test string");
+
+# TRANSLATORS: This is a test. You don't need to translate it.
+printf __("TEST: A Perl test variable %s"), "moo";
+
+# TRANSLATORS: If you see this, Git has a bug
+print _"TEST: A Perl string xgettext will not get";
diff --git a/t/t0200/test.sh b/t/t0200/test.sh
new file mode 100644
index 0000000..022d607
--- /dev/null
+++ b/t/t0200/test.sh
@@ -0,0 +1,14 @@
+# This is a phony Shell program that's only here to test xgettext
+# message extraction
+
+# so the above comment won't be folded into the next one by xgettext
+echo
+
+# TRANSLATORS: This is a test. You don't need to translate it.
+gettext "TEST: A Shell test string"
+
+# TRANSLATORS: This is a test. You don't need to translate it.
+eval_gettext "TEST: A Shell test \$variable"
+
+# TRANSLATORS: If you see this, Git has a bug
+_("TEST: A Shell string xgettext won't get")
diff --git a/t/t0201-gettext-fallbacks.sh b/t/t0201-gettext-fallbacks.sh
new file mode 100755
index 0000000..90da1c7
--- /dev/null
+++ b/t/t0201-gettext-fallbacks.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+
+test_description='Gettext Shell fallbacks'
+
+GIT_INTERNAL_GETTEXT_TEST_FALLBACKS=YesPlease
+export GIT_INTERNAL_GETTEXT_TEST_FALLBACKS
+
+. ./lib-gettext.sh
+
+test_expect_success "sanity: \$GIT_INTERNAL_GETTEXT_SH_SCHEME is set (to $GIT_INTERNAL_GETTEXT_SH_SCHEME)" '
+ test -n "$GIT_INTERNAL_GETTEXT_SH_SCHEME"
+'
+
+test_expect_success 'sanity: $GIT_INTERNAL_GETTEXT_TEST_FALLBACKS is set' '
+ test -n "$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS"
+'
+
+test_expect_success C_LOCALE_OUTPUT 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME" is fallthrough' '
+ echo fallthrough >expect &&
+ echo $GIT_INTERNAL_GETTEXT_SH_SCHEME >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'gettext: our gettext() fallback has pass-through semantics' '
+ printf "test" >expect &&
+ gettext "test" >actual &&
+ test_i18ncmp expect actual &&
+ printf "test more words" >expect &&
+ gettext "test more words" >actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'eval_gettext: our eval_gettext() fallback has pass-through semantics' '
+ printf "test" >expect &&
+ eval_gettext "test" >actual &&
+ test_i18ncmp expect actual &&
+ printf "test more words" >expect &&
+ eval_gettext "test more words" >actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables' '
+ printf "test YesPlease" >expect &&
+ GIT_INTERNAL_GETTEXT_TEST_FALLBACKS=YesPlease eval_gettext "test \$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS" >actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables with spaces' '
+ cmdline="git am" &&
+ export cmdline &&
+ printf "When you have resolved this problem, run git am --resolved." >expect &&
+ eval_gettext "When you have resolved this problem, run \$cmdline --resolved." >actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables with spaces and quotes' '
+ cmdline="git am" &&
+ export cmdline &&
+ printf "When you have resolved this problem, run \"git am --resolved\"." >expect &&
+ eval_gettext "When you have resolved this problem, run \"\$cmdline --resolved\"." >actual &&
+ test_i18ncmp expect actual
+'
+
+test_done
diff --git a/t/t0202-gettext-perl.sh b/t/t0202-gettext-perl.sh
new file mode 100755
index 0000000..a29d166
--- /dev/null
+++ b/t/t0202-gettext-perl.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+
+test_description='Perl gettext interface (Git::I18N)'
+
+. ./lib-gettext.sh
+
+if ! test_have_prereq PERL; then
+ skip_all='skipping perl interface tests, perl not available'
+ test_done
+fi
+
+perl -MTest::More -e 0 2>/dev/null || {
+ skip_all="Perl Test::More unavailable, skipping test"
+ test_done
+}
+
+# The external test will outputs its own plan
+test_external_has_tap=1
+
+test_external_without_stderr \
+ 'Perl Git::I18N API' \
+ perl "$TEST_DIRECTORY"/t0202/test.pl
+
+test_done
diff --git a/t/t0202/test.pl b/t/t0202/test.pl
new file mode 100755
index 0000000..2c10cb4
--- /dev/null
+++ b/t/t0202/test.pl
@@ -0,0 +1,110 @@
+#!/usr/bin/perl
+use 5.008;
+use lib (split(/:/, $ENV{GITPERLLIB}));
+use strict;
+use warnings;
+use POSIX qw(:locale_h);
+use Test::More tests => 8;
+use Git::I18N;
+
+my $has_gettext_library = $Git::I18N::__HAS_LIBRARY;
+
+ok(1, "Testing Git::I18N with " .
+ ($has_gettext_library
+ ? (defined $Locale::Messages::VERSION
+ ? "Locale::Messages version $Locale::Messages::VERSION"
+ # Versions of Locale::Messages before 1.17 didn't have a
+ # $VERSION variable.
+ : "Locale::Messages version <1.17")
+ : "NO Perl gettext library"));
+ok(1, "Git::I18N is located at $INC{'Git/I18N.pm'}");
+
+{
+ my $exports = @Git::I18N::EXPORT;
+ ok($exports, "sanity: Git::I18N has $exports export(s)");
+}
+is_deeply(\@Git::I18N::EXPORT, \@Git::I18N::EXPORT_OK, "sanity: Git::I18N exports everything by default");
+
+# prototypes
+{
+ # Add prototypes here when modifying the public interface to add
+ # more gettext wrapper functions.
+ my %prototypes = (qw(
+ __ $
+ ));
+ while (my ($sub, $proto) = each %prototypes) {
+ is(prototype(\&{"Git::I18N::$sub"}), $proto, "sanity: $sub has a $proto prototype");
+ }
+}
+
+# Test basic passthrough in the C locale
+{
+ local $ENV{LANGUAGE} = 'C';
+ local $ENV{LC_ALL} = 'C';
+ local $ENV{LANG} = 'C';
+
+ my ($got, $expect) = (('TEST: A Perl test string') x 2);
+
+ is(__($got), $expect, "Passing a string through __() in the C locale works");
+}
+
+# Test a basic message on different locales
+SKIP: {
+ unless ($ENV{GETTEXT_LOCALE}) {
+ # Can't reliably test __() with a non-C locales because the
+ # required locales may not be installed on the system.
+ #
+ # We test for these anyway as part of the shell
+ # tests. Skipping these here will eliminate failures on odd
+ # platforms with incomplete locale data.
+
+ skip "GETTEXT_LOCALE must be set by lib-gettext.sh for exhaustive Git::I18N tests", 2;
+ }
+
+ # The is_IS UTF-8 locale passed from lib-gettext.sh
+ my $is_IS_locale = $ENV{is_IS_locale};
+
+ my $test = sub {
+ my ($got, $expect, $msg, $locale) = @_;
+ # Maybe this system doesn't have the locale we're trying to
+ # test.
+ my $locale_ok = setlocale(LC_ALL, $locale);
+ is(__($got), $expect, "$msg a gettext library + <$locale> locale <$got> turns into <$expect>");
+ };
+
+ my $env_C = sub {
+ $ENV{LANGUAGE} = 'C';
+ $ENV{LC_ALL} = 'C';
+ };
+
+ my $env_is = sub {
+ $ENV{LANGUAGE} = 'is';
+ $ENV{LC_ALL} = $is_IS_locale;
+ };
+
+ # Translation's the same as the original
+ my ($got, $expect) = (('TEST: A Perl test string') x 2);
+
+ if ($has_gettext_library) {
+ {
+ local %ENV; $env_C->();
+ $test->($got, $expect, "With", 'C');
+ }
+
+ {
+ my ($got, $expect) = ($got, 'TILRAUN: Perl tilraunastrengur');
+ local %ENV; $env_is->();
+ $test->($got, $expect, "With", $is_IS_locale);
+ }
+ } else {
+ {
+ local %ENV; $env_C->();
+ $test->($got, $expect, "Without", 'C');
+ }
+
+ {
+ local %ENV; $env_is->();
+ $test->($got, $expect, "Without", 'is');
+ }
+ }
+}
diff --git a/t/t0203-gettext-setlocale-sanity.sh b/t/t0203-gettext-setlocale-sanity.sh
new file mode 100755
index 0000000..a212460
--- /dev/null
+++ b/t/t0203-gettext-setlocale-sanity.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+
+test_description="The Git C functions aren't broken by setlocale(3)"
+
+. ./lib-gettext.sh
+
+test_expect_success 'git show a ISO-8859-1 commit under C locale' '
+ . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+ test_commit "iso-c-commit" iso-under-c &&
+ git show >out 2>err &&
+ ! test -s err &&
+ grep -q "iso-c-commit" out
+'
+
+test_expect_success GETTEXT_LOCALE 'git show a ISO-8859-1 commit under a UTF-8 locale' '
+ . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+ test_commit "iso-utf8-commit" iso-under-utf8 &&
+ LANGUAGE=is LC_ALL="$is_IS_locale" git show >out 2>err &&
+ ! test -s err &&
+ grep -q "iso-utf8-commit" out
+'
+
+test_done
diff --git a/t/t0204-gettext-reencode-sanity.sh b/t/t0204-gettext-reencode-sanity.sh
new file mode 100755
index 0000000..8437e51
--- /dev/null
+++ b/t/t0204-gettext-reencode-sanity.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+
+test_description="Gettext reencoding of our *.po/*.mo files works"
+
+. ./lib-gettext.sh
+
+# The constants used in a tricky observation for undefined behaviour
+RUNES="TILRAUN: ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛠᚻᛖ ᛒᚢᛞᛖ áš©áš¾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ áš¹á›áš¦ ᚦᚪ ᚹᛖᛥᚫ"
+PUNTS="TILRAUN: ?? ???? ??? ?? ???? ?? ??? ????? ??????????? ??? ?? ????"
+MSGKEY="TEST: Old English Runes"
+
+test_expect_success GETTEXT_LOCALE 'gettext: Emitting UTF-8 from our UTF-8 *.mo files / Icelandic' '
+ printf "TILRAUN: Halló Heimur!" >expect &&
+ LANGUAGE=is LC_ALL="$is_IS_locale" gettext "TEST: Hello World!" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GETTEXT_LOCALE 'gettext: Emitting UTF-8 from our UTF-8 *.mo files / Runes' '
+ printf "%s" "$RUNES" >expect &&
+ LANGUAGE=is LC_ALL="$is_IS_locale" gettext "$MSGKEY" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GETTEXT_ISO_LOCALE 'gettext: Emitting ISO-8859-1 from our UTF-8 *.mo files / Icelandic' '
+ printf "TILRAUN: Halló Heimur!" | iconv -f UTF-8 -t ISO8859-1 >expect &&
+ LANGUAGE=is LC_ALL="$is_IS_iso_locale" gettext "TEST: Hello World!" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GETTEXT_ISO_LOCALE 'gettext: impossible ISO-8859-1 output' '
+ LANGUAGE=is LC_ALL="$is_IS_iso_locale" gettext "$MSGKEY" >runes &&
+ case "$(cat runes)" in
+ "$MSGKEY")
+ say "Your system gives back the key to message catalog"
+ ;;
+ "$PUNTS")
+ say "Your system replaces an impossible character with ?"
+ ;;
+ "$RUNES")
+ say "Your system gives back the raw message for an impossible request"
+ ;;
+ *)
+ say "We never saw the error behaviour your system exhibits"
+ false
+ ;;
+ esac
+'
+
+test_expect_success GETTEXT_LOCALE 'gettext: Fetching a UTF-8 msgid -> UTF-8' '
+ printf "TILRAUN: ‚einfaldar‘ og „tvöfaldar“ gæsalappir" >expect &&
+ LANGUAGE=is LC_ALL="$is_IS_locale" gettext "TEST: ‘single’ and “double†quotes" >actual &&
+ test_cmp expect actual
+'
+
+# How these quotes get transliterated depends on the gettext implementation:
+#
+# Debian: ,einfaldar' og ,,tvöfaldar" [GNU libintl]
+# FreeBSD: `einfaldar` og "tvöfaldar" [GNU libintl]
+# Solaris: ?einfaldar? og ?tvöfaldar? [Solaris libintl]
+#
+# Just make sure the contents are transliterated, and don't use grep -q
+# so that these differences are emitted under --verbose for curious
+# eyes.
+test_expect_success GETTEXT_ISO_LOCALE 'gettext: Fetching a UTF-8 msgid -> ISO-8859-1' '
+ LANGUAGE=is LC_ALL="$is_IS_iso_locale" gettext "TEST: ‘single’ and “double†quotes" >actual &&
+ grep "einfaldar" actual &&
+ grep "$(echo tvöfaldar | iconv -f UTF-8 -t ISO8859-1)" actual
+'
+
+test_expect_success GETTEXT_LOCALE 'gettext.c: git init UTF-8 -> UTF-8' '
+ printf "Bjó til tóma Git lind" >expect &&
+ LANGUAGE=is LC_ALL="$is_IS_locale" git init repo >actual &&
+ test_when_finished "rm -rf repo" &&
+ grep "^$(cat expect) " actual
+'
+
+test_expect_success GETTEXT_ISO_LOCALE 'gettext.c: git init UTF-8 -> ISO-8859-1' '
+ printf "Bjó til tóma Git lind" >expect &&
+ LANGUAGE=is LC_ALL="$is_IS_iso_locale" git init repo >actual &&
+ test_when_finished "rm -rf repo" &&
+ grep "^$(cat expect | iconv -f UTF-8 -t ISO8859-1) " actual
+'
+
+test_done
diff --git a/t/t0205-gettext-poison.sh b/t/t0205-gettext-poison.sh
new file mode 100755
index 0000000..2361590
--- /dev/null
+++ b/t/t0205-gettext-poison.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+
+test_description='Gettext Shell poison'
+
+. ./lib-gettext.sh
+
+test_expect_success GETTEXT_POISON "sanity: \$GIT_INTERNAL_GETTEXT_SH_SCHEME is set (to $GIT_INTERNAL_GETTEXT_SH_SCHEME)" '
+ test -n "$GIT_INTERNAL_GETTEXT_SH_SCHEME"
+'
+
+test_expect_success GETTEXT_POISON 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME" is poison' '
+ test "$GIT_INTERNAL_GETTEXT_SH_SCHEME" = "poison"
+'
+
+test_expect_success GETTEXT_POISON 'gettext: our gettext() fallback has poison semantics' '
+ printf "# GETTEXT POISON #" >expect &&
+ gettext "test" >actual &&
+ test_cmp expect actual &&
+ printf "# GETTEXT POISON #" >expect &&
+ gettext "test more words" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GETTEXT_POISON 'eval_gettext: our eval_gettext() fallback has poison semantics' '
+ printf "# GETTEXT POISON #" >expect &&
+ eval_gettext "test" >actual &&
+ test_cmp expect actual &&
+ printf "# GETTEXT POISON #" >expect &&
+ eval_gettext "test more words" >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
new file mode 100755
index 0000000..03bd31e
--- /dev/null
+++ b/t/t0300-credentials.sh
@@ -0,0 +1,312 @@
+#!/bin/sh
+
+test_description='basic credential helper tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-credential.sh
+
+test_expect_success 'setup helper scripts' '
+ cat >dump <<-\EOF &&
+ whoami=$(echo $0 | sed s/.*git-credential-//)
+ echo >&2 "$whoami: $*"
+ OIFS=$IFS
+ IFS==
+ while read key value; do
+ echo >&2 "$whoami: $key=$value"
+ eval "$key=$value"
+ done
+ IFS=$OIFS
+ EOF
+
+ write_script git-credential-useless <<-\EOF &&
+ . ./dump
+ exit 0
+ EOF
+
+ write_script git-credential-verbatim <<-\EOF &&
+ user=$1; shift
+ pass=$1; shift
+ . ./dump
+ test -z "$user" || echo username=$user
+ test -z "$pass" || echo password=$pass
+ EOF
+
+ PATH="$PWD:$PATH"
+'
+
+test_expect_success 'credential_fill invokes helper' '
+ check fill "verbatim foo bar" <<-\EOF
+ --
+ username=foo
+ password=bar
+ --
+ verbatim: get
+ EOF
+'
+
+test_expect_success 'credential_fill invokes multiple helpers' '
+ check fill useless "verbatim foo bar" <<-\EOF
+ --
+ username=foo
+ password=bar
+ --
+ useless: get
+ verbatim: get
+ EOF
+'
+
+test_expect_success 'credential_fill stops when we get a full response' '
+ check fill "verbatim one two" "verbatim three four" <<-\EOF
+ --
+ username=one
+ password=two
+ --
+ verbatim: get
+ EOF
+'
+
+test_expect_success 'credential_fill continues through partial response' '
+ check fill "verbatim one \"\"" "verbatim two three" <<-\EOF
+ --
+ username=two
+ password=three
+ --
+ verbatim: get
+ verbatim: get
+ verbatim: username=one
+ EOF
+'
+
+test_expect_success 'credential_fill passes along metadata' '
+ check fill "verbatim one two" <<-\EOF
+ protocol=ftp
+ host=example.com
+ path=foo.git
+ --
+ protocol=ftp
+ host=example.com
+ path=foo.git
+ username=one
+ password=two
+ --
+ verbatim: get
+ verbatim: protocol=ftp
+ verbatim: host=example.com
+ verbatim: path=foo.git
+ EOF
+'
+
+test_expect_success 'credential_approve calls all helpers' '
+ check approve useless "verbatim one two" <<-\EOF
+ username=foo
+ password=bar
+ --
+ --
+ useless: store
+ useless: username=foo
+ useless: password=bar
+ verbatim: store
+ verbatim: username=foo
+ verbatim: password=bar
+ EOF
+'
+
+test_expect_success 'do not bother storing password-less credential' '
+ check approve useless <<-\EOF
+ username=foo
+ --
+ --
+ EOF
+'
+
+
+test_expect_success 'credential_reject calls all helpers' '
+ check reject useless "verbatim one two" <<-\EOF
+ username=foo
+ password=bar
+ --
+ --
+ useless: erase
+ useless: username=foo
+ useless: password=bar
+ verbatim: erase
+ verbatim: username=foo
+ verbatim: password=bar
+ EOF
+'
+
+test_expect_success 'usernames can be preserved' '
+ check fill "verbatim \"\" three" <<-\EOF
+ username=one
+ --
+ username=one
+ password=three
+ --
+ verbatim: get
+ verbatim: username=one
+ EOF
+'
+
+test_expect_success 'usernames can be overridden' '
+ check fill "verbatim two three" <<-\EOF
+ username=one
+ --
+ username=two
+ password=three
+ --
+ verbatim: get
+ verbatim: username=one
+ EOF
+'
+
+test_expect_success 'do not bother completing already-full credential' '
+ check fill "verbatim three four" <<-\EOF
+ username=one
+ password=two
+ --
+ username=one
+ password=two
+ --
+ EOF
+'
+
+# We can't test the basic terminal password prompt here because
+# getpass() tries too hard to find the real terminal. But if our
+# askpass helper is run, we know the internal getpass is working.
+test_expect_success 'empty helper list falls back to internal getpass' '
+ check fill <<-\EOF
+ --
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username:
+ askpass: Password:
+ EOF
+'
+
+test_expect_success 'internal getpass does not ask for known username' '
+ check fill <<-\EOF
+ username=foo
+ --
+ username=foo
+ password=askpass-password
+ --
+ askpass: Password:
+ EOF
+'
+
+HELPER="!f() {
+ cat >/dev/null
+ echo username=foo
+ echo password=bar
+ }; f"
+test_expect_success 'respect configured credentials' '
+ test_config credential.helper "$HELPER" &&
+ check fill <<-\EOF
+ --
+ username=foo
+ password=bar
+ --
+ EOF
+'
+
+test_expect_success 'match configured credential' '
+ test_config credential.https://example.com.helper "$HELPER" &&
+ check fill <<-\EOF
+ protocol=https
+ host=example.com
+ path=repo.git
+ --
+ protocol=https
+ host=example.com
+ username=foo
+ password=bar
+ --
+ EOF
+'
+
+test_expect_success 'do not match configured credential' '
+ test_config credential.https://foo.helper "$HELPER" &&
+ check fill <<-\EOF
+ protocol=https
+ host=bar
+ --
+ protocol=https
+ host=bar
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://bar'\'':
+ askpass: Password for '\''https://askpass-username@bar'\'':
+ EOF
+'
+
+test_expect_success 'pull username from config' '
+ test_config credential.https://example.com.username foo &&
+ check fill <<-\EOF
+ protocol=https
+ host=example.com
+ --
+ protocol=https
+ host=example.com
+ username=foo
+ password=askpass-password
+ --
+ askpass: Password for '\''https://foo@example.com'\'':
+ EOF
+'
+
+test_expect_success 'http paths can be part of context' '
+ check fill "verbatim foo bar" <<-\EOF &&
+ protocol=https
+ host=example.com
+ path=foo.git
+ --
+ protocol=https
+ host=example.com
+ username=foo
+ password=bar
+ --
+ verbatim: get
+ verbatim: protocol=https
+ verbatim: host=example.com
+ EOF
+ test_config credential.https://example.com.useHttpPath true &&
+ check fill "verbatim foo bar" <<-\EOF
+ protocol=https
+ host=example.com
+ path=foo.git
+ --
+ protocol=https
+ host=example.com
+ path=foo.git
+ username=foo
+ password=bar
+ --
+ verbatim: get
+ verbatim: protocol=https
+ verbatim: host=example.com
+ verbatim: path=foo.git
+ EOF
+'
+
+test_expect_success 'helpers can abort the process' '
+ test_must_fail git \
+ -c credential.helper="!f() { echo quit=1; }; f" \
+ -c credential.helper="verbatim foo bar" \
+ credential fill >stdout &&
+ >expect &&
+ test_cmp expect stdout
+'
+
+test_expect_success 'empty helper spec resets helper list' '
+ test_config credential.helper "verbatim file file" &&
+ check fill "" "verbatim cmdline cmdline" <<-\EOF
+ --
+ username=cmdline
+ password=cmdline
+ --
+ verbatim: get
+ EOF
+'
+
+test_done
diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh
new file mode 100755
index 0000000..82c8411
--- /dev/null
+++ b/t/t0301-credential-cache.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+test_description='credential-cache tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-credential.sh
+
+test -z "$NO_UNIX_SOCKETS" || {
+ skip_all='skipping credential-cache tests, unix sockets not available'
+ test_done
+}
+
+# don't leave a stale daemon running
+trap 'code=$?; git credential-cache exit; (exit $code); die' EXIT
+
+helper_test cache
+helper_test_timeout cache --timeout=1
+
+# we can't rely on our "trap" above working after test_done,
+# as test_done will delete the trash directory containing
+# our socket, leaving us with no way to access the daemon.
+git credential-cache exit
+
+test_done
diff --git a/t/t0302-credential-store.sh b/t/t0302-credential-store.sh
new file mode 100755
index 0000000..1d8d1f2
--- /dev/null
+++ b/t/t0302-credential-store.sh
@@ -0,0 +1,123 @@
+#!/bin/sh
+
+test_description='credential-store tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-credential.sh
+
+helper_test store
+
+test_expect_success 'when xdg file does not exist, xdg file not created' '
+ test_path_is_missing "$HOME/.config/git/credentials" &&
+ test -s "$HOME/.git-credentials"
+'
+
+test_expect_success 'setup xdg file' '
+ rm -f "$HOME/.git-credentials" &&
+ mkdir -p "$HOME/.config/git" &&
+ >"$HOME/.config/git/credentials"
+'
+
+helper_test store
+
+test_expect_success 'when xdg file exists, home file not created' '
+ test -s "$HOME/.config/git/credentials" &&
+ test_path_is_missing "$HOME/.git-credentials"
+'
+
+test_expect_success 'setup custom xdg file' '
+ rm -f "$HOME/.git-credentials" &&
+ rm -f "$HOME/.config/git/credentials" &&
+ mkdir -p "$HOME/xdg/git" &&
+ >"$HOME/xdg/git/credentials"
+'
+
+XDG_CONFIG_HOME="$HOME/xdg"
+export XDG_CONFIG_HOME
+helper_test store
+unset XDG_CONFIG_HOME
+
+test_expect_success 'if custom xdg file exists, home and xdg files not created' '
+ test_when_finished "rm -f $HOME/xdg/git/credentials" &&
+ test -s "$HOME/xdg/git/credentials" &&
+ test_path_is_missing "$HOME/.git-credentials" &&
+ test_path_is_missing "$HOME/.config/git/credentials"
+'
+
+test_expect_success 'get: use home file if both home and xdg files have matches' '
+ echo "https://home-user:home-pass@example.com" >"$HOME/.git-credentials" &&
+ mkdir -p "$HOME/.config/git" &&
+ echo "https://xdg-user:xdg-pass@example.com" >"$HOME/.config/git/credentials" &&
+ check fill store <<-\EOF
+ protocol=https
+ host=example.com
+ --
+ protocol=https
+ host=example.com
+ username=home-user
+ password=home-pass
+ --
+ EOF
+'
+
+test_expect_success 'get: use xdg file if home file has no matches' '
+ >"$HOME/.git-credentials" &&
+ mkdir -p "$HOME/.config/git" &&
+ echo "https://xdg-user:xdg-pass@example.com" >"$HOME/.config/git/credentials" &&
+ check fill store <<-\EOF
+ protocol=https
+ host=example.com
+ --
+ protocol=https
+ host=example.com
+ username=xdg-user
+ password=xdg-pass
+ --
+ EOF
+'
+
+test_expect_success POSIXPERM,SANITY 'get: use xdg file if home file is unreadable' '
+ echo "https://home-user:home-pass@example.com" >"$HOME/.git-credentials" &&
+ chmod -r "$HOME/.git-credentials" &&
+ mkdir -p "$HOME/.config/git" &&
+ echo "https://xdg-user:xdg-pass@example.com" >"$HOME/.config/git/credentials" &&
+ check fill store <<-\EOF
+ protocol=https
+ host=example.com
+ --
+ protocol=https
+ host=example.com
+ username=xdg-user
+ password=xdg-pass
+ --
+ EOF
+'
+
+test_expect_success 'store: if both xdg and home files exist, only store in home file' '
+ >"$HOME/.git-credentials" &&
+ mkdir -p "$HOME/.config/git" &&
+ >"$HOME/.config/git/credentials" &&
+ check approve store <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=store-user
+ password=store-pass
+ EOF
+ echo "https://store-user:store-pass@example.com" >expected &&
+ test_cmp expected "$HOME/.git-credentials" &&
+ test_must_be_empty "$HOME/.config/git/credentials"
+'
+
+
+test_expect_success 'erase: erase matching credentials from both xdg and home files' '
+ echo "https://home-user:home-pass@example.com" >"$HOME/.git-credentials" &&
+ mkdir -p "$HOME/.config/git" &&
+ echo "https://xdg-user:xdg-pass@example.com" >"$HOME/.config/git/credentials" &&
+ check reject store <<-\EOF &&
+ protocol=https
+ host=example.com
+ EOF
+ test_must_be_empty "$HOME/.git-credentials" &&
+ test_must_be_empty "$HOME/.config/git/credentials"
+'
+
+test_done
diff --git a/t/t0303-credential-external.sh b/t/t0303-credential-external.sh
new file mode 100755
index 0000000..f028fd1
--- /dev/null
+++ b/t/t0303-credential-external.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='external credential helper tests
+
+This is a tool for authors of external helper tools to sanity-check
+their helpers. If you have written the "git-credential-foo" helper,
+you check it with:
+
+ make GIT_TEST_CREDENTIAL_HELPER=foo t0303-credential-external.sh
+
+This assumes that your helper is capable of both storing and
+retrieving credentials (some helpers may be read-only, and they will
+fail these tests).
+
+Please note that the individual tests do not verify all of the
+preconditions themselves, but rather build on each other. A failing
+test means that tests later in the sequence can return false "OK"
+results.
+
+If your helper supports time-based expiration with a configurable
+timeout, you can test that feature with:
+
+ make GIT_TEST_CREDENTIAL_HELPER=foo \
+ GIT_TEST_CREDENTIAL_HELPER_TIMEOUT="foo --timeout=1" \
+ t0303-credential-external.sh
+
+If your helper requires additional setup before the tests are started,
+you can set GIT_TEST_CREDENTIAL_HELPER_SETUP to a sequence of shell
+commands.
+'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-credential.sh
+
+if test -z "$GIT_TEST_CREDENTIAL_HELPER"; then
+ skip_all="used to test external credential helpers"
+ test_done
+fi
+
+test -z "$GIT_TEST_CREDENTIAL_HELPER_SETUP" ||
+ eval "$GIT_TEST_CREDENTIAL_HELPER_SETUP"
+
+# clean before the test in case there is cruft left
+# over from a previous run that would impact results
+helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
+
+helper_test "$GIT_TEST_CREDENTIAL_HELPER"
+
+if test -z "$GIT_TEST_CREDENTIAL_HELPER_TIMEOUT"; then
+ say "# skipping timeout tests (GIT_TEST_CREDENTIAL_HELPER_TIMEOUT not set)"
+else
+ helper_test_timeout "$GIT_TEST_CREDENTIAL_HELPER_TIMEOUT"
+fi
+
+# clean afterwards so that we are good citizens
+# and don't leave cruft in the helper's storage, which
+# might be long-term system storage
+helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
+
+test_done
diff --git a/t/t1000-read-tree-m-3way.sh b/t/t1000-read-tree-m-3way.sh
index 4f17172..a0b79b4 100755
--- a/t/t1000-read-tree-m-3way.sh
+++ b/t/t1000-read-tree-m-3way.sh
@@ -72,6 +72,7 @@ In addition:
'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
. "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
################################################################
@@ -137,7 +138,7 @@ 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 &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
# This starts out with the first head, which is the normal
@@ -146,9 +147,9 @@ 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 &&
+ read_tree_must_succeed $tree_A &&
git checkout-index -f -u -a &&
- git read-tree -m $tree_O $tree_A $tree_B &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
: <<\END_OF_CASE_TABLE
@@ -211,7 +212,7 @@ 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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
@@ -219,7 +220,7 @@ test_expect_success \
"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"
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B"
test_expect_success \
'2 - matching B alone is OK in !O && !A && B case.' \
@@ -227,14 +228,14 @@ test_expect_success \
cp .orig-B/NA NA &&
git update-index --add NA &&
echo extra >>NA &&
- git read-tree -m $tree_O $tree_A $tree_B"
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B"
test_expect_success \
'3 - must match A in !O && A && !B case.' \
"rm -f .git/index AN &&
cp .orig-A/AN AN &&
git update-index --add AN &&
- git read-tree -m $tree_O $tree_A $tree_B &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
@@ -243,7 +244,7 @@ test_expect_success \
cp .orig-A/AN AN &&
git update-index --add AN &&
echo extra >>AN &&
- git read-tree -m $tree_O $tree_A $tree_B"
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B"
test_expect_success \
'3 (fail) - must match A in !O && A && !B case.' "
@@ -251,7 +252,7 @@ test_expect_success \
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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
@@ -259,7 +260,7 @@ test_expect_success \
"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 &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
@@ -268,7 +269,7 @@ test_expect_success \
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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
@@ -277,7 +278,7 @@ test_expect_success \
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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
@@ -285,7 +286,7 @@ test_expect_success \
"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 &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
@@ -294,7 +295,7 @@ test_expect_success \
cp .orig-A/LL LL &&
git update-index --add LL &&
echo extra >>LL &&
- git read-tree -m $tree_O $tree_A $tree_B &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
@@ -303,15 +304,15 @@ test_expect_success \
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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
'6 - must not exist in O && !A && !B case' "
rm -f .git/index DD &&
- echo DD >DD
+ echo DD >DD &&
git update-index --add DD &&
- test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
@@ -319,7 +320,7 @@ test_expect_success \
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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
@@ -327,7 +328,7 @@ test_expect_success \
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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
@@ -335,7 +336,7 @@ test_expect_success \
"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 &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
@@ -344,7 +345,7 @@ test_expect_success \
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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
@@ -353,7 +354,7 @@ test_expect_success \
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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
@@ -361,7 +362,7 @@ test_expect_success \
"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 &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
@@ -370,7 +371,7 @@ test_expect_success \
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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
@@ -379,7 +380,7 @@ test_expect_success \
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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
@@ -387,7 +388,7 @@ test_expect_success \
"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 &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
@@ -396,7 +397,7 @@ test_expect_success \
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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
@@ -405,7 +406,7 @@ test_expect_success \
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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
@@ -413,7 +414,7 @@ test_expect_success \
"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 &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
@@ -422,7 +423,7 @@ test_expect_success \
cp .orig-A/SS SS &&
git update-index --add SS &&
echo extra >>SS &&
- git read-tree -m $tree_O $tree_A $tree_B &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
@@ -431,7 +432,7 @@ test_expect_success \
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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
@@ -439,7 +440,7 @@ test_expect_success \
"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 &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
@@ -448,7 +449,7 @@ test_expect_success \
cp .orig-A/MN MN &&
git update-index --add MN &&
echo extra >>MN &&
- git read-tree -m $tree_O $tree_A $tree_B &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
@@ -456,7 +457,7 @@ test_expect_success \
"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 &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
@@ -465,7 +466,7 @@ test_expect_success \
cp .orig-B/NM NM &&
git update-index --add NM &&
echo extra >>NM &&
- git read-tree -m $tree_O $tree_A $tree_B &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
@@ -474,7 +475,7 @@ test_expect_success \
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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
@@ -483,7 +484,7 @@ test_expect_success \
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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
test_expect_success \
@@ -491,7 +492,7 @@ test_expect_success \
"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 &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
@@ -500,7 +501,7 @@ test_expect_success \
cp .orig-A/NN NN &&
git update-index --add NN &&
echo extra >>NN &&
- git read-tree -m $tree_O $tree_A $tree_B &&
+ read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
@@ -509,7 +510,7 @@ test_expect_success \
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
+ read_tree_must_fail -m $tree_O $tree_A $tree_B
"
# #16
@@ -518,11 +519,11 @@ test_expect_success \
'rm -f .git/index F16 &&
echo F16 >F16 &&
git update-index --add F16 &&
- tree0=`git write-tree` &&
+ tree0=$(git write-tree) &&
echo E16 >F16 &&
git update-index F16 &&
- tree1=`git write-tree` &&
- git read-tree -m $tree0 $tree1 $tree1 $tree0 &&
+ tree1=$(git write-tree) &&
+ read_tree_must_succeed -m $tree0 $tree1 $tree1 $tree0 &&
git ls-files --stage'
test_done
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 0c562bb..db1b6f5 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -18,9 +18,10 @@ In the test, these paths are used:
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
+ yomin - not in H or M
'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
read_tree_twoway () {
git read-tree -m "$1" "$2" && git ls-files --stage
@@ -35,7 +36,7 @@ compare_change () {
}
check_cache_at () {
- clean_if_empty=`git diff-files -- "$1"`
+ clean_if_empty=$(git diff-files -- "$1")
case "$clean_if_empty" in
'') echo "$1: clean" ;;
?*) echo "$1: dirty" ;;
@@ -67,14 +68,14 @@ test_expect_success \
echo rezrov >rezrov &&
echo yomin >yomin &&
git update-index --add nitfol bozbar rezrov &&
- treeH=`git write-tree` &&
+ 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` &&
+ treeM=$(git write-tree) &&
echo treeM $treeM &&
git ls-tree $treeM &&
git diff-tree $treeH $treeM'
@@ -94,33 +95,33 @@ echo '+100644 X 0 yomin' >expected
test_expect_success \
'4 - carry forward local addition.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
git update-index --add yomin &&
read_tree_twoway $treeH $treeM &&
- git ls-files --stage >4.out || return 1
- git diff --no-index M.out 4.out >4diff.out
+ git ls-files --stage >4.out &&
+ test_must_fail git diff --no-index M.out 4.out >4diff.out &&
compare_change 4diff.out expected &&
check_cache_at yomin clean'
test_expect_success \
'5 - carry forward local addition.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
echo yomin >yomin &&
git update-index --add yomin &&
echo yomin yomin >yomin &&
read_tree_twoway $treeH $treeM &&
- git ls-files --stage >5.out || return 1
- git diff --no-index M.out 5.out >5diff.out
+ git ls-files --stage >5.out &&
+ test_must_fail git diff --no-index M.out 5.out >5diff.out &&
compare_change 5diff.out expected &&
check_cache_at yomin dirty'
test_expect_success \
'6 - local addition already has the same.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
git update-index --add frotz &&
read_tree_twoway $treeH $treeM &&
@@ -131,7 +132,7 @@ test_expect_success \
test_expect_success \
'7 - local addition already has the same.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
echo frotz >frotz &&
git update-index --add frotz &&
@@ -144,7 +145,7 @@ test_expect_success \
test_expect_success \
'8 - conflicting addition.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
echo frotz frotz >frotz &&
git update-index --add frotz &&
@@ -153,7 +154,7 @@ test_expect_success \
test_expect_success \
'9 - conflicting addition.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
echo frotz frotz >frotz &&
git update-index --add frotz &&
@@ -163,7 +164,7 @@ test_expect_success \
test_expect_success \
'10 - path removed.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
echo rezrov >rezrov &&
git update-index --add rezrov &&
@@ -174,7 +175,7 @@ test_expect_success \
test_expect_success \
'11 - dirty path removed.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
echo rezrov >rezrov &&
git update-index --add rezrov &&
@@ -184,7 +185,7 @@ test_expect_success \
test_expect_success \
'12 - unmatching local changes being removed.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
echo rezrov rezrov >rezrov &&
git update-index --add rezrov &&
@@ -193,7 +194,7 @@ test_expect_success \
test_expect_success \
'13 - unmatching local changes being removed.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
echo rezrov rezrov >rezrov &&
git update-index --add rezrov &&
@@ -208,34 +209,34 @@ EOF
test_expect_success \
'14 - unchanged in two heads.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
echo nitfol nitfol >nitfol &&
git update-index --add nitfol &&
read_tree_twoway $treeH $treeM &&
- git ls-files --stage >14.out || return 1
- git diff --no-index M.out 14.out >14diff.out
+ git ls-files --stage >14.out &&
+ test_must_fail git diff --no-index M.out 14.out >14diff.out &&
compare_change 14diff.out expected &&
check_cache_at nitfol clean'
test_expect_success \
'15 - unchanged in two heads.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
echo nitfol nitfol >nitfol &&
git update-index --add nitfol &&
echo nitfol nitfol nitfol >nitfol &&
read_tree_twoway $treeH $treeM &&
- git ls-files --stage >15.out || return 1
- git diff --no-index M.out 15.out >15diff.out
+ git ls-files --stage >15.out &&
+ test_must_fail git diff --no-index M.out 15.out >15diff.out &&
compare_change 15diff.out expected &&
check_cache_at nitfol dirty'
test_expect_success \
'16 - conflicting local change.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
echo bozbar bozbar >bozbar &&
git update-index --add bozbar &&
@@ -244,7 +245,7 @@ test_expect_success \
test_expect_success \
'17 - conflicting local change.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
echo bozbar bozbar >bozbar &&
git update-index --add bozbar &&
@@ -254,7 +255,7 @@ test_expect_success \
test_expect_success \
'18 - local change already having a good result.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
cat bozbar-new >bozbar &&
git update-index --add bozbar &&
@@ -266,7 +267,7 @@ test_expect_success \
test_expect_success \
'19 - local change already having a good result, further modified.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
cat bozbar-new >bozbar &&
git update-index --add bozbar &&
@@ -279,7 +280,7 @@ test_expect_success \
test_expect_success \
'20 - no local change, use new tree.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
cat bozbar-old >bozbar &&
git update-index --add bozbar &&
@@ -291,7 +292,7 @@ test_expect_success \
test_expect_success \
'21 - no local change, dirty cache.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
cat bozbar-old >bozbar &&
git update-index --add bozbar &&
@@ -302,7 +303,7 @@ test_expect_success \
test_expect_success \
'22 - local change cache updated.' \
'rm -f .git/index &&
- git read-tree $treeH &&
+ read_tree_must_succeed $treeH &&
git checkout-index -u -f -q -a &&
sed -e "s/such as/SUCH AS/" bozbar-old >bozbar &&
git update-index --add bozbar &&
@@ -314,7 +315,7 @@ test_expect_success \
'rm -f .git/index &&
echo DF >DF &&
git update-index --add DF &&
- treeDF=`git write-tree` &&
+ treeDF=$(git write-tree) &&
echo treeDF $treeDF &&
git ls-tree $treeDF &&
@@ -322,7 +323,7 @@ test_expect_success \
mkdir DF &&
echo DF/DF >DF/DF &&
git update-index --add --remove DF DF/DF &&
- treeDFDF=`git write-tree` &&
+ treeDFDF=$(git write-tree) &&
echo treeDFDF $treeDFDF &&
git ls-tree $treeDFDF &&
git ls-files --stage >DFDF.out'
@@ -344,7 +345,7 @@ test_expect_success \
'rm -f .git/index &&
: >a &&
git update-index --add a &&
- treeM=`git write-tree` &&
+ treeM=$(git write-tree) &&
echo treeM $treeM &&
git ls-tree $treeM &&
git ls-files --stage >treeM.out &&
@@ -353,13 +354,13 @@ test_expect_success \
git update-index --remove a &&
mkdir a &&
: >a/b &&
- treeH=`git write-tree` &&
+ 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" &&
+ 'read_tree_u_must_fail -u -m "$treeH" "$treeM" &&
git ls-files --stage &&
test -f a/b'
@@ -371,22 +372,22 @@ test_expect_success \
mkdir c &&
: >c/d &&
git update-index --add a c/d &&
- treeM=`git write-tree` &&
+ treeM=$(git write-tree) &&
echo treeM $treeM &&
git ls-tree $treeM &&
git ls-files --stage >treeM.out &&
rm -f a &&
- mkdir a
+ mkdir a &&
: >a/b &&
git update-index --add --remove a a/b &&
- treeH=`git write-tree` &&
+ 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" &&
+ 'read_tree_u_must_succeed -u -m "$treeH" "$treeM" &&
git ls-files --stage | tee >treeMcheck.out &&
test_cmp treeM.out treeMcheck.out'
@@ -394,14 +395,14 @@ 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 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 &&
+ read_tree_must_succeed -m HEAD initial-mod &&
git ls-tree $(git write-tree) file-a >actual &&
test_cmp expect actual
'
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index 0241329..e3bf821 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -9,6 +9,7 @@ This is identical to t1001, but uses -u to update the work tree as well.
'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
compare_change () {
sed >current \
@@ -20,7 +21,7 @@ compare_change () {
}
check_cache_at () {
- clean_if_empty=`git diff-files -- "$1"`
+ clean_if_empty=$(git diff-files -- "$1")
case "$clean_if_empty" in
'') echo "$1: clean" ;;
?*) echo "$1: dirty" ;;
@@ -40,14 +41,14 @@ test_expect_success \
echo bozbar >bozbar &&
echo rezrov >rezrov &&
git update-index --add nitfol bozbar rezrov &&
- treeH=`git write-tree` &&
+ 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` &&
+ treeM=$(git write-tree) &&
echo treeM $treeM &&
git ls-tree $treeM &&
sum bozbar frotz nitfol >M.sum &&
@@ -56,8 +57,8 @@ test_expect_success \
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 &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
git ls-files --stage >1-3.out &&
cmp M.out 1-3.out &&
sum bozbar frotz nitfol >actual3.sum &&
@@ -69,13 +70,13 @@ test_expect_success \
test_expect_success \
'4 - carry forward local addition.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git read-tree --reset -u $treeH &&
+ read_tree_u_must_succeed --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
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
+ git ls-files --stage >4.out &&
+ test_might_fail 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 &&
@@ -87,14 +88,14 @@ test_expect_success \
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 &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ read_tree_u_must_succeed -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
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
+ git ls-files --stage >5.out &&
+ test_might_fail 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 &&
@@ -107,10 +108,10 @@ test_expect_success \
test_expect_success \
'6 - local addition already has the same.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git read-tree --reset -u $treeH &&
+ read_tree_u_must_succeed --reset -u $treeH &&
echo frotz >frotz &&
git update-index --add frotz &&
- git read-tree -m -u $treeH $treeM &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
git ls-files --stage >6.out &&
test_cmp M.out 6.out &&
check_cache_at frotz clean &&
@@ -123,11 +124,11 @@ test_expect_success \
test_expect_success \
'7 - local addition already has the same.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git read-tree --reset -u $treeH &&
+ read_tree_u_must_succeed --reset -u $treeH &&
echo frotz >frotz &&
git update-index --add frotz &&
echo frotz frotz >frotz &&
- git read-tree -m -u $treeH $treeM &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
git ls-files --stage >7.out &&
test_cmp M.out 7.out &&
check_cache_at frotz dirty &&
@@ -141,27 +142,27 @@ test_expect_success \
test_expect_success \
'8 - conflicting addition.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git read-tree --reset -u $treeH &&
+ read_tree_u_must_succeed --reset -u $treeH &&
echo frotz frotz >frotz &&
git update-index --add frotz &&
- if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+ if read_tree_u_must_succeed -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 &&
+ read_tree_u_must_succeed --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'
+ if read_tree_u_must_succeed -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 &&
+ read_tree_u_must_succeed --reset -u $treeH &&
echo rezrov >rezrov &&
git update-index --add rezrov &&
- git read-tree -m -u $treeH $treeM &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
git ls-files --stage >10.out &&
cmp M.out 10.out &&
sum bozbar frotz nitfol >actual10.sum &&
@@ -170,28 +171,28 @@ test_expect_success \
test_expect_success \
'11 - dirty path removed.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git read-tree --reset -u $treeH &&
+ read_tree_u_must_succeed --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'
+ if read_tree_u_must_succeed -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 &&
+ read_tree_u_must_succeed --reset -u $treeH &&
echo rezrov rezrov >rezrov &&
git update-index --add rezrov &&
- if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+ if read_tree_u_must_succeed -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 &&
+ read_tree_u_must_succeed --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'
+ if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
cat >expected <<EOF
-100644 X 0 nitfol
@@ -201,12 +202,12 @@ EOF
test_expect_success \
'14 - unchanged in two heads.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git read-tree --reset -u $treeH &&
+ read_tree_u_must_succeed --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
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
+ git ls-files --stage >14.out &&
+ test_must_fail 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 &&
@@ -221,13 +222,13 @@ test_expect_success \
test_expect_success \
'15 - unchanged in two heads.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git read-tree --reset -u $treeH &&
+ read_tree_u_must_succeed --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
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
+ git ls-files --stage >15.out &&
+ test_must_fail 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 &&
@@ -242,27 +243,27 @@ test_expect_success \
test_expect_success \
'16 - conflicting local change.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git read-tree --reset -u $treeH &&
+ read_tree_u_must_succeed --reset -u $treeH &&
echo bozbar bozbar >bozbar &&
git update-index --add bozbar &&
- if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+ if read_tree_u_must_succeed -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 &&
+ read_tree_u_must_succeed --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'
+ if read_tree_u_must_succeed -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 &&
+ read_tree_u_must_succeed --reset -u $treeH &&
echo gnusto >bozbar &&
git update-index --add bozbar &&
- git read-tree -m -u $treeH $treeM &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
git ls-files --stage >18.out &&
test_cmp M.out 18.out &&
check_cache_at bozbar clean &&
@@ -272,11 +273,11 @@ test_expect_success \
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 &&
+ read_tree_u_must_succeed --reset -u $treeH &&
echo gnusto >bozbar &&
git update-index --add bozbar &&
echo gnusto gnusto >bozbar &&
- git read-tree -m -u $treeH $treeM &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
git ls-files --stage >19.out &&
test_cmp M.out 19.out &&
check_cache_at bozbar dirty &&
@@ -292,10 +293,10 @@ test_expect_success \
test_expect_success \
'20 - no local change, use new tree.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git read-tree --reset -u $treeH &&
+ read_tree_u_must_succeed --reset -u $treeH &&
echo bozbar >bozbar &&
git update-index --add bozbar &&
- git read-tree -m -u $treeH $treeM &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
git ls-files --stage >20.out &&
test_cmp M.out 20.out &&
check_cache_at bozbar clean &&
@@ -305,19 +306,19 @@ test_expect_success \
test_expect_success \
'21 - no local change, dirty cache.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git read-tree --reset -u $treeH &&
+ read_tree_u_must_succeed --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'
+ if read_tree_u_must_succeed -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
+ 'rm -f .git/index &&
echo DF >DF &&
git update-index --add DF &&
- treeDF=`git write-tree` &&
+ treeDF=$(git write-tree) &&
echo treeDF $treeDF &&
git ls-tree $treeDF &&
@@ -325,7 +326,7 @@ test_expect_success \
mkdir DF &&
echo DF/DF >DF/DF &&
git update-index --add --remove DF DF/DF &&
- treeDFDF=`git write-tree` &&
+ treeDFDF=$(git write-tree) &&
echo treeDFDF $treeDFDF &&
git ls-tree $treeDFDF &&
git ls-files --stage >DFDF.out'
@@ -336,7 +337,7 @@ test_expect_success \
rm -fr DF &&
echo DF >DF &&
git update-index --add DF &&
- git read-tree -m -u $treeDF $treeDFDF &&
+ read_tree_u_must_succeed -m -u $treeDF $treeDFDF &&
git ls-files --stage >DFDFcheck.out &&
test_cmp DFDF.out DFDFcheck.out &&
check_cache_at DF/DF clean'
diff --git a/t/t1003-read-tree-prefix.sh b/t/t1003-read-tree-prefix.sh
index 8c6d67e..b6111cd 100755
--- a/t/t1003-read-tree-prefix.sh
+++ b/t/t1003-read-tree-prefix.sh
@@ -11,7 +11,7 @@ test_description='git read-tree --prefix test.
test_expect_success setup '
echo hello >one &&
git update-index --add one &&
- tree=`git write-tree` &&
+ tree=$(git write-tree) &&
echo tree is $tree
'
diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh
index f19b4a2..c70cf42 100755
--- a/t/t1004-read-tree-m-u-wf.sh
+++ b/t/t1004-read-tree-m-u-wf.sh
@@ -3,6 +3,7 @@
test_description='read-tree -m -u checks working tree files'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
# two-tree test
@@ -29,7 +30,7 @@ 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`
+ if err=$(read_tree_u_must_succeed -m -u master side 2>&1)
then
echo should have complained
false
@@ -42,7 +43,7 @@ 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`
+ if err=$(read_tree_u_must_succeed -m --exclude-per-directory=.gitignore master side 2>&1)
then
echo should have complained
false
@@ -53,7 +54,7 @@ test_expect_success 'two-way with incorrect --exclude-per-directory (1)' '
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`
+ if err=$(read_tree_u_must_succeed -m -u --exclude-per-directory=foo --exclude-per-directory=.gitignore master side 2>&1)
then
echo should have complained
false
@@ -64,7 +65,7 @@ test_expect_success 'two-way with incorrect --exclude-per-directory (2)' '
test_expect_success 'two-way clobbering a ignored file' '
- git read-tree -m -u --exclude-per-directory=.gitignore master side
+ read_tree_u_must_succeed -m -u --exclude-per-directory=.gitignore master side
'
rm -f .gitignore
@@ -84,7 +85,7 @@ test_expect_success 'three-way not complaining on an untracked path in both' '
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
+ read_tree_u_must_succeed -m -u branch-point master side
'
test_expect_success 'three-way not clobbering a working tree file' '
@@ -94,7 +95,7 @@ test_expect_success 'three-way not clobbering a working tree file' '
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`
+ if err=$(read_tree_u_must_succeed -m -u branch-point master side 2>&1)
then
echo should have complained
false
@@ -113,7 +114,7 @@ test_expect_success 'three-way not complaining on an untracked file' '
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
+ read_tree_u_must_succeed -m -u --exclude-per-directory=.gitignore branch-point master side
'
test_expect_success '3-way not overwriting local changes (setup)' '
@@ -137,7 +138,7 @@ test_expect_success '3-way not overwriting local changes (our side)' '
git reset --hard &&
echo >>file1 "local changes" &&
- git read-tree -m -u branch-point side-a side-b &&
+ read_tree_u_must_succeed -m -u branch-point side-a side-b &&
grep "new line to be kept" file1 &&
grep "local changes" file1
@@ -151,13 +152,13 @@ test_expect_success '3-way not overwriting local changes (their side)' '
git reset --hard &&
echo >>file2 "local changes" &&
- test_must_fail git read-tree -m -u branch-point side-a side-b &&
+ read_tree_u_must_fail -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' '
+test_expect_success 'funny symlink in work tree' '
git reset --hard &&
git checkout -b sym-b side-b &&
@@ -169,15 +170,14 @@ test_expect_success SYMLINKS 'funny symlink in work tree' '
rm -fr a &&
git checkout -b sym-a side-a &&
mkdir -p a &&
- ln -s ../b a/b &&
- git add a/b &&
+ test_ln_s_add ../b a/b &&
git commit -m "we add a/b" &&
- git read-tree -m -u sym-a sym-a sym-b
+ read_tree_u_must_succeed -m -u sym-a sym-a sym-b
'
-test_expect_success SYMLINKS 'funny symlink in work tree, un-unlink-able' '
+test_expect_success SANITY 'funny symlink in work tree, un-unlink-able' '
rm -fr a b &&
git reset --hard &&
@@ -209,7 +209,7 @@ test_expect_success 'D/F setup' '
test_expect_success 'D/F' '
git checkout side-b &&
- git read-tree -m -u branch-point side-b side-a &&
+ read_tree_u_must_succeed -m -u branch-point side-b side-a &&
git ls-files -u >actual &&
(
a=$(git rev-parse branch-point:subdir/file2)
diff --git a/t/t1005-read-tree-reset.sh b/t/t1005-read-tree-reset.sh
index 8499116..0745685 100755
--- a/t/t1005-read-tree-reset.sh
+++ b/t/t1005-read-tree-reset.sh
@@ -3,88 +3,104 @@
test_description='read-tree -u --reset'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.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
+ 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
+ read_tree_u_must_succeed -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
+ read_tree_u_must_succeed --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 &&
+ read_tree_u_must_succeed --reset -u HEAD &&
+ git ls-files -s >actual &&
+ ! test -f old
+'
+
+test_expect_success 'two-way reset should remove remnants too' '
+ read_tree_u_must_succeed --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 &&
+ read_tree_u_must_succeed --reset -u HEAD 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
+ read_tree_u_must_succeed --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
+ read_tree_u_must_succeed --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
+ read_tree_u_must_succeed --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
index d8b7f2f..4f38078 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -36,66 +36,88 @@ $content"
'
test_expect_success "Type of $type is correct" '
- test $type = "$(git cat-file -t $sha1)"
+ echo $type >expect &&
+ git cat-file -t $sha1 >actual &&
+ test_cmp expect actual
'
test_expect_success "Size of $type is correct" '
- test $size = "$(git cat-file -s $sha1)"
+ echo $size >expect &&
+ git cat-file -s $sha1 >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "Type of $type is correct using --allow-unknown-type" '
+ echo $type >expect &&
+ git cat-file -t --allow-unknown-type $sha1 >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "Size of $type is correct using --allow-unknown-type" '
+ echo $size >expect &&
+ git cat-file -s --allow-unknown-type $sha1 >actual &&
+ test_cmp expect actual
'
test -z "$content" ||
test_expect_success "Content of $type is correct" '
- expect="$(maybe_remove_timestamp "$content" $no_ts)"
- actual="$(maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts)"
-
- if test "z$expect" = "z$actual"
- then
- : happy
- else
- echo "Oops: expected $expect"
- echo "but got $actual"
- false
- fi
+ maybe_remove_timestamp "$content" $no_ts >expect &&
+ maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts >actual &&
+ test_cmp expect actual
'
test_expect_success "Pretty content of $type is correct" '
- expect="$(maybe_remove_timestamp "$pretty_content" $no_ts)"
- actual="$(maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts)"
- if test "z$expect" = "z$actual"
- then
- : happy
- else
- echo "Oops: expected $expect"
- echo "but got $actual"
- false
- fi
+ maybe_remove_timestamp "$pretty_content" $no_ts >expect &&
+ maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts >actual &&
+ test_cmp expect actual
'
test -z "$content" ||
test_expect_success "--batch output of $type is correct" '
- expect="$(maybe_remove_timestamp "$batch_output" $no_ts)"
- actual="$(maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts)"
- if test "z$expect" = "z$actual"
- then
- : happy
- else
- echo "Oops: expected $expect"
- echo "but got $actual"
- false
- fi
+ maybe_remove_timestamp "$batch_output" $no_ts >expect &&
+ maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts >actual &&
+ test_cmp expect actual
'
test_expect_success "--batch-check output of $type is correct" '
- expect="$sha1 $type $size"
- actual="$(echo_without_newline $sha1 | git cat-file --batch-check)"
- if test "z$expect" = "z$actual"
- then
- : happy
- else
- echo "Oops: expected $expect"
- echo "but got $actual"
- false
- fi
+ echo "$sha1 $type $size" >expect &&
+ echo_without_newline $sha1 | git cat-file --batch-check >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "custom --batch-check format" '
+ echo "$type $sha1" >expect &&
+ echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success '--batch-check with %(rest)' '
+ echo "$type this is some extra content" >expect &&
+ echo "$sha1 this is some extra content" |
+ git cat-file --batch-check="%(objecttype) %(rest)" >actual &&
+ test_cmp expect actual
+ '
+
+ test -z "$content" ||
+ test_expect_success "--batch without type ($type)" '
+ {
+ echo "$size" &&
+ maybe_remove_timestamp "$content" $no_ts
+ } >expect &&
+ echo $sha1 | git cat-file --batch="%(objectsize)" >actual.full &&
+ maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual &&
+ test_cmp expect actual
+ '
+
+ test -z "$content" ||
+ test_expect_success "--batch without size ($type)" '
+ {
+ echo "$type" &&
+ maybe_remove_timestamp "$content" $no_ts
+ } >expect &&
+ echo $sha1 | git cat-file --batch="%(objecttype)" >actual.full &&
+ maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual &&
+ test_cmp expect actual
'
}
@@ -110,15 +132,23 @@ test_expect_success "setup" '
run_tests 'blob' $hello_sha1 $hello_size "$hello_content" "$hello_content"
+test_expect_success '--batch-check without %(rest) considers whole line' '
+ echo "$hello_sha1 blob $hello_size" >expect &&
+ git update-index --add --cacheinfo 100644 $hello_sha1 "white space" &&
+ test_when_finished "git update-index --remove \"white space\"" &&
+ echo ":white space" | git cat-file --batch-check >actual &&
+ test_cmp expect actual
+'
+
tree_sha1=$(git write-tree)
tree_size=33
tree_pretty_content="100644 blob $hello_sha1 hello"
run_tests 'tree' $tree_sha1 $tree_size "" "$tree_pretty_content"
-commit_message="Intial commit"
+commit_message="Initial commit"
commit_sha1=$(echo_without_newline "$commit_message" | git commit-tree $tree_sha1)
-commit_size=176
+commit_size=177
commit_content="tree $tree_sha1
author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 0000000000 +0000
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 0000000000 +0000
@@ -135,14 +165,11 @@ tag_description="This is a tag"
tag_content="$tag_header_without_timestamp 0000000000 +0000
$tag_description"
-tag_pretty_content="$tag_header_without_timestamp Thu Jan 1 00:00:00 1970 +0000
-
-$tag_description"
tag_sha1=$(echo_without_newline "$tag_content" | git mktag)
tag_size=$(strlen "$tag_content")
-run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_pretty_content" 1
+run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_content" 1
test_expect_success \
"Reach a blob from a tag pointing to it" \
@@ -174,6 +201,13 @@ do
'
done
+for opt in t s e p
+do
+ test_expect_success "Passing -$opt with --follow-symlinks fails" '
+ test_must_fail git cat-file --follow-symlinks -$opt $hello_sha1
+ '
+done
+
test_expect_success "--batch-check for a non-existent named object" '
test "foobar42 missing
foobar84 missing" = \
@@ -201,6 +235,12 @@ test_expect_success "--batch-check for an emtpy line" '
test " missing" = "$(echo | git cat-file --batch-check)"
'
+test_expect_success 'empty --batch-check notices missing object' '
+ echo "$_z40 missing" >expect &&
+ echo "$_z40" | git cat-file --batch-check="" >actual &&
+ test_cmp expect actual
+'
+
batch_input="$hello_sha1
$commit_sha1
$tag_sha1
@@ -241,4 +281,296 @@ test_expect_success "--batch-check with multiple sha1s gives correct format" '
"$(echo_without_newline "$batch_check_input" | git cat-file --batch-check)"
'
+test_expect_success 'setup blobs which are likely to delta' '
+ test-genrandom foo 10240 >foo &&
+ { cat foo; echo plus; } >foo-plus &&
+ git add foo foo-plus &&
+ git commit -m foo &&
+ cat >blobs <<-\EOF
+ HEAD:foo
+ HEAD:foo-plus
+ EOF
+'
+
+test_expect_success 'confirm that neither loose blob is a delta' '
+ cat >expect <<-EOF &&
+ $_z40
+ $_z40
+ EOF
+ git cat-file --batch-check="%(deltabase)" <blobs >actual &&
+ test_cmp expect actual
+'
+
+# To avoid relying too much on the current delta heuristics,
+# we will check only that one of the two objects is a delta
+# against the other, but not the order. We can do so by just
+# asking for the base of both, and checking whether either
+# sha1 appears in the output.
+test_expect_success '%(deltabase) reports packed delta bases' '
+ git repack -ad &&
+ git cat-file --batch-check="%(deltabase)" <blobs >actual &&
+ {
+ grep "$(git rev-parse HEAD:foo)" actual ||
+ grep "$(git rev-parse HEAD:foo-plus)" actual
+ }
+'
+
+bogus_type="bogus"
+bogus_content="bogus"
+bogus_size=$(strlen "$bogus_content")
+bogus_sha1=$(echo_without_newline "$bogus_content" | git hash-object -t $bogus_type --literally -w --stdin)
+
+test_expect_success "Type of broken object is correct" '
+ echo $bogus_type >expect &&
+ git cat-file -t --allow-unknown-type $bogus_sha1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success "Size of broken object is correct" '
+ echo $bogus_size >expect &&
+ git cat-file -s --allow-unknown-type $bogus_sha1 >actual &&
+ test_cmp expect actual
+'
+bogus_type="abcdefghijklmnopqrstuvwxyz1234679"
+bogus_content="bogus"
+bogus_size=$(strlen "$bogus_content")
+bogus_sha1=$(echo_without_newline "$bogus_content" | git hash-object -t $bogus_type --literally -w --stdin)
+
+test_expect_success "Type of broken object is correct when type is large" '
+ echo $bogus_type >expect &&
+ git cat-file -t --allow-unknown-type $bogus_sha1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success "Size of large broken object is correct when type is large" '
+ echo $bogus_size >expect &&
+ git cat-file -s --allow-unknown-type $bogus_sha1 >actual &&
+ test_cmp expect actual
+'
+
+# Tests for git cat-file --follow-symlinks
+test_expect_success 'prep for symlink tests' '
+ echo_without_newline "$hello_content" >morx &&
+ test_ln_s_add morx same-dir-link &&
+ test_ln_s_add dir link-to-dir &&
+ test_ln_s_add ../fleem out-of-repo-link &&
+ test_ln_s_add .. out-of-repo-link-dir &&
+ test_ln_s_add same-dir-link link-to-link &&
+ test_ln_s_add nope broken-same-dir-link &&
+ mkdir dir &&
+ test_ln_s_add ../morx dir/parent-dir-link &&
+ test_ln_s_add .. dir/link-dir &&
+ test_ln_s_add ../../escape dir/out-of-repo-link &&
+ test_ln_s_add ../.. dir/out-of-repo-link-dir &&
+ test_ln_s_add nope dir/broken-link-in-dir &&
+ mkdir dir/subdir &&
+ test_ln_s_add ../../morx dir/subdir/grandparent-dir-link &&
+ test_ln_s_add ../../../great-escape dir/subdir/out-of-repo-link &&
+ test_ln_s_add ../../.. dir/subdir/out-of-repo-link-dir &&
+ test_ln_s_add ../../../ dir/subdir/out-of-repo-link-dir-trailing &&
+ test_ln_s_add ../parent-dir-link dir/subdir/parent-dir-link-to-link &&
+ echo_without_newline "$hello_content" >dir/subdir/ind2 &&
+ echo_without_newline "$hello_content" >dir/ind1 &&
+ test_ln_s_add dir dirlink &&
+ test_ln_s_add dir/subdir subdirlink &&
+ test_ln_s_add subdir/ind2 dir/link-to-child &&
+ test_ln_s_add dir/link-to-child link-to-down-link &&
+ test_ln_s_add dir/.. up-down &&
+ test_ln_s_add dir/../ up-down-trailing &&
+ test_ln_s_add dir/../morx up-down-file &&
+ test_ln_s_add dir/../../morx up-up-down-file &&
+ test_ln_s_add subdirlink/../../morx up-two-down-file &&
+ test_ln_s_add loop1 loop2 &&
+ test_ln_s_add loop2 loop1 &&
+ git add morx dir/subdir/ind2 dir/ind1 &&
+ git commit -am "test" &&
+ echo $hello_sha1 blob $hello_size >found
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for non-links' '
+ echo HEAD:morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:nope missing >expect &&
+ echo HEAD:nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, same-dir links' '
+ echo HEAD:same-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, links to dirs' '
+ echo HEAD:link-to-dir/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for broken in-repo, same-dir links' '
+ echo dangling 25 >expect &&
+ echo HEAD:broken-same-dir-link >>expect &&
+ echo HEAD:broken-same-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for same-dir links-to-links' '
+ echo HEAD:link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for parent-dir links' '
+ echo HEAD:dir/parent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo notdir 29 >expect &&
+ echo HEAD:dir/parent-dir-link/nope >>expect &&
+ echo HEAD:dir/parent-dir-link/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for .. links' '
+ echo dangling 22 >expect &&
+ echo HEAD:dir/link-dir/nope >>expect &&
+ echo HEAD:dir/link-dir/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/link-dir/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo dangling 27 >expect &&
+ echo HEAD:dir/broken-link-in-dir >>expect &&
+ echo HEAD:dir/broken-link-in-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for ../.. links' '
+ echo notdir 41 >expect &&
+ echo HEAD:dir/subdir/grandparent-dir-link/nope >>expect &&
+ echo HEAD:dir/subdir/grandparent-dir-link/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/subdir/grandparent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:dir/subdir/parent-dir-link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/ links' '
+ echo dangling 17 >expect &&
+ echo HEAD:dirlink/morx >>expect &&
+ echo HEAD:dirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo $hello_sha1 blob $hello_size >expect &&
+ echo HEAD:dirlink/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/subdir links' '
+ echo dangling 20 >expect &&
+ echo HEAD:subdirlink/morx >>expect &&
+ echo HEAD:subdirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:subdirlink/ind2 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir ->subdir links' '
+ echo notdir 27 >expect &&
+ echo HEAD:dir/link-to-child/morx >>expect &&
+ echo HEAD:dir/link-to-child/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/link-to-child | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:link-to-down-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks' '
+ echo symlink 8 >expect &&
+ echo ../fleem >>expect &&
+ echo HEAD:out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in dirs' '
+ echo symlink 9 >expect &&
+ echo ../escape >>expect &&
+ echo HEAD:dir/out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:dir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in subdirs' '
+ echo symlink 15 >expect &&
+ echo ../great-escape >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 3 >expect &&
+ echo ../ >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link-dir-trailing | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for symlinks with internal ..' '
+ echo HEAD: | git cat-file --batch-check >expect &&
+ echo HEAD:up-down | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-down-trailing | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo symlink 7 >expect &&
+ echo ../morx >>expect &&
+ echo HEAD:up-up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-two-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlink breaks loops' '
+ echo loop 10 >expect &&
+ echo HEAD:loop1 >>expect &&
+ echo HEAD:loop1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch --follow-symlink returns correct sha and mode' '
+ echo HEAD:morx | git cat-file --batch >expect &&
+ echo HEAD:morx | git cat-file --batch --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cat-file --batch-all-objects shows all objects' '
+ # make new repos so we know the full set of objects; we will
+ # also make sure that there are some packed and some loose
+ # objects, some referenced and some not, and that there are
+ # some available only via alternates.
+ git init all-one &&
+ (
+ cd all-one &&
+ echo content >file &&
+ git add file &&
+ git commit -qm base &&
+ git rev-parse HEAD HEAD^{tree} HEAD:file &&
+ git repack -ad &&
+ echo not-cloned | git hash-object -w --stdin
+ ) >expect.unsorted &&
+ git clone -s all-one all-two &&
+ (
+ cd all-two &&
+ echo local-unref | git hash-object -w --stdin
+ ) >>expect.unsorted &&
+ sort <expect.unsorted >expect &&
+ git -C all-two cat-file --batch-all-objects \
+ --batch-check="%(objectname)" >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
index dd32432..7d2baa1 100755
--- a/t/t1007-hash-object.sh
+++ b/t/t1007-hash-object.sh
@@ -188,4 +188,36 @@ for args in "-w --stdin-paths" "--stdin-paths -w"; do
pop_repo
done
+test_expect_success 'corrupt tree' '
+ echo abc >malformed-tree &&
+ test_must_fail git hash-object -t tree malformed-tree
+'
+
+test_expect_success 'corrupt commit' '
+ test_must_fail git hash-object -t commit --stdin </dev/null
+'
+
+test_expect_success 'corrupt tag' '
+ test_must_fail git hash-object -t tag --stdin </dev/null
+'
+
+test_expect_success 'hash-object complains about bogus type name' '
+ test_must_fail git hash-object -t bogus --stdin </dev/null
+'
+
+test_expect_success 'hash-object complains about truncated type name' '
+ test_must_fail git hash-object -t bl --stdin </dev/null
+'
+
+test_expect_success '--literally' '
+ t=1234567890 &&
+ echo example | git hash-object -t $t --literally --stdin
+'
+
+test_expect_success '--literally with extra-long type' '
+ t=12345678901234567890123456789012345678901234567890 &&
+ t="$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t" &&
+ echo example | git hash-object -t $t --literally --stdin
+'
+
test_done
diff --git a/t/t1008-read-tree-overlay.sh b/t/t1008-read-tree-overlay.sh
index f9e0028..4c50ed9 100755
--- a/t/t1008-read-tree-overlay.sh
+++ b/t/t1008-read-tree-overlay.sh
@@ -3,6 +3,7 @@
test_description='test multi-tree read-tree without merging'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
test_expect_success setup '
echo one >a &&
@@ -21,7 +22,7 @@ test_expect_success setup '
'
test_expect_success 'multi-read' '
- git read-tree initial master side &&
+ read_tree_must_succeed initial master side &&
(echo a; echo b/c) >expect &&
git ls-files >actual &&
test_cmp expect actual
diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh
index 62246db..0c74bee 100755
--- a/t/t1011-read-tree-sparse-checkout.sh
+++ b/t/t1011-read-tree-sparse-checkout.sh
@@ -1,45 +1,60 @@
#!/bin/sh
-test_description='sparse checkout tests'
+test_description='sparse checkout tests
+
+* (tag: removed, master) removed
+| D sub/added
+* (HEAD, tag: top) modified and added
+| M init.t
+| A sub/added
+* (tag: init) init
+ A init.t
+'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
-cat >expected <<EOF
-100644 77f0ba1734ed79d12881f81b36ee134de6a3327b 0 init.t
-100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 sub/added
-EOF
test_expect_success 'setup' '
+ cat >expected <<-\EOF &&
+ 100644 77f0ba1734ed79d12881f81b36ee134de6a3327b 0 init.t
+ 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 sub/added
+ 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 sub/addedtoo
+ 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 subsub/added
+ EOF
+ cat >expected.swt <<-\EOF &&
+ H init.t
+ H sub/added
+ H sub/addedtoo
+ H subsub/added
+ EOF
+
test_commit init &&
- echo modified >> init.t &&
- mkdir sub &&
- touch sub/added &&
- git add init.t sub/added &&
+ echo modified >>init.t &&
+ mkdir sub subsub &&
+ touch sub/added sub/addedtoo subsub/added &&
+ git add init.t sub/added sub/addedtoo subsub/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 &&
+ 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 &&
+ read_tree_u_must_succeed -m -u HEAD &&
+ git ls-files --stage >result &&
test_cmp expected result &&
- git ls-files -t > 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 &&
+ echo >.git/info/sparse-checkout &&
+ read_tree_u_must_succeed -m -u HEAD &&
+ git ls-files -t >result &&
test_cmp expected.swt result &&
test -f init.t &&
test -f sub/added
@@ -47,9 +62,9 @@ test_expect_success 'read-tree with .git/info/sparse-checkout but disabled' '
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 &&
+ echo >.git/info/sparse-checkout &&
+ read_tree_u_must_succeed --no-sparse-checkout -m -u HEAD &&
+ git ls-files -t >result &&
test_cmp expected.swt result &&
test -f init.t &&
test -f sub/added
@@ -57,94 +72,206 @@ test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-
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 &&
+ echo >.git/info/sparse-checkout &&
+ read_tree_u_must_fail -m -u HEAD &&
+ git ls-files --stage >result &&
test_cmp expected result &&
- git ls-files -t > 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' '
+ cat >expected.swt-noinit <<-\EOF &&
+ S init.t
+ H sub/added
+ H sub/addedtoo
+ S subsub/added
+ EOF
+
echo sub/ > .git/info/sparse-checkout &&
- git read-tree -m -u HEAD &&
+ read_tree_u_must_succeed -m -u HEAD &&
git ls-files -t > result &&
- test_cmp expected.swt result &&
+ test_cmp expected.swt-noinit result &&
+ test ! -f init.t &&
+ test -f sub/added
+'
+
+test_expect_success 'match directories without trailing slash' '
+ echo sub >.git/info/sparse-checkout &&
+ read_tree_u_must_succeed -m -u HEAD &&
+ git ls-files -t >result &&
+ test_cmp expected.swt-noinit result &&
test ! -f init.t &&
test -f sub/added
'
-cat >expected.swt <<EOF
+test_expect_success 'match directories with negated patterns' '
+ cat >expected.swt-negation <<\EOF &&
+S init.t
+S sub/added
+H sub/addedtoo
+S subsub/added
+EOF
+
+ cat >.git/info/sparse-checkout <<\EOF &&
+sub
+!sub/added
+EOF
+ git read-tree -m -u HEAD &&
+ git ls-files -t >result &&
+ test_cmp expected.swt-negation result &&
+ test ! -f init.t &&
+ test ! -f sub/added &&
+ test -f sub/addedtoo
+'
+
+test_expect_success 'match directories with negated patterns (2)' '
+ cat >expected.swt-negation2 <<\EOF &&
H init.t
H sub/added
+S sub/addedtoo
+H subsub/added
+EOF
+
+ cat >.git/info/sparse-checkout <<\EOF &&
+/*
+!sub
+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 &&
+ git ls-files -t >result &&
+ test_cmp expected.swt-negation2 result &&
+ test -f init.t &&
+ test -f sub/added &&
+ test ! -f sub/addedtoo
+'
+
+test_expect_success 'match directory pattern' '
+ echo "s?b" >.git/info/sparse-checkout &&
+ read_tree_u_must_succeed -m -u HEAD &&
+ git ls-files -t >result &&
+ test_cmp expected.swt-noinit 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 &&
+ cat >expected.swt-nosub <<-\EOF &&
+ H init.t
+ S sub/added
+ S sub/addedtoo
+ S subsub/added
+ EOF
+
+ echo init.t >.git/info/sparse-checkout &&
+ read_tree_u_must_succeed -m -u HEAD &&
+ git ls-files -t >result &&
+ test_cmp expected.swt-nosub 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 &&
+ echo sub/added >.git/info/sparse-checkout &&
git checkout -f top &&
- git read-tree -m -u HEAD^ &&
+ read_tree_u_must_succeed -m -u HEAD^ &&
test ! -f init.t
'
test_expect_success 'read-tree updates worktree, dirty case' '
- echo sub/added > .git/info/sparse-checkout &&
+ echo sub/added >.git/info/sparse-checkout &&
git checkout -f top &&
- echo dirty > init.t &&
- git read-tree -m -u HEAD^ &&
+ echo dirty >init.t &&
+ read_tree_u_must_succeed -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 &&
+ echo init.t >.git/info/sparse-checkout &&
git checkout -f top &&
- echo dirty > added &&
- git read-tree -m -u HEAD^ &&
+ echo dirty >added &&
+ read_tree_u_must_succeed -m -u HEAD^ &&
grep -q dirty added
'
test_expect_success 'read-tree adds to worktree, absent case' '
- echo init.t > .git/info/sparse-checkout &&
+ echo init.t >.git/info/sparse-checkout &&
git checkout -f removed &&
- git read-tree -u -m HEAD^ &&
+ read_tree_u_must_succeed -u -m HEAD^ &&
test ! -f sub/added
'
test_expect_success 'read-tree adds to worktree, dirty case' '
- echo init.t > .git/info/sparse-checkout &&
+ echo init.t >.git/info/sparse-checkout &&
git checkout -f removed &&
mkdir sub &&
- echo dirty > sub/added &&
- git read-tree -u -m HEAD^ &&
+ echo dirty >sub/added &&
+ read_tree_u_must_succeed -u -m HEAD^ &&
grep -q dirty sub/added
'
+test_expect_success 'index removal and worktree narrowing at the same time' '
+ >empty &&
+ echo init.t >.git/info/sparse-checkout &&
+ echo sub/added >>.git/info/sparse-checkout &&
+ git checkout -f top &&
+ echo init.t >.git/info/sparse-checkout &&
+ git checkout removed &&
+ git ls-files sub/added >result &&
+ test ! -f sub/added &&
+ test_cmp empty result
+'
+
+test_expect_success 'read-tree --reset removes outside worktree' '
+ >empty &&
+ echo init.t >.git/info/sparse-checkout &&
+ git checkout -f top &&
+ git reset --hard removed &&
+ git ls-files sub/added >result &&
+ test_cmp empty result
+'
+
+test_expect_success 'print errors when failed to update worktree' '
+ echo sub >.git/info/sparse-checkout &&
+ git checkout -f init &&
+ mkdir sub &&
+ touch sub/added sub/addedtoo &&
+ test_must_fail git checkout top 2>actual &&
+ cat >expected <<\EOF &&
+error: The following untracked working tree files would be overwritten by checkout:
+ sub/added
+ sub/addedtoo
+Please move or remove them before you can switch branches.
+Aborting
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'checkout without --ignore-skip-worktree-bits' '
+ echo "*" >.git/info/sparse-checkout &&
+ git checkout -f top &&
+ test_path_is_file init.t &&
+ echo sub >.git/info/sparse-checkout &&
+ git checkout &&
+ echo modified >> sub/added &&
+ git checkout . &&
+ test_path_is_missing init.t &&
+ git diff --exit-code HEAD
+'
+
+test_expect_success 'checkout with --ignore-skip-worktree-bits' '
+ echo "*" >.git/info/sparse-checkout &&
+ git checkout -f top &&
+ test_path_is_file init.t &&
+ echo sub >.git/info/sparse-checkout &&
+ git checkout &&
+ echo modified >> sub/added &&
+ git checkout --ignore-skip-worktree-bits . &&
+ test_path_is_file init.t &&
+ git diff --exit-code HEAD
+'
+
test_done
diff --git a/t/t1012-read-tree-df.sh b/t/t1012-read-tree-df.sh
index 9811d46..a6a04b6 100755
--- a/t/t1012-read-tree-df.sh
+++ b/t/t1012-read-tree-df.sh
@@ -3,6 +3,7 @@
test_description='read-tree D/F conflict corner cases'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
maketree () {
(
@@ -53,7 +54,7 @@ test_expect_success setup '
test_expect_success '3-way (1)' '
settree A-000 &&
- git read-tree -m -u O-000 A-000 B-000 &&
+ read_tree_u_must_succeed -m -u O-000 A-000 B-000 &&
checkindex <<-EOF
3 a/b
0 a/b-2/c/d
@@ -65,7 +66,7 @@ test_expect_success '3-way (1)' '
test_expect_success '3-way (2)' '
settree A-001 &&
- git read-tree -m -u O-000 A-001 B-000 &&
+ read_tree_u_must_succeed -m -u O-000 A-001 B-000 &&
checkindex <<-EOF
3 a/b
0 a/b-2/c/d
@@ -78,7 +79,7 @@ test_expect_success '3-way (2)' '
test_expect_success '3-way (3)' '
settree A-010 &&
- git read-tree -m -u O-010 A-010 B-010 &&
+ read_tree_u_must_succeed -m -u O-010 A-010 B-010 &&
checkindex <<-EOF
2 t
1 t-0
@@ -92,7 +93,7 @@ test_expect_success '3-way (3)' '
test_expect_success '2-way (1)' '
settree O-020 &&
- git read-tree -m -u O-020 A-020 &&
+ read_tree_u_must_succeed -m -u O-020 A-020 &&
checkindex <<-EOF
0 ds/dma/ioat/Makefile
0 ds/dma/ioat/registers.h
diff --git a/t/t1013-read-tree-submodule.sh b/t/t1013-read-tree-submodule.sh
new file mode 100755
index 0000000..20526ae
--- /dev/null
+++ b/t/t1013-read-tree-submodule.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+test_description='read-tree can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+test_submodule_switch "git read-tree -u -m"
+
+test_submodule_forced_switch "git read-tree -u --reset"
+
+test_done
diff --git a/t/t1014-read-tree-confusing.sh b/t/t1014-read-tree-confusing.sh
new file mode 100755
index 0000000..2f5a25d
--- /dev/null
+++ b/t/t1014-read-tree-confusing.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+test_description='check that read-tree rejects confusing paths'
+. ./test-lib.sh
+
+test_expect_success 'create base tree' '
+ echo content >file &&
+ git add file &&
+ git commit -m base &&
+ blob=$(git rev-parse HEAD:file) &&
+ tree=$(git rev-parse HEAD^{tree})
+'
+
+test_expect_success 'enable core.protectHFS for rejection tests' '
+ git config core.protectHFS true
+'
+
+test_expect_success 'enable core.protectNTFS for rejection tests' '
+ git config core.protectNTFS true
+'
+
+while read path pretty; do
+ : ${pretty:=$path}
+ case "$path" in
+ *SPACE)
+ path="${path%SPACE} "
+ ;;
+ esac
+ test_expect_success "reject $pretty at end of path" '
+ printf "100644 blob %s\t%s" "$blob" "$path" >tree &&
+ bogus=$(git mktree <tree) &&
+ test_must_fail git read-tree $bogus
+ '
+
+ test_expect_success "reject $pretty as subtree" '
+ printf "040000 tree %s\t%s" "$tree" "$path" >tree &&
+ bogus=$(git mktree <tree) &&
+ test_must_fail git read-tree $bogus
+ '
+done <<-EOF
+.
+..
+.git
+.GIT
+${u200c}.Git {u200c}.Git
+.gI${u200c}T .gI{u200c}T
+.GiT${u200c} .GiT{u200c}
+git~1
+.git.SPACE .git.{space}
+.\\\\.GIT\\\\foobar backslashes
+.git\\\\foobar backslashes2
+EOF
+
+test_expect_success 'utf-8 paths allowed with core.protectHFS off' '
+ test_when_finished "git read-tree HEAD" &&
+ test_config core.protectHFS false &&
+ printf "100644 blob %s\t%s" "$blob" ".gi${u200c}t" >tree &&
+ ok=$(git mktree <tree) &&
+ git read-tree $ok
+'
+
+test_done
diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
index 5687499..df3183e 100755
--- a/t/t1020-subdirectory.sh
+++ b/t/t1020-subdirectory.sh
@@ -7,6 +7,7 @@ test_description='Try various core-level commands in subdirectory.
'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.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" &&
@@ -16,123 +17,179 @@ test_expect_success setup '
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
+ 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
+ (
+ cd dir &&
+ git update-index --add two &&
+ case "$(git ls-files)" in
+ two) echo pass two ;;
+ *) echo bad two; exit 1 ;;
+ esac
+ ) &&
+ 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]*\\)"` &&
+ 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
+ (
+ 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
+ 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
+ (
+ 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` &&
+ top=$(git write-tree) &&
echo $top &&
- cd dir &&
- sub=`git write-tree` &&
- echo $sub &&
- test "z$top" = "z$sub"
+ (
+ 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
+ (
+ 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" &&
+ tree=$(git write-tree) &&
+ read_tree_u_must_succeed --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
+ (
+ cd dir &&
+ rm -f two &&
+ read_tree_u_must_succeed --reset -u "$tree" &&
+ cmp two ../original.two &&
+ cmp ../one ../original.one
+ )
+'
+
+test_expect_success 'alias expansion' '
+ (
+ git config alias.test-status-alias status &&
+ cd dir &&
+ git status &&
+ git test-status-alias
+ )
+'
+
+test_expect_success !MINGW '!alias expansion' '
+ pwd >expect &&
+ (
+ git config alias.test-alias-directory !pwd &&
+ cd dir &&
+ git test-alias-directory >../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'GIT_PREFIX for !alias' '
+ printf "dir/" >expect &&
+ (
+ git config alias.test-alias-directory "!sh -c \"printf \$GIT_PREFIX\"" &&
+ cd dir &&
+ git test-alias-directory >../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'GIT_PREFIX for built-ins' '
+ # Use GIT_EXTERNAL_DIFF to test that the "diff" built-in
+ # receives the GIT_PREFIX variable.
+ echo "dir/" >expect &&
+ write_script diff <<-\EOF &&
+ printf "%s\n" "$GIT_PREFIX"
+ EOF
+ (
+ cd dir &&
+ echo "change" >two &&
+ GIT_EXTERNAL_DIFF=./diff git diff >../actual
+ git checkout -- two
+ ) &&
+ test_cmp expect actual
'
test_expect_success 'no file/rev ambiguity check inside .git' '
- cd "$HERE" &&
git commit -a -m 1 &&
- cd "$HERE"/.git &&
- git show -s HEAD
+ (
+ cd .git &&
+ git show -s HEAD
+ )
'
-test_expect_success 'no file/rev ambiguity check inside a bare repo' '
- cd "$HERE" &&
+test_expect_success 'no file/rev ambiguity check inside a bare repo (explicit GIT_DIR)' '
+ test_when_finished "rm -fr foo.git" &&
git clone -s --bare .git foo.git &&
- cd foo.git && GIT_DIR=. git show -s HEAD
+ (
+ cd foo.git &&
+ # older Git needed help by exporting GIT_DIR=.
+ # to realize that it is inside a bare repository.
+ # We keep this test around for regression testing.
+ 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" &&
+test_expect_success 'no file/rev ambiguity check inside a bare repo' '
+ test_when_finished "rm -fr foo.git" &&
git clone -s --bare .git foo.git &&
- cd foo.git && git show -s HEAD
+ (
+ 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
+ (
+ cd yetanother/.git &&
+ git show -s HEAD
+ )
'
test_done
diff --git a/t/t1021-rerere-in-workdir.sh b/t/t1021-rerere-in-workdir.sh
new file mode 100755
index 0000000..301e071
--- /dev/null
+++ b/t/t1021-rerere-in-workdir.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='rerere run in a workdir'
+. ./test-lib.sh
+
+test_expect_success SYMLINKS setup '
+ git config rerere.enabled true &&
+ >world &&
+ git add world &&
+ test_tick &&
+ git commit -m initial &&
+
+ echo hello >world &&
+ test_tick &&
+ git commit -a -m hello &&
+
+ git checkout -b side HEAD^ &&
+ echo goodbye >world &&
+ test_tick &&
+ git commit -a -m goodbye &&
+
+ git checkout master
+'
+
+test_expect_success SYMLINKS 'rerere in workdir' '
+ rm -rf .git/rr-cache &&
+ "$SHELL_PATH" "$TEST_DIRECTORY/../contrib/workdir/git-new-workdir" . work &&
+ (
+ cd work &&
+ test_must_fail git merge side &&
+ git rerere status >actual &&
+ echo world >expect &&
+ test_cmp expect actual
+ )
+'
+
+# This fails because we don't resolve relative symlink in mkdir_in_gitdir()
+# For the purpose of helping contrib/workdir/git-new-workdir users, we do not
+# have to support relative symlinks, but it might be nicer to make this work
+# with a relative symbolic link someday.
+test_expect_failure SYMLINKS 'rerere in workdir (relative)' '
+ rm -rf .git/rr-cache &&
+ "$SHELL_PATH" "$TEST_DIRECTORY/../contrib/workdir/git-new-workdir" . krow &&
+ (
+ cd krow &&
+ rm -f .git/rr-cache &&
+ ln -s ../.git/rr-cache .git/rr-cache &&
+ test_must_fail git merge side &&
+ git rerere status >actual &&
+ echo world >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_done
diff --git a/t/t1050-large.sh b/t/t1050-large.sh
new file mode 100755
index 0000000..f9f3d13
--- /dev/null
+++ b/t/t1050-large.sh
@@ -0,0 +1,186 @@
+#!/bin/sh
+# Copyright (c) 2011, Google Inc.
+
+test_description='adding and checking out large blobs'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ # clone does not allow us to pass core.bigfilethreshold to
+ # new repos, so set core.bigfilethreshold globally
+ git config --global core.bigfilethreshold 200k &&
+ printf "%2000000s" X >large1 &&
+ cp large1 large2 &&
+ cp large1 large3 &&
+ printf "%2500000s" Y >huge &&
+ GIT_ALLOC_LIMIT=1500k &&
+ export GIT_ALLOC_LIMIT
+'
+
+test_expect_success 'add a large file or two' '
+ git add large1 huge large2 &&
+ # make sure we got a single packfile and no loose objects
+ bad= count=0 idx= &&
+ for p in .git/objects/pack/pack-*.pack
+ do
+ count=$(( $count + 1 ))
+ if test -f "$p" && idx=${p%.pack}.idx && test -f "$idx"
+ then
+ continue
+ fi
+ bad=t
+ done &&
+ test -z "$bad" &&
+ test $count = 1 &&
+ cnt=$(git show-index <"$idx" | wc -l) &&
+ test $cnt = 2 &&
+ for l in .git/objects/??/??????????????????????????????????????
+ do
+ test -f "$l" || continue
+ bad=t
+ done &&
+ test -z "$bad" &&
+
+ # attempt to add another copy of the same
+ git add large3 &&
+ bad= count=0 &&
+ for p in .git/objects/pack/pack-*.pack
+ do
+ count=$(( $count + 1 ))
+ if test -f "$p" && idx=${p%.pack}.idx && test -f "$idx"
+ then
+ continue
+ fi
+ bad=t
+ done &&
+ test -z "$bad" &&
+ test $count = 1
+'
+
+test_expect_success 'checkout a large file' '
+ large1=$(git rev-parse :large1) &&
+ git update-index --add --cacheinfo 100644 $large1 another &&
+ git checkout another &&
+ test_cmp large1 another
+'
+
+test_expect_success 'packsize limit' '
+ test_create_repo mid &&
+ (
+ cd mid &&
+ git config core.bigfilethreshold 64k &&
+ git config pack.packsizelimit 256k &&
+
+ # mid1 and mid2 will fit within 256k limit but
+ # appending mid3 will bust the limit and will
+ # result in a separate packfile.
+ test-genrandom "a" $(( 66 * 1024 )) >mid1 &&
+ test-genrandom "b" $(( 80 * 1024 )) >mid2 &&
+ test-genrandom "c" $(( 128 * 1024 )) >mid3 &&
+ git add mid1 mid2 mid3 &&
+
+ count=0
+ for pi in .git/objects/pack/pack-*.idx
+ do
+ test -f "$pi" && count=$(( $count + 1 ))
+ done &&
+ test $count = 2 &&
+
+ (
+ git hash-object --stdin <mid1
+ git hash-object --stdin <mid2
+ git hash-object --stdin <mid3
+ ) |
+ sort >expect &&
+
+ for pi in .git/objects/pack/pack-*.idx
+ do
+ git show-index <"$pi"
+ done |
+ sed -e "s/^[0-9]* \([0-9a-f]*\) .*/\1/" |
+ sort >actual &&
+
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'diff --raw' '
+ git commit -q -m initial &&
+ echo modified >>large1 &&
+ git add large1 &&
+ git commit -q -m modified &&
+ git diff --raw HEAD^
+'
+
+test_expect_success 'diff --stat' '
+ git diff --stat HEAD^ HEAD
+'
+
+test_expect_success 'diff' '
+ git diff HEAD^ HEAD >actual &&
+ grep "Binary files.*differ" actual
+'
+
+test_expect_success 'diff --cached' '
+ git diff --cached HEAD^ >actual &&
+ grep "Binary files.*differ" actual
+'
+
+test_expect_success 'hash-object' '
+ git hash-object large1
+'
+
+test_expect_success 'cat-file a large file' '
+ git cat-file blob :large1 >/dev/null
+'
+
+test_expect_success 'cat-file a large file from a tag' '
+ git tag -m largefile largefiletag :large1 &&
+ git cat-file blob largefiletag >/dev/null
+'
+
+test_expect_success 'git-show a large file' '
+ git show :large1 >/dev/null
+
+'
+
+test_expect_success 'index-pack' '
+ git clone file://"$(pwd)"/.git foo &&
+ GIT_DIR=non-existent git index-pack --strict --verify foo/.git/objects/pack/*.pack
+'
+
+test_expect_success 'repack' '
+ git repack -ad
+'
+
+test_expect_success 'pack-objects with large loose object' '
+ SHA1=$(git hash-object huge) &&
+ test_create_repo loose &&
+ echo $SHA1 | git pack-objects --stdout |
+ GIT_ALLOC_LIMIT=0 GIT_DIR=loose/.git git unpack-objects &&
+ echo $SHA1 | GIT_DIR=loose/.git git pack-objects pack &&
+ test_create_repo packed &&
+ mv pack-* packed/.git/objects/pack &&
+ GIT_DIR=packed/.git git cat-file blob $SHA1 >actual &&
+ test_cmp huge actual
+'
+
+test_expect_success 'tar achiving' '
+ git archive --format=tar HEAD >/dev/null
+'
+
+test_expect_success 'zip achiving, store only' '
+ git archive --format=zip -0 HEAD >/dev/null
+'
+
+test_expect_success 'zip achiving, deflate' '
+ git archive --format=zip HEAD >/dev/null
+'
+
+test_expect_success 'fsck' '
+ test_must_fail git fsck 2>err &&
+ n=$(grep "error: attempting to allocate .* over limit" err | wc -l) &&
+ test "$n" -gt 1
+'
+
+test_done
diff --git a/t/t1051-large-conversion.sh b/t/t1051-large-conversion.sh
new file mode 100755
index 0000000..8b7640b
--- /dev/null
+++ b/t/t1051-large-conversion.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+
+test_description='test conversion filters on large files'
+. ./test-lib.sh
+
+set_attr() {
+ test_when_finished 'rm -f .gitattributes' &&
+ echo "* $*" >.gitattributes
+}
+
+check_input() {
+ git read-tree --empty &&
+ git add small large &&
+ git cat-file blob :small >small.index &&
+ git cat-file blob :large | head -n 1 >large.index &&
+ test_cmp small.index large.index
+}
+
+check_output() {
+ rm -f small large &&
+ git checkout small large &&
+ head -n 1 large >large.head &&
+ test_cmp small large.head
+}
+
+test_expect_success 'setup input tests' '
+ printf "\$Id: foo\$\\r\\n" >small &&
+ cat small small >large &&
+ git config core.bigfilethreshold 20 &&
+ git config filter.test.clean "sed s/.*/CLEAN/"
+'
+
+test_expect_success 'autocrlf=true converts on input' '
+ test_config core.autocrlf true &&
+ check_input
+'
+
+test_expect_success 'eol=crlf converts on input' '
+ set_attr eol=crlf &&
+ check_input
+'
+
+test_expect_success 'ident converts on input' '
+ set_attr ident &&
+ check_input
+'
+
+test_expect_success 'user-defined filters convert on input' '
+ set_attr filter=test &&
+ check_input
+'
+
+test_expect_success 'setup output tests' '
+ echo "\$Id\$" >small &&
+ cat small small >large &&
+ git add small large &&
+ git config core.bigfilethreshold 7 &&
+ git config filter.test.smudge "sed s/.*/SMUDGE/"
+'
+
+test_expect_success 'autocrlf=true converts on output' '
+ test_config core.autocrlf true &&
+ check_output
+'
+
+test_expect_success 'eol=crlf converts on output' '
+ set_attr eol=crlf &&
+ check_output
+'
+
+test_expect_success 'user-defined filters convert on output' '
+ set_attr filter=test &&
+ check_output
+'
+
+test_expect_success 'ident converts on output' '
+ set_attr ident &&
+ rm -f small large &&
+ git checkout small large &&
+ sed -n "s/Id: .*/Id: SHA/p" <small >small.clean &&
+ head -n 1 large >large.head &&
+ sed -n "s/Id: .*/Id: SHA/p" <large.head >large.clean &&
+ test_cmp small.clean large.clean
+'
+
+test_done
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
new file mode 100755
index 0000000..3f87051
--- /dev/null
+++ b/t/t1060-object-corruption.sh
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+test_description='see how we handle various forms of corruption'
+. ./test-lib.sh
+
+# convert "1234abcd" to ".git/objects/12/34abcd"
+obj_to_file() {
+ echo "$(git rev-parse --git-dir)/objects/$(git rev-parse "$1" | sed 's,..,&/,')"
+}
+
+# Convert byte at offset "$2" of object "$1" into '\0'
+corrupt_byte() {
+ obj_file=$(obj_to_file "$1") &&
+ chmod +w "$obj_file" &&
+ printf '\0' | dd of="$obj_file" bs=1 seek="$2" conv=notrunc
+}
+
+test_expect_success 'setup corrupt repo' '
+ git init bit-error &&
+ (
+ cd bit-error &&
+ test_commit content &&
+ corrupt_byte HEAD:content.t 10
+ )
+'
+
+test_expect_success 'setup repo with missing object' '
+ git init missing &&
+ (
+ cd missing &&
+ test_commit content &&
+ rm -f "$(obj_to_file HEAD:content.t)"
+ )
+'
+
+test_expect_success 'setup repo with misnamed object' '
+ git init misnamed &&
+ (
+ cd misnamed &&
+ test_commit content &&
+ good=$(obj_to_file HEAD:content.t) &&
+ blob=$(echo corrupt | git hash-object -w --stdin) &&
+ bad=$(obj_to_file $blob) &&
+ rm -f "$good" &&
+ mv "$bad" "$good"
+ )
+'
+
+test_expect_success 'streaming a corrupt blob fails' '
+ (
+ cd bit-error &&
+ test_must_fail git cat-file blob HEAD:content.t
+ )
+'
+
+test_expect_success 'read-tree -u detects bit-errors in blobs' '
+ (
+ cd bit-error &&
+ rm -f content.t &&
+ test_must_fail git read-tree --reset -u HEAD
+ )
+'
+
+test_expect_success 'read-tree -u detects missing objects' '
+ (
+ cd missing &&
+ rm -f content.t &&
+ test_must_fail git read-tree --reset -u HEAD
+ )
+'
+
+# We use --bare to make sure that the transport detects it, not the checkout
+# phase.
+test_expect_success 'clone --no-local --bare detects corruption' '
+ test_must_fail git clone --no-local --bare bit-error corrupt-transport
+'
+
+test_expect_success 'clone --no-local --bare detects missing object' '
+ test_must_fail git clone --no-local --bare missing missing-transport
+'
+
+test_expect_success 'clone --no-local --bare detects misnamed object' '
+ test_must_fail git clone --no-local --bare misnamed misnamed-transport
+'
+
+# We do not expect --local to detect corruption at the transport layer,
+# so we are really checking the checkout() code path.
+test_expect_success 'clone --local detects corruption' '
+ test_must_fail git clone --local bit-error corrupt-checkout
+'
+
+test_expect_success 'error detected during checkout leaves repo intact' '
+ test_path_is_dir corrupt-checkout/.git
+'
+
+test_expect_success 'clone --local detects missing objects' '
+ test_must_fail git clone --local missing missing-checkout
+'
+
+test_expect_failure 'clone --local detects misnamed objects' '
+ test_must_fail git clone --local misnamed misnamed-checkout
+'
+
+test_done
diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh
new file mode 100755
index 0000000..1f61eb3
--- /dev/null
+++ b/t/t1090-sparse-checkout-scope.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+test_description='sparse checkout scope tests'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo "initial" >a &&
+ echo "initial" >b &&
+ echo "initial" >c &&
+ git add a b c &&
+ git commit -m "initial commit"
+'
+
+test_expect_success 'create feature branch' '
+ git checkout -b feature &&
+ echo "modified" >b &&
+ echo "modified" >c &&
+ git add b c &&
+ git commit -m "modification"
+'
+
+test_expect_success 'perform sparse checkout of master' '
+ git config --local --bool core.sparsecheckout true &&
+ echo "!/*" >.git/info/sparse-checkout &&
+ echo "/a" >>.git/info/sparse-checkout &&
+ echo "/c" >>.git/info/sparse-checkout &&
+ git checkout master &&
+ test_path_is_file a &&
+ test_path_is_missing b &&
+ test_path_is_file c
+'
+
+test_expect_success 'merge feature branch into sparse checkout of master' '
+ git merge feature &&
+ test_path_is_file a &&
+ test_path_is_missing b &&
+ test_path_is_file c &&
+ test "$(cat c)" = "modified"
+'
+
+test_expect_success 'return to full checkout of master' '
+ git checkout feature &&
+ echo "/*" >.git/info/sparse-checkout &&
+ git checkout master &&
+ test_path_is_file a &&
+ test_path_is_file b &&
+ test_path_is_file c &&
+ test "$(cat b)" = "modified"
+'
+
+test_done
diff --git a/t/t1100-commit-tree-options.sh b/t/t1100-commit-tree-options.sh
index c4414ff..b7e9b4fc 100755
--- a/t/t1100-commit-tree-options.sh
+++ b/t/t1100-commit-tree-options.sh
@@ -7,6 +7,9 @@ test_description='git commit-tree options test
This test checks that git commit-tree can create a specific commit
object by defining all environment variables that it understands.
+
+Also make sure that command line parser understands the normal
+"flags first and then non flag arguments" command line.
'
. ./test-lib.sh
@@ -32,14 +35,29 @@ test_expect_success \
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'
+ TZ=GMT git commit-tree $(cat treeid) >commitid 2>/dev/null'
test_expect_success \
'read commit' \
- 'git cat-file commit `cat commitid` >commit'
+ 'git cat-file commit $(cat commitid) >commit'
test_expect_success \
'compare commit' \
'test_cmp expected commit'
+
+test_expect_success 'flags and then non flags' '
+ test_tick &&
+ echo comment text |
+ git commit-tree $(cat treeid) >commitid &&
+ echo comment text |
+ git commit-tree $(cat treeid) -p $(cat commitid) >childid-1 &&
+ echo comment text |
+ git commit-tree -p $(cat commitid) $(cat treeid) >childid-2 &&
+ test_cmp childid-1 childid-2 &&
+ git commit-tree $(cat treeid) -m foo >childid-3 &&
+ git commit-tree -m foo $(cat treeid) >childid-4 &&
+ test_cmp childid-3 childid-4
+'
+
test_done
diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh
index ab55eda..397ccb6 100755
--- a/t/t1200-tutorial.sh
+++ b/t/t1200-tutorial.sh
@@ -42,7 +42,7 @@ test_expect_success 'git diff' '
'
test_expect_success 'tree' '
- tree=$(git write-tree 2>/dev/null)
+ tree=$(git write-tree 2>/dev/null) &&
test 8988da15d077d4829fc51d8544c097def6644dbb = $tree
'
@@ -154,17 +154,20 @@ test_expect_success 'git show-branch' '
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(-)
+ example | 1 +
+ hello | 1 +
+ 2 files changed, 2 insertions(+)
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
+ -e "s/^Fast[- ]forward /FASTFORWARD /" >resolve.output
+'
+
+test_expect_success 'git resolve output' '
+ test_i18ncmp resolve.expect resolve.output
'
cat > show-branch2.expect << EOF
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 64f0508..d934a24 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -7,28 +7,28 @@ test_description='Test git config in different settings'
. ./test-lib.sh
-test -f .git/config && rm .git/config
-
-git config core.penguin "little blue"
+test_expect_success 'clear default config' '
+ rm -f .git/config
+'
cat > expect << EOF
[core]
penguin = little blue
EOF
-
-test_expect_success 'initial' 'cmp .git/config expect'
-
-git config Core.Movie BadPhysics
+test_expect_success 'initial' '
+ git config core.penguin "little blue" &&
+ test_cmp expect .git/config
+'
cat > expect << EOF
[core]
penguin = little blue
Movie = BadPhysics
EOF
-
-test_expect_success 'mixed case' 'cmp .git/config expect'
-
-git config Cores.WhatEver Second
+test_expect_success 'mixed case' '
+ git config Core.Movie BadPhysics &&
+ test_cmp expect .git/config
+'
cat > expect << EOF
[core]
@@ -37,10 +37,10 @@ cat > expect << EOF
[Cores]
WhatEver = Second
EOF
-
-test_expect_success 'similar section' 'cmp .git/config expect'
-
-git config CORE.UPPERCASE true
+test_expect_success 'similar section' '
+ git config Cores.WhatEver Second &&
+ test_cmp expect .git/config
+'
cat > expect << EOF
[core]
@@ -50,14 +50,18 @@ cat > expect << EOF
[Cores]
WhatEver = Second
EOF
+test_expect_success 'uppercase section' '
+ git config CORE.UPPERCASE true &&
+ test_cmp expect .git/config
+'
-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' '
+ git config core.penguin kingpin !blue
+'
-test_expect_success 'replace with non-match (actually matching)' \
- 'git config core.penguin "very blue" !kingpin'
+test_expect_success 'replace with non-match (actually matching)' '
+ git config core.penguin "very blue" !kingpin
+'
cat > expect << EOF
[core]
@@ -69,7 +73,34 @@ cat > expect << EOF
WhatEver = Second
EOF
-test_expect_success 'non-match result' 'cmp .git/config expect'
+test_expect_success 'non-match result' 'test_cmp expect .git/config'
+
+test_expect_success 'find mixed-case key by canonical name' '
+ echo Second >expect &&
+ git config cores.whatever >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'find mixed-case key by non-canonical name' '
+ echo Second >expect &&
+ git config CoReS.WhAtEvEr >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'subsections are not canonicalized by git-config' '
+ cat >>.git/config <<-\EOF &&
+ [section.SubSection]
+ key = one
+ [section "SubSection"]
+ key = two
+ EOF
+ echo one >expect &&
+ git config section.subsection.key >actual &&
+ test_cmp expect actual &&
+ echo two >expect &&
+ git config section.SubSection.key >actual &&
+ test_cmp expect actual
+'
cat > .git/config <<\EOF
[alpha]
@@ -79,8 +110,9 @@ baz = multiple \
lines
EOF
-test_expect_success 'unset with cont. lines' \
- 'git config --unset beta.baz'
+test_expect_success 'unset with cont. lines' '
+ git config --unset beta.baz
+'
cat > expect <<\EOF
[alpha]
@@ -88,7 +120,7 @@ bar = foo
[beta]
EOF
-test_expect_success 'unset with cont. lines is correct' 'cmp .git/config expect'
+test_expect_success 'unset with cont. lines is correct' 'test_cmp expect .git/config'
cat > .git/config << EOF
[beta] ; silly comment # another comment
@@ -104,8 +136,9 @@ EOF
cp .git/config .git/config2
-test_expect_success 'multiple unset' \
- 'git config --unset-all beta.haha'
+test_expect_success 'multiple unset' '
+ git config --unset-all beta.haha
+'
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -116,7 +149,9 @@ noIndent= sillyValue ; 'nother silly comment
[nextSection] noNewline = ouch
EOF
-test_expect_success 'multiple unset is correct' 'cmp .git/config expect'
+test_expect_success 'multiple unset is correct' '
+ test_cmp expect .git/config
+'
cp .git/config2 .git/config
@@ -127,8 +162,9 @@ test_expect_success '--replace-all missing value' '
rm .git/config2
-test_expect_success '--replace-all' \
- 'git config --replace-all beta.haha gamma'
+test_expect_success '--replace-all' '
+ git config --replace-all beta.haha gamma
+'
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -140,9 +176,9 @@ noIndent= sillyValue ; 'nother silly comment
[nextSection] noNewline = ouch
EOF
-test_expect_success 'all replaced' 'cmp .git/config expect'
-
-git config beta.haha alpha
+test_expect_success 'all replaced' '
+ test_cmp expect .git/config
+'
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -153,10 +189,10 @@ noIndent= sillyValue ; 'nother silly comment
haha = alpha
[nextSection] noNewline = ouch
EOF
-
-test_expect_success 'really mean test' 'cmp .git/config expect'
-
-git config nextsection.nonewline wow
+test_expect_success 'really mean test' '
+ git config beta.haha alpha &&
+ test_cmp expect .git/config
+'
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -168,11 +204,16 @@ noIndent= sillyValue ; 'nother silly comment
[nextSection]
nonewline = wow
EOF
+test_expect_success 'really really mean test' '
+ git config nextsection.nonewline wow &&
+ test_cmp expect .git/config
+'
-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
+test_expect_success 'get value' '
+ echo alpha >expect &&
+ git config beta.haha >actual &&
+ test_cmp expect actual
+'
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -183,10 +224,10 @@ noIndent= sillyValue ; 'nother silly comment
[nextSection]
nonewline = wow
EOF
-
-test_expect_success 'unset' 'cmp .git/config expect'
-
-git config nextsection.NoNewLine "wow2 for me" "for me$"
+test_expect_success 'unset' '
+ git config --unset beta.haha &&
+ test_cmp expect .git/config
+'
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -198,23 +239,35 @@ noIndent= sillyValue ; 'nother silly comment
nonewline = wow
NoNewLine = wow2 for me
EOF
+test_expect_success 'multivar' '
+ git config nextsection.NoNewLine "wow2 for me" "for me$" &&
+ test_cmp expect .git/config
+'
-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 'non-match' '
+ git config --get nextsection.nonewline !for
+'
-test_expect_success 'ambiguous get' '
- test_must_fail git config --get nextsection.nonewline
+test_expect_success 'non-match value' '
+ echo wow >expect &&
+ git config --get nextsection.nonewline !for >actual &&
+ test_cmp expect actual
'
-test_expect_success 'get multivar' \
- 'git config --get-all nextsection.nonewline'
+test_expect_success 'multi-valued get returns final one' '
+ echo "wow2 for me" >expect &&
+ git config --get nextsection.nonewline >actual &&
+ test_cmp expect actual
+'
-git config nextsection.nonewline "wow3" "wow$"
+test_expect_success 'multi-valued get-all returns all' '
+ cat >expect <<-\EOF &&
+ wow
+ wow2 for me
+ EOF
+ git config --get-all nextsection.nonewline >actual &&
+ test_cmp expect actual
+'
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -226,11 +279,9 @@ noIndent= sillyValue ; 'nother silly comment
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 'multivar replace' '
+ git config nextsection.nonewline "wow3" "wow$" &&
+ test_cmp expect .git/config
'
test_expect_success 'ambiguous unset' '
@@ -241,8 +292,6 @@ 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
@@ -253,14 +302,18 @@ noIndent= sillyValue ; 'nother silly comment
NoNewLine = wow2 for me
EOF
-test_expect_success 'multivar unset' 'cmp .git/config expect'
+test_expect_success 'multivar unset' '
+ git config --unset nextsection.nonewline "wow3$" &&
+ test_cmp expect .git/config
+'
test_expect_success 'invalid key' 'test_must_fail git config inval.2key blabla'
test_expect_success 'correct key' 'git config 123456.a123 987'
-test_expect_success 'hierarchical section' \
- 'git config Version.1.2.3eX.Alpha beta'
+test_expect_success 'hierarchical section' '
+ git config Version.1.2.3eX.Alpha beta
+'
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -276,7 +329,9 @@ noIndent= sillyValue ; 'nother silly comment
Alpha = beta
EOF
-test_expect_success 'hierarchical section value' 'cmp .git/config expect'
+test_expect_success 'hierarchical section value' '
+ test_cmp expect .git/config
+'
cat > expect << EOF
beta.noindent=sillyValue
@@ -285,26 +340,60 @@ nextsection.nonewline=wow2 for me
version.1.2.3eX.alpha=beta
EOF
-test_expect_success 'working --list' \
- 'git config --list > output && cmp output expect'
+test_expect_success 'working --list' '
+ git config --list > output &&
+ test_cmp expect output
+'
+cat > expect << EOF
+EOF
+
+test_expect_success '--list without repo produces empty output' '
+ git --git-dir=nonexistent config --list >output &&
+ test_cmp expect output
+'
+
+cat > expect << EOF
+beta.noindent
+nextsection.nonewline
+123456.a123
+version.1.2.3eX.alpha
+EOF
+
+test_expect_success '--name-only --list' '
+ git config --name-only --list >output &&
+ test_cmp expect output
+'
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'
+test_expect_success '--get-regexp' '
+ git config --get-regexp in >output &&
+ test_cmp expect output
+'
+
+cat > expect << EOF
+beta.noindent
+nextsection.nonewline
+EOF
-git config --add nextsection.nonewline "wow4 for you"
+test_expect_success '--name-only --get-regexp' '
+ git config --name-only --get-regexp in >output &&
+ test_cmp expect output
+'
cat > expect << EOF
wow2 for me
wow4 for you
EOF
-test_expect_success '--add' \
- 'git config --get-all nextsection.nonewline > output && cmp output expect'
+test_expect_success '--add' '
+ git config --add nextsection.nonewline "wow4 for you" &&
+ git config --get-all nextsection.nonewline > output &&
+ test_cmp expect output
+'
cat > .git/config << EOF
[novalue]
@@ -313,39 +402,52 @@ cat > .git/config << EOF
variable =
EOF
-test_expect_success 'get variable with no value' \
- 'git config --get novalue.variable ^$'
+test_expect_success 'get variable with no value' '
+ git config --get novalue.variable ^$
+'
-test_expect_success 'get variable with empty value' \
- 'git config --get emptyvalue.variable ^$'
+test_expect_success 'get variable with empty value' '
+ git config --get emptyvalue.variable ^$
+'
echo novalue.variable > expect
-test_expect_success 'get-regexp variable with no value' \
- 'git config --get-regexp novalue > output &&
- cmp output expect'
+test_expect_success 'get-regexp variable with no value' '
+ git config --get-regexp novalue > output &&
+ test_cmp expect output
+'
+
+echo 'novalue.variable true' > expect
+
+test_expect_success 'get-regexp --bool variable with no value' '
+ git config --bool --get-regexp novalue > output &&
+ test_cmp expect output
+'
echo 'emptyvalue.variable ' > expect
-test_expect_success 'get-regexp variable with empty value' \
- 'git config --get-regexp emptyvalue > output &&
- cmp output expect'
+test_expect_success 'get-regexp variable with empty value' '
+ git config --get-regexp emptyvalue > output &&
+ test_cmp expect output
+'
echo true > expect
-test_expect_success 'get bool variable with no value' \
- 'git config --bool novalue.variable > output &&
- cmp output expect'
+test_expect_success 'get bool variable with no value' '
+ git config --bool novalue.variable > output &&
+ test_cmp expect output
+'
echo false > expect
-test_expect_success 'get bool variable with empty value' \
- 'git config --bool emptyvalue.variable > output &&
- cmp output expect'
+test_expect_success 'get bool variable with empty value' '
+ git config --bool emptyvalue.variable > output &&
+ test_cmp expect output
+'
test_expect_success 'no arguments, but no crash' '
test_must_fail git config >output 2>&1 &&
- grep usage output
+ test_i18ngrep usage output
'
cat > .git/config << EOF
@@ -353,8 +455,6 @@ cat > .git/config << EOF
c = d
EOF
-git config a.x y
-
cat > expect << EOF
[a.b]
c = d
@@ -362,10 +462,10 @@ cat > expect << EOF
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
+test_expect_success 'new section is partial match of another' '
+ git config a.x y &&
+ test_cmp expect .git/config
+'
cat > expect << EOF
[a.b]
@@ -377,10 +477,15 @@ cat > expect << EOF
x = y
EOF
-test_expect_success 'new variable inserts into proper section' 'cmp .git/config expect'
+test_expect_success 'new variable inserts into proper section' '
+ git config b.x y &&
+ git config a.b c &&
+ test_cmp expect .git/config
+'
-test_expect_success 'alternative GIT_CONFIG (non-existing file should fail)' \
- 'test_must_fail git config --file non-existing-config -l'
+test_expect_success 'alternative --file (non-existing file should fail)' '
+ test_must_fail git config --file non-existing-config -l
+'
cat > other-config << EOF
[ein]
@@ -391,25 +496,47 @@ cat > expect << EOF
ein.bahn=strasse
EOF
-GIT_CONFIG=other-config git config -l > output
+test_expect_success 'alternative GIT_CONFIG' '
+ GIT_CONFIG=other-config git config --list >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'alternative GIT_CONFIG (--file)' '
+ git config --file other-config --list >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'alternative GIT_CONFIG (--file=-)' '
+ git config --file - --list <other-config >output &&
+ test_cmp expect output
+'
-test_expect_success 'alternative GIT_CONFIG' 'cmp output expect'
+test_expect_success 'setting a value in stdin is an error' '
+ test_must_fail git config --file - some.value foo
+'
-test_expect_success 'alternative GIT_CONFIG (--file)' \
- 'git config --file other-config -l > output && cmp output expect'
+test_expect_success 'editing stdin is an error' '
+ test_must_fail git config --file - --edit
+'
test_expect_success 'refer config from subdirectory' '
mkdir x &&
(
cd x &&
- echo strasse >expect
+ echo strasse >expect &&
git config --get --file ../other-config ein.bahn >actual &&
test_cmp expect actual
)
'
-GIT_CONFIG=other-config git config anwohner.park ausweis
+test_expect_success 'refer config from subdirectory via --file' '
+ (
+ cd x &&
+ git config --file=../other-config --get ein.bahn >actual &&
+ test_cmp expect actual
+ )
+'
cat > expect << EOF
[ein]
@@ -418,7 +545,10 @@ cat > expect << EOF
park = ausweis
EOF
-test_expect_success '--set in alternative GIT_CONFIG' 'cmp other-config expect'
+test_expect_success '--set in alternative file' '
+ git config --file=other-config anwohner.park ausweis &&
+ test_cmp expect other-config
+'
cat > .git/config << EOF
# Hallo
@@ -431,8 +561,9 @@ cat > .git/config << EOF
weird
EOF
-test_expect_success "rename section" \
- "git config --rename-section branch.eins branch.zwei"
+test_expect_success 'rename section' '
+ git config --rename-section branch.eins branch.zwei
+'
cat > expect << EOF
# Hallo
@@ -445,17 +576,22 @@ cat > expect << EOF
weird
EOF
-test_expect_success "rename succeeded" "test_cmp expect .git/config"
+test_expect_success 'rename succeeded' '
+ test_cmp expect .git/config
+'
-test_expect_success "rename non-existing section" '
+test_expect_success 'rename non-existing section' '
test_must_fail git config --rename-section \
branch."world domination" branch.drei
'
-test_expect_success "rename succeeded" "test_cmp expect .git/config"
+test_expect_success 'rename succeeded' '
+ test_cmp expect .git/config
+'
-test_expect_success "rename another section" \
- 'git config --rename-section branch."1 234 blabl/a" branch.drei'
+test_expect_success 'rename another section' '
+ git config --rename-section branch."1 234 blabl/a" branch.drei
+'
cat > expect << EOF
# Hallo
@@ -468,14 +604,17 @@ cat > expect << EOF
weird
EOF
-test_expect_success "rename succeeded" "test_cmp expect .git/config"
+test_expect_success 'rename succeeded' '
+ test_cmp expect .git/config
+'
cat >> .git/config << EOF
[branch "vier"] z = 1
EOF
-test_expect_success "rename a section with a var on the same line" \
- 'git config --rename-section branch.vier branch.zwei'
+test_expect_success 'rename a section with a var on the same line' '
+ git config --rename-section branch.vier branch.zwei
+'
cat > expect << EOF
# Hallo
@@ -490,13 +629,25 @@ weird
z = 1
EOF
-test_expect_success "rename succeeded" "test_cmp expect .git/config"
+test_expect_success 'rename succeeded' '
+ test_cmp expect .git/config
+'
+
+test_expect_success 'renaming empty section name is rejected' '
+ test_must_fail git config --rename-section branch.zwei ""
+'
+
+test_expect_success 'renaming to bogus section is rejected' '
+ test_must_fail git config --rename-section branch.zwei "bogus name"
+'
cat >> .git/config << EOF
[branch "zwei"] a = 1 [branch "vier"]
EOF
-test_expect_success "remove section" "git config --remove-section branch.zwei"
+test_expect_success 'remove section' '
+ git config --remove-section branch.zwei
+'
cat > expect << EOF
# Hallo
@@ -505,10 +656,9 @@ cat > expect << EOF
weird
EOF
-test_expect_success "section was removed properly" \
- "test_cmp expect .git/config"
-
-rm .git/config
+test_expect_success 'section was removed properly' '
+ test_cmp expect .git/config
+'
cat > expect << EOF
[gitcvs]
@@ -519,39 +669,43 @@ cat > expect << EOF
EOF
test_expect_success 'section ending' '
-
+ rm -f .git/config &&
git config gitcvs.enabled true &&
git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
- cmp .git/config expect
+ test_cmp expect .git/config
'
test_expect_success numbers '
-
git config kilo.gram 1k &&
git config mega.ton 1m &&
- k=$(git config --int --get kilo.gram) &&
- test z1024 = "z$k" &&
- m=$(git config --int --get mega.ton) &&
- test z1048576 = "z$m"
+ echo 1024 >expect &&
+ echo 1048576 >>expect &&
+ git config --int --get kilo.gram >actual &&
+ git config --int --get mega.ton >>actual &&
+ test_cmp expect actual
'
-cat > expect <<EOF
-fatal: bad config value for 'aninvalid.unit' in .git/config
-EOF
+test_expect_success '--int is at least 64 bits' '
+ git config giga.watts 121g &&
+ echo 129922760704 >expect &&
+ git config --int --get giga.watts >actual &&
+ test_cmp expect actual
+'
test_expect_success 'invalid unit' '
-
git config aninvalid.unit "1auto" &&
- s=$(git config aninvalid.unit) &&
- test "z1auto" = "z$s" &&
- if git config --int --get aninvalid.unit 2>actual
- then
- echo config should have failed
- false
- fi &&
- cmp actual expect
+ echo 1auto >expect &&
+ git config aninvalid.unit >actual &&
+ test_cmp expect actual &&
+ test_must_fail git config --int --get aninvalid.unit 2>actual &&
+ test_i18ngrep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
+'
+
+test_expect_success 'invalid stdin config' '
+ echo "[broken" | test_must_fail git config --list --file - >output 2>&1 &&
+ test_i18ngrep "bad config line 1 in standard input" output
'
cat > expect << EOF
@@ -581,7 +735,7 @@ test_expect_success bool '
git config --bool --get bool.true$i >>result
git config --bool --get bool.false$i >>result
done &&
- cmp expect result'
+ test_cmp expect result'
test_expect_success 'invalid bool (--get)' '
@@ -592,8 +746,6 @@ test_expect_success 'invalid bool (set)' '
test_must_fail git config --bool bool.nobool foobar'
-rm .git/config
-
cat > expect <<\EOF
[bool]
true1 = true
@@ -608,6 +760,7 @@ EOF
test_expect_success 'set --bool' '
+ rm -f .git/config &&
git config --bool bool.true1 01 &&
git config --bool bool.true2 -1 &&
git config --bool bool.true3 YeS &&
@@ -616,9 +769,7 @@ test_expect_success 'set --bool' '
git config --bool bool.false2 "" &&
git config --bool bool.false3 nO &&
git config --bool bool.false4 FALSE &&
- cmp expect .git/config'
-
-rm .git/config
+ test_cmp expect .git/config'
cat > expect <<\EOF
[int]
@@ -629,46 +780,43 @@ EOF
test_expect_success 'set --int' '
+ rm -f .git/config &&
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
+ test_cmp expect .git/config
+'
-cat >expect <<\EOF
-[bool]
- true1 = true
+test_expect_success 'get --bool-or-int' '
+ cat >.git/config <<-\EOF &&
+ [bool]
+ true1
true2 = true
- false1 = false
- false2 = false
-[int]
+ false = false
+ [int]
int1 = 0
int2 = 1
int3 = -1
-EOF
+ EOF
+ cat >expect <<-\EOF &&
+ true
+ true
+ false
+ 0
+ 1
+ -1
+ EOF
+ {
+ git config --bool-or-int bool.true1 &&
+ git config --bool-or-int bool.true2 &&
+ git config --bool-or-int bool.false &&
+ git config --bool-or-int int.int1 &&
+ git config --bool-or-int int.int2 &&
+ git config --bool-or-int int.int3
+ } >actual &&
+ test_cmp expect actual
+'
-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
@@ -682,6 +830,7 @@ cat >expect <<\EOF
EOF
test_expect_success 'set --bool-or-int' '
+ rm -f .git/config &&
git config --bool-or-int bool.true1 true &&
git config --bool-or-int bool.false1 false &&
git config --bool-or-int bool.true2 yes &&
@@ -692,8 +841,6 @@ test_expect_success 'set --bool-or-int' '
test_cmp expect .git/config
'
-rm .git/config
-
cat >expect <<\EOF
[path]
home = ~/
@@ -701,31 +848,52 @@ cat >expect <<\EOF
trailingtilde = foo~
EOF
-test_expect_success 'set --path' '
+test_expect_success !MINGW 'set --path' '
+ rm -f .git/config &&
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_have_prereq !MINGW && test "${HOME+set}"
+then
+ test_set_prereq HOMEVAR
+fi
+
cat >expect <<EOF
$HOME/
/dev/null
foo~
EOF
-test_expect_success 'get --path' '
+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
'
-rm .git/config
+cat >expect <<\EOF
+/dev/null
+foo~
+EOF
+
+test_expect_success !MINGW '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
+'
-git config quote.leading " test"
-git config quote.ending "test "
-git config quote.semicolon "test;test"
-git config quote.hash "test#test"
+test_expect_success 'get --path barfs on boolean variable' '
+ echo "[path]bool" >.git/config &&
+ test_must_fail git config --get --path path.bool
+'
cat > expect << EOF
[quote]
@@ -734,8 +902,14 @@ cat > expect << EOF
semicolon = "test;test"
hash = "test#test"
EOF
-
-test_expect_success 'quoting' 'cmp .git/config expect'
+test_expect_success 'quoting' '
+ rm -f .git/config &&
+ git config quote.leading " test" &&
+ git config quote.ending "test " &&
+ git config quote.semicolon "test;test" &&
+ git config quote.hash "test#test" &&
+ test_cmp expect .git/config
+'
test_expect_success 'key with newline' '
test_must_fail git config "key.with
@@ -760,9 +934,10 @@ section.noncont=not continued
section.quotecont=cont;inued
EOF
-git config --list > result
-
-test_expect_success 'value continued on next line' 'cmp result expect'
+test_expect_success 'value continued on next line' '
+ git config --list > result &&
+ test_cmp result expect
+'
cat > .git/config <<\EOF
[section "sub=section"]
@@ -783,35 +958,57 @@ barQsection.sub=section.val3
Qsection.sub=section.val4
Qsection.sub=section.val5Q
EOF
+test_expect_success '--null --list' '
+ git config --null --list >result.raw &&
+ nul_to_q <result.raw >result &&
+ echo >>result &&
+ test_cmp expect result
+'
-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 '--null --get-regexp' '
+ git config --null --get-regexp "val[0-9]" >result.raw &&
+ nul_to_q <result.raw >result &&
+ echo >>result &&
+ test_cmp expect result
+'
test_expect_success 'inner whitespace kept verbatim' '
git config section.val "foo bar" &&
- test "z$(git config section.val)" = "zfoo bar"
+ echo "foo bar" >expect &&
+ git config section.val >actual &&
+ test_cmp expect actual
'
test_expect_success SYMLINKS 'symlinked configuration' '
-
ln -s notyet myconfig &&
- GIT_CONFIG=myconfig git config test.frotz nitfol &&
+ git config --file=myconfig 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 "z$(git config --file=notyet test.frotz)" = znitfol &&
+ git config --file=myconfig test.xyzzy rezrov &&
test -h myconfig &&
test -f notyet &&
- test "z$(GIT_CONFIG=notyet git config test.frotz)" = znitfol &&
- test "z$(GIT_CONFIG=notyet git config test.xyzzy)" = zrezrov
+ cat >expect <<-\EOF &&
+ nitfol
+ rezrov
+ EOF
+ {
+ git config --file=notyet test.frotz &&
+ git config --file=notyet test.xyzzy
+ } >actual &&
+ test_cmp expect actual
+'
+test_expect_success 'nonexistent configuration' '
+ test_must_fail git config --file=doesnotexist --list &&
+ test_must_fail git config --file=doesnotexist test.xyzzy
+'
+
+test_expect_success SYMLINKS 'symlink to nonexistent configuration' '
+ ln -s doesnotexist linktonada &&
+ ln -s linktonada linktolinktonada &&
+ test_must_fail git config --file=linktonada --list &&
+ test_must_fail git config --file=linktolinktonada --list
'
test_expect_success 'check split_cmdline return' "
@@ -822,14 +1019,357 @@ test_expect_success 'check split_cmdline return' "
git commit -m 'initial commit' &&
git config branch.master.mergeoptions 'echo \"' &&
test_must_fail git merge master
- "
+"
test_expect_success 'git -c "key=value" support' '
- test "z$(git -c 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
+ cat >expect <<-\EOF &&
+ value
+ value
+ true
+ EOF
+ {
+ git -c core.name=value config core.name &&
+ git -c foo.CamelCase=value config foo.camelcase &&
+ git -c foo.flag config --bool foo.flag
+ } >actual &&
+ test_cmp expect actual &&
+ test_must_fail git -c name=value config core.name
+'
+
+# We just need a type-specifier here that cares about the
+# distinction internally between a NULL boolean and a real
+# string (because most of git's internal parsers do care).
+# Using "--path" works, but we do not otherwise care about
+# its semantics.
+test_expect_success 'git -c can represent empty string' '
+ echo >expect &&
+ git -c foo.empty= config --path foo.empty >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'key sanity-checking' '
+ test_must_fail git config foo=bar &&
+ test_must_fail git config foo=.bar &&
+ test_must_fail git config foo.ba=r &&
+ test_must_fail git config foo.1bar &&
+ test_must_fail git config foo."ba
+ z".bar &&
+ test_must_fail git config . false &&
+ test_must_fail git config .foo false &&
+ test_must_fail git config foo. false &&
+ test_must_fail git config .foo. false &&
+ git config foo.bar true &&
+ git config foo."ba =z".bar false
+'
+
+test_expect_success 'git -c works with aliases of builtins' '
+ git config alias.checkconfig "-c foo.check=bar config foo.check" &&
+ echo bar >expect &&
+ git checkconfig >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git -c does not split values on equals' '
+ echo "value with = in it" >expect &&
+ git -c core.foo="value with = in it" config core.foo >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git -c dies on bogus config' '
+ test_must_fail git -c core.bare=foo rev-parse
+'
+
+test_expect_success 'git -c complains about empty key' '
+ test_must_fail git -c "=foo" rev-parse
+'
+
+test_expect_success 'git -c complains about empty key and value' '
+ test_must_fail git -c "" rev-parse
+'
+
+test_expect_success 'multiple git -c appends config' '
+ test_config alias.x "!git -c x.two=2 config --get-regexp ^x\.*" &&
+ cat >expect <<-\EOF &&
+ x.one 1
+ x.two 2
+ EOF
+ git -c x.one=1 x >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git -c is not confused by empty environment' '
+ GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list
+'
+
+test_expect_success 'git config --edit works' '
+ git config -f tmp test.value no &&
+ echo test.value=yes >expect &&
+ GIT_EDITOR="echo [test]value=yes >" git config -f tmp --edit &&
+ git config -f tmp --list >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git config --edit respects core.editor' '
+ git config -f tmp test.value no &&
+ echo test.value=yes >expect &&
+ test_config core.editor "echo [test]value=yes >" &&
+ git config -f tmp --edit &&
+ git config -f tmp --list >actual &&
+ test_cmp expect actual
+'
+
+# malformed configuration files
+test_expect_success 'barf on syntax error' '
+ cat >.git/config <<-\EOF &&
+ # broken section line
+ [section]
+ key garbage
+ EOF
+ test_must_fail git config --get section.key >actual 2>error &&
+ grep " line 3 " error
+'
+
+test_expect_success 'barf on incomplete section header' '
+ cat >.git/config <<-\EOF &&
+ # broken section line
+ [section
+ key = value
+ EOF
+ test_must_fail git config --get section.key >actual 2>error &&
+ grep " line 2 " error
+'
+
+test_expect_success 'barf on incomplete string' '
+ cat >.git/config <<-\EOF &&
+ # broken section line
+ [section]
+ key = "value string
+ EOF
+ test_must_fail git config --get section.key >actual 2>error &&
+ grep " line 3 " error
+'
+
+test_expect_success 'urlmatch' '
+ cat >.git/config <<-\EOF &&
+ [http]
+ sslVerify
+ [http "https://weak.example.com"]
+ sslVerify = false
+ cookieFile = /tmp/cookie.txt
+ EOF
+
+ test_expect_code 1 git config --bool --get-urlmatch doesnt.exist https://good.example.com >actual &&
+ test_must_be_empty actual &&
+
+ echo true >expect &&
+ git config --bool --get-urlmatch http.SSLverify https://good.example.com >actual &&
+ test_cmp expect actual &&
+
+ echo false >expect &&
+ git config --bool --get-urlmatch http.sslverify https://weak.example.com >actual &&
+ test_cmp expect actual &&
+
+ {
+ echo http.cookiefile /tmp/cookie.txt &&
+ echo http.sslverify false
+ } >expect &&
+ git config --get-urlmatch HTTP https://weak.example.com >actual &&
+ test_cmp expect actual
+'
+
+# good section hygiene
+test_expect_failure 'unsetting the last key in a section removes header' '
+ cat >.git/config <<-\EOF &&
+ # some generic comment on the configuration file itself
+ # a comment specific to this "section" section.
+ [section]
+ # some intervening lines
+ # that should also be dropped
+
+ key = value
+ # please be careful when you update the above variable
+ EOF
+
+ cat >expect <<-\EOF &&
+ # some generic comment on the configuration file itself
+ EOF
+
+ git config --unset section.key &&
+ test_cmp expect .git/config
+'
+
+test_expect_failure 'adding a key into an empty section reuses header' '
+ cat >.git/config <<-\EOF &&
+ [section]
+ EOF
+
+ q_to_tab >expect <<-\EOF &&
+ [section]
+ Qkey = value
+ EOF
+
+ git config section.key value &&
+ test_cmp expect .git/config
+'
+
+test_expect_success POSIXPERM,PERL 'preserves existing permissions' '
+ chmod 0600 .git/config &&
+ git config imap.pass Hunter2 &&
+ perl -e \
+ "die q(badset) if ((stat(q(.git/config)))[2] & 07777) != 0600" &&
+ git config --rename-section imap pop &&
+ perl -e \
+ "die q(badrename) if ((stat(q(.git/config)))[2] & 07777) != 0600"
+'
+
+! test_have_prereq MINGW ||
+HOME="$(pwd)" # convert to Windows path
+
+test_expect_success 'set up --show-origin tests' '
+ INCLUDE_DIR="$HOME/include" &&
+ mkdir -p "$INCLUDE_DIR" &&
+ cat >"$INCLUDE_DIR"/absolute.include <<-\EOF &&
+ [user]
+ absolute = include
+ EOF
+ cat >"$INCLUDE_DIR"/relative.include <<-\EOF &&
+ [user]
+ relative = include
+ EOF
+ cat >"$HOME"/.gitconfig <<-EOF &&
+ [user]
+ global = true
+ override = global
+ [include]
+ path = "$INCLUDE_DIR/absolute.include"
+ EOF
+ cat >.git/config <<-\EOF
+ [user]
+ local = true
+ override = local
+ [include]
+ path = ../include/relative.include
+ EOF
+'
+
+test_expect_success '--show-origin with --list' '
+ cat >expect <<-EOF &&
+ file:$HOME/.gitconfig user.global=true
+ file:$HOME/.gitconfig user.override=global
+ file:$HOME/.gitconfig include.path=$INCLUDE_DIR/absolute.include
+ file:$INCLUDE_DIR/absolute.include user.absolute=include
+ file:.git/config user.local=true
+ file:.git/config user.override=local
+ file:.git/config include.path=../include/relative.include
+ file:.git/../include/relative.include user.relative=include
+ command line: user.cmdline=true
+ EOF
+ git -c user.cmdline=true config --list --show-origin >output &&
+ test_cmp expect output
+'
+
+test_expect_success '--show-origin with --list --null' '
+ cat >expect <<-EOF &&
+ file:$HOME/.gitconfigQuser.global
+ trueQfile:$HOME/.gitconfigQuser.override
+ globalQfile:$HOME/.gitconfigQinclude.path
+ $INCLUDE_DIR/absolute.includeQfile:$INCLUDE_DIR/absolute.includeQuser.absolute
+ includeQfile:.git/configQuser.local
+ trueQfile:.git/configQuser.override
+ localQfile:.git/configQinclude.path
+ ../include/relative.includeQfile:.git/../include/relative.includeQuser.relative
+ includeQcommand line:Quser.cmdline
+ trueQ
+ EOF
+ git -c user.cmdline=true config --null --list --show-origin >output.raw &&
+ nul_to_q <output.raw >output &&
+ # The here-doc above adds a newline that the --null output would not
+ # include. Add it here to make the two comparable.
+ echo >>output &&
+ test_cmp expect output
+'
+
+test_expect_success '--show-origin with single file' '
+ cat >expect <<-\EOF &&
+ file:.git/config user.local=true
+ file:.git/config user.override=local
+ file:.git/config include.path=../include/relative.include
+ EOF
+ git config --local --list --show-origin >output &&
+ test_cmp expect output
+'
+
+test_expect_success '--show-origin with --get-regexp' '
+ cat >expect <<-EOF &&
+ file:$HOME/.gitconfig user.global true
+ file:.git/config user.local true
+ EOF
+ git config --show-origin --get-regexp "user\.[g|l].*" >output &&
+ test_cmp expect output
+'
+
+test_expect_success '--show-origin getting a single key' '
+ cat >expect <<-\EOF &&
+ file:.git/config local
+ EOF
+ git config --show-origin user.override >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'set up custom config file' '
+ CUSTOM_CONFIG_FILE="file\" (dq) and spaces.conf" &&
+ cat >"$CUSTOM_CONFIG_FILE" <<-\EOF
+ [user]
+ custom = true
+ EOF
+'
+
+test_expect_success !MINGW '--show-origin escape special file name characters' '
+ cat >expect <<-\EOF &&
+ file:"file\" (dq) and spaces.conf" user.custom=true
+ EOF
+ git config --file "$CUSTOM_CONFIG_FILE" --show-origin --list >output &&
+ test_cmp expect output
+'
+
+test_expect_success '--show-origin stdin' '
+ cat >expect <<-\EOF &&
+ standard input: user.custom=true
+ EOF
+ git config --file - --show-origin --list <"$CUSTOM_CONFIG_FILE" >output &&
+ test_cmp expect output
+'
+
+test_expect_success '--show-origin stdin with file include' '
+ cat >"$INCLUDE_DIR"/stdin.include <<-EOF &&
+ [user]
+ stdin = include
+ EOF
+ cat >expect <<-EOF &&
+ file:$INCLUDE_DIR/stdin.include include
+ EOF
+ echo "[include]path=\"$INCLUDE_DIR\"/stdin.include" \
+ | git config --show-origin --includes --file - user.stdin >output &&
+ test_cmp expect output
+'
+
+test_expect_success !MINGW '--show-origin blob' '
+ cat >expect <<-\EOF &&
+ blob:a9d9f9e555b5c6f07cbe09d3f06fe3df11e09c08 user.custom=true
+ EOF
+ blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
+ git config --blob=$blob --show-origin --list >output &&
+ test_cmp expect output
+'
+
+test_expect_success !MINGW '--show-origin blob ref' '
+ cat >expect <<-\EOF &&
+ blob:"master:file\" (dq) and spaces.conf" user.custom=true
+ EOF
+ git add "$CUSTOM_CONFIG_FILE" &&
+ git commit -m "new config file" &&
+ git config --blob=master:"$CUSTOM_CONFIG_FILE" --show-origin --list >output &&
+ test_cmp expect output
'
test_done
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index de42d21..ac10875 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -12,12 +12,11 @@ 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)' '
+ test_when_finished "rm -rf sub" &&
mkdir sub && (
- cd sub && git init --shared=0400
+ cd sub &&
+ test_must_fail git init --shared=0400
)
- ret="$?"
- rm -rf sub
- test $ret != "0"
'
modebits () {
@@ -33,7 +32,7 @@ do
git init --shared=1 &&
test 1 = "$(git config core.sharedrepository)"
) &&
- actual=$(ls -l sub/.git/HEAD)
+ actual=$(ls -l sub/.git/HEAD) &&
case "$actual" in
-rw-rw-r--*)
: happy
@@ -90,10 +89,8 @@ do
rm -f .git/info/refs &&
git update-server-info &&
actual="$(modebits .git/info/refs)" &&
- test "x$actual" = "x-$y" || {
- ls -lt .git/info
- false
- }
+ verbose test "x$actual" = "x-$y"
+
'
umask 077 &&
@@ -102,16 +99,24 @@ do
rm -f .git/info/refs &&
git update-server-info &&
actual="$(modebits .git/info/refs)" &&
- test "x$actual" = "x-$x" || {
- ls -lt .git/info
- false
- }
+ verbose test "x$actual" = "x-$x"
'
done
+test_expect_success POSIXPERM 'info/refs respects umask in unshared repo' '
+ rm -f .git/info/refs &&
+ test_unconfig core.sharedrepository &&
+ umask 002 &&
+ git update-server-info &&
+ echo "-rw-rw-r--" >expect &&
+ modebits .git/info/refs >actual &&
+ test_cmp expect actual
+'
+
test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' '
+ umask 077 &&
git config core.sharedRepository group &&
git reflog expire --all &&
actual="$(ls -l .git/logs/refs/heads/master)" &&
diff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh
index 8d305b4..9bcd349 100755
--- a/t/t1302-repo-version.sh
+++ b/t/t1302-repo-version.sh
@@ -7,41 +7,124 @@ 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"
+test_expect_success 'setup' '
+ 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
-GIT_CONFIG=test2/.git/config git config core.repositoryformatversion 99 || exit 1
+ test_create_repo "test" &&
+ test_create_repo "test2" &&
+ git config --file=test2/.git/config core.repositoryformatversion 99
+'
test_expect_success 'gitdir selection on normal repos' '
- (test "$(git config core.repositoryformatversion)" = 0 &&
- cd test &&
- test "$(git config core.repositoryformatversion)" = 0)'
+ echo 0 >expect &&
+ git config core.repositoryformatversion >actual &&
+ (
+ cd test &&
+ git config core.repositoryformatversion >../actual2
+ ) &&
+ test_cmp expect actual &&
+ test_cmp expect actual2
+'
-# 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)'
+ # Make sure it would stop at test2, not trash
+ echo 99 >expect &&
+ (
+ cd test2 &&
+ git config core.repositoryformatversion >../actual
+ ) &&
+ test_cmp expect actual
+'
test_expect_success 'gitdir not required mode' '
- (git apply --stat test.patch &&
- cd test && git apply --stat ../test.patch &&
- cd ../test2 && git apply --stat ../test.patch)'
+ git apply --stat test.patch &&
+ (
+ cd test &&
+ git apply --stat ../test.patch
+ ) &&
+ (
+ cd test2 &&
+ git apply --stat ../test.patch
+ )
+'
+
+test_expect_success 'gitdir required mode' '
+ git apply --check --index test.patch &&
+ (
+ cd test &&
+ git apply --check --index ../test.patch
+ ) &&
+ (
+ cd test2 &&
+ test_must_fail git apply --check --index ../test.patch
+ )
+'
+
+check_allow () {
+ git rev-parse --git-dir >actual &&
+ echo .git >expect &&
+ test_cmp expect actual
+}
+
+check_abort () {
+ test_must_fail git rev-parse --git-dir
+}
+
+# avoid git-config, since it cannot be trusted to run
+# in a repository with a broken version
+mkconfig () {
+ echo '[core]' &&
+ echo "repositoryformatversion = $1" &&
+ shift &&
-test_expect_success 'gitdir required mode on normal repos' '
- (git apply --check --index test.patch &&
- cd test && git apply --check --index ../test.patch)'
+ if test $# -gt 0; then
+ echo '[extensions]' &&
+ for i in "$@"; do
+ echo "$i"
+ done
+ fi
+}
+
+while read outcome version extensions; do
+ test_expect_success "$outcome version=$version $extensions" "
+ mkconfig $version $extensions >.git/config &&
+ check_${outcome}
+ "
+done <<\EOF
+allow 0
+allow 1
+allow 1 noop
+abort 1 no-such-extension
+allow 0 no-such-extension
+EOF
+
+test_expect_success 'precious-objects allowed' '
+ mkconfig 1 preciousObjects >.git/config &&
+ check_allow
+'
+
+test_expect_success 'precious-objects blocks destructive repack' '
+ test_must_fail git repack -ad
+'
+
+test_expect_success 'other repacks are OK' '
+ test_commit foo &&
+ git repack
+'
+
+test_expect_success 'precious-objects blocks prune' '
+ test_must_fail git prune
+'
-test_expect_success 'gitdir required mode on unsupported repo' '
- (cd test2 && test_must_fail git apply --check --index ../test.patch)
+test_expect_success 'gc runs without complaint' '
+ git gc
'
test_done
diff --git a/t/t1303-wacky-config.sh b/t/t1303-wacky-config.sh
index 080117c..3b92083 100755
--- a/t/t1303-wacky-config.sh
+++ b/t/t1303-wacky-config.sh
@@ -3,17 +3,28 @@
test_description='Test wacky input to git config'
. ./test-lib.sh
+# Leaving off the newline is intentional!
setup() {
(printf "[section]\n" &&
printf " key = foo") >.git/config
}
+# 'check section.key value' verifies that the entry for section.key is
+# 'value'
check() {
echo "$2" >expected
git config --get "$1" >actual 2>&1
test_cmp actual expected
}
+# 'check section.key regex value' verifies that the entry for
+# section.key *that matches 'regex'* is 'value'
+check_regex() {
+ echo "$3" >expected
+ git config --get "$1" "$2" >actual 2>&1
+ test_cmp actual expected
+}
+
test_expect_success 'modify same key' '
setup &&
git config section.key bar &&
@@ -44,7 +55,80 @@ 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"
+ check section.key "$LONG_VALUE"
+'
+
+setup_many() {
+ setup &&
+ # This time we want the newline so that we can tack on more
+ # entries.
+ echo >>.git/config &&
+ # Semi-efficient way of concatenating 5^5 = 3125 lines. Note
+ # that because 'setup' already put one line, this means 3126
+ # entries for section.key in the config file.
+ cat >5to1 <<-\EOF &&
+ key = foo
+ key = foo
+ key = foo
+ key = foo
+ key = foo
+ EOF
+ cat 5to1 5to1 5to1 5to1 5to1 >5to2 && # 25
+ cat 5to2 5to2 5to2 5to2 5to2 >5to3 && # 125
+ cat 5to3 5to3 5to3 5to3 5to3 >5to4 && # 635
+ cat 5to4 5to4 5to4 5to4 5to4 >>.git/config # 3125
+}
+
+test_expect_success 'get many entries' '
+ setup_many &&
+ git config --get-all section.key >actual &&
+ test_line_count = 3126 actual
+'
+
+test_expect_success 'get many entries by regex' '
+ setup_many &&
+ git config --get-regexp "sec.*ke." >actual &&
+ test_line_count = 3126 actual
+'
+
+test_expect_success 'add and replace one of many entries' '
+ setup_many &&
+ git config --add section.key bar &&
+ check_regex section.key "b.*r" bar &&
+ git config section.key beer "b.*r" &&
+ check_regex section.key "b.*r" beer
+'
+
+test_expect_success 'replace many entries' '
+ setup_many &&
+ git config --replace-all section.key bar &&
+ check section.key bar
+'
+
+test_expect_success 'unset many entries' '
+ setup_many &&
+ git config --unset-all section.key &&
+ test_must_fail git config section.key
+'
+
+test_expect_success '--add appends new value after existing empty value' '
+ cat >expect <<-\EOF &&
+
+
+ fool
+ roll
+ EOF
+ cp .git/config .git/config.old &&
+ test_when_finished "mv .git/config.old .git/config" &&
+ cat >.git/config <<-\EOF &&
+ [foo]
+ baz
+ baz =
+ baz = fool
+ EOF
+ git config --add foo.baz roll &&
+ git config --get-all foo.baz >output &&
+ test_cmp expect output
'
test_done
diff --git a/t/t1304-default-acl.sh b/t/t1304-default-acl.sh
index 97ab02a..f5422f1 100755
--- a/t/t1304-default-acl.sh
+++ b/t/t1304-default-acl.sh
@@ -14,13 +14,19 @@ umask 077
# We need an arbitrary other user give permission to using ACLs. root
# is a good candidate: exists on all unices, and it has permission
# anyway, so we don't create a security hole running the testsuite.
+test_expect_success 'checking for a working acl setup' '
+ if setfacl -m d:m:rwx -m u:root:rwx . &&
+ getfacl . | grep user:root:rwx &&
+ touch should-have-readable-acl &&
+ getfacl should-have-readable-acl | egrep "mask::?rw-"
+ then
+ test_set_prereq SETFACL
+ fi
+'
-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
+if test -z "$LOGNAME"
+then
+ LOGNAME="${USER:-$(id -u -n)}"
fi
check_perms_and_acl () {
@@ -34,7 +40,7 @@ check_perms_and_acl () {
dirs_to_set="./ .git/ .git/objects/ .git/objects/pack/"
-test_expect_success 'Setup test repo' '
+test_expect_success SETFACL '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 &&
@@ -46,12 +52,12 @@ test_expect_success 'Setup test repo' '
git commit -m "init"
'
-test_expect_success 'Objects creation does not break ACLs with restrictive umask' '
+test_expect_success SETFACL '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' '
+test_expect_success SETFACL 'git gc does not break ACLs with restrictive umask' '
git gc &&
check_perms_and_acl .git/objects/pack/*.pack
'
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
new file mode 100755
index 0000000..9ba2ba1
--- /dev/null
+++ b/t/t1305-config-include.sh
@@ -0,0 +1,172 @@
+#!/bin/sh
+
+test_description='test config file include directives'
+. ./test-lib.sh
+
+test_expect_success 'include file by absolute path' '
+ echo "[test]one = 1" >one &&
+ echo "[include]path = \"$(pwd)/one\"" >.gitconfig &&
+ echo 1 >expect &&
+ git config test.one >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'include file by relative path' '
+ echo "[test]one = 1" >one &&
+ echo "[include]path = one" >.gitconfig &&
+ echo 1 >expect &&
+ git config test.one >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'chained relative paths' '
+ mkdir subdir &&
+ echo "[test]three = 3" >subdir/three &&
+ echo "[include]path = three" >subdir/two &&
+ echo "[include]path = subdir/two" >.gitconfig &&
+ echo 3 >expect &&
+ git config test.three >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'include paths get tilde-expansion' '
+ echo "[test]one = 1" >one &&
+ echo "[include]path = ~/one" >.gitconfig &&
+ echo 1 >expect &&
+ git config test.one >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'include options can still be examined' '
+ echo "[test]one = 1" >one &&
+ echo "[include]path = one" >.gitconfig &&
+ echo one >expect &&
+ git config include.path >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'listing includes option and expansion' '
+ echo "[test]one = 1" >one &&
+ echo "[include]path = one" >.gitconfig &&
+ cat >expect <<-\EOF &&
+ include.path=one
+ test.one=1
+ EOF
+ git config --list >actual.full &&
+ grep -v ^core actual.full >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'single file lookup does not expand includes by default' '
+ echo "[test]one = 1" >one &&
+ echo "[include]path = one" >.gitconfig &&
+ test_must_fail git config -f .gitconfig test.one &&
+ test_must_fail git config --global test.one &&
+ echo 1 >expect &&
+ git config --includes -f .gitconfig test.one >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'single file list does not expand includes by default' '
+ echo "[test]one = 1" >one &&
+ echo "[include]path = one" >.gitconfig &&
+ echo "include.path=one" >expect &&
+ git config -f .gitconfig --list >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'writing config file does not expand includes' '
+ echo "[test]one = 1" >one &&
+ echo "[include]path = one" >.gitconfig &&
+ git config test.two 2 &&
+ echo 2 >expect &&
+ git config --no-includes test.two >actual &&
+ test_cmp expect actual &&
+ test_must_fail git config --no-includes test.one
+'
+
+test_expect_success 'config modification does not affect includes' '
+ echo "[test]one = 1" >one &&
+ echo "[include]path = one" >.gitconfig &&
+ git config test.one 2 &&
+ echo 1 >expect &&
+ git config -f one test.one >actual &&
+ test_cmp expect actual &&
+ cat >expect <<-\EOF &&
+ 1
+ 2
+ EOF
+ git config --get-all test.one >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'missing include files are ignored' '
+ cat >.gitconfig <<-\EOF &&
+ [include]path = foo
+ [test]value = yes
+ EOF
+ echo yes >expect &&
+ git config test.value >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'absolute includes from command line work' '
+ echo "[test]one = 1" >one &&
+ echo 1 >expect &&
+ git -c include.path="$(pwd)/one" config test.one >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'relative includes from command line fail' '
+ echo "[test]one = 1" >one &&
+ test_must_fail git -c include.path=one config test.one
+'
+
+test_expect_success 'absolute includes from blobs work' '
+ echo "[test]one = 1" >one &&
+ echo "[include]path=$(pwd)/one" >blob &&
+ blob=$(git hash-object -w blob) &&
+ echo 1 >expect &&
+ git config --blob=$blob test.one >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'relative includes from blobs fail' '
+ echo "[test]one = 1" >one &&
+ echo "[include]path=one" >blob &&
+ blob=$(git hash-object -w blob) &&
+ test_must_fail git config --blob=$blob test.one
+'
+
+test_expect_success 'absolute includes from stdin work' '
+ echo "[test]one = 1" >one &&
+ echo 1 >expect &&
+ echo "[include]path=\"$(pwd)/one\"" |
+ git config --file - test.one >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'relative includes from stdin line fail' '
+ echo "[test]one = 1" >one &&
+ echo "[include]path=one" |
+ test_must_fail git config --file - test.one
+'
+
+test_expect_success 'include cycles are detected' '
+ cat >.gitconfig <<-\EOF &&
+ [test]value = gitconfig
+ [include]path = cycle
+ EOF
+ cat >cycle <<-\EOF &&
+ [test]value = cycle
+ [include]path = .gitconfig
+ EOF
+ cat >expect <<-\EOF &&
+ gitconfig
+ cycle
+ EOF
+ test_must_fail git config --get-all test.value 2>stderr &&
+ grep "exceeded maximum include depth" stderr
+'
+
+test_done
diff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh
new file mode 100755
index 0000000..8b14ab1
--- /dev/null
+++ b/t/t1306-xdg-files.sh
@@ -0,0 +1,197 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Valentin Duperray, Lucien Kong, Franck Jonas,
+# Thomas Nguy, Khoi Nguyen
+# Grenoble INP Ensimag
+#
+
+test_description='Compatibility with $XDG_CONFIG_HOME/git/ files'
+
+. ./test-lib.sh
+
+test_expect_success 'read config: xdg file exists and ~/.gitconfig doesn'\''t' '
+ mkdir -p .config/git &&
+ echo "[alias]" >.config/git/config &&
+ echo " myalias = !echo in_config" >>.config/git/config &&
+ echo in_config >expected &&
+ git myalias >actual &&
+ test_cmp expected actual
+'
+
+
+test_expect_success 'read config: xdg file exists and ~/.gitconfig exists' '
+ >.gitconfig &&
+ echo "[alias]" >.gitconfig &&
+ echo " myalias = !echo in_gitconfig" >>.gitconfig &&
+ echo in_gitconfig >expected &&
+ git myalias >actual &&
+ test_cmp expected actual
+'
+
+
+test_expect_success 'read with --get: xdg file exists and ~/.gitconfig doesn'\''t' '
+ rm .gitconfig &&
+ echo "[user]" >.config/git/config &&
+ echo " name = read_config" >>.config/git/config &&
+ echo read_config >expected &&
+ git config --get user.name >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '"$XDG_CONFIG_HOME overrides $HOME/.config/git' '
+ mkdir -p "$HOME"/xdg/git &&
+ echo "[user]name = in_xdg" >"$HOME"/xdg/git/config &&
+ echo in_xdg >expected &&
+ XDG_CONFIG_HOME="$HOME"/xdg git config --get-all user.name >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'read with --get: xdg file exists and ~/.gitconfig exists' '
+ >.gitconfig &&
+ echo "[user]" >.gitconfig &&
+ echo " name = read_gitconfig" >>.gitconfig &&
+ echo read_gitconfig >expected &&
+ git config --get user.name >actual &&
+ test_cmp expected actual
+'
+
+
+test_expect_success 'read with --list: xdg file exists and ~/.gitconfig doesn'\''t' '
+ rm .gitconfig &&
+ echo user.name=read_config >expected &&
+ git config --global --list >actual &&
+ test_cmp expected actual
+'
+
+
+test_expect_success 'read with --list: xdg file exists and ~/.gitconfig exists' '
+ >.gitconfig &&
+ echo "[user]" >.gitconfig &&
+ echo " name = read_gitconfig" >>.gitconfig &&
+ echo user.name=read_gitconfig >expected &&
+ git config --global --list >actual &&
+ test_cmp expected actual
+'
+
+
+test_expect_success 'Setup' '
+ git init git &&
+ cd git &&
+ echo foo >to_be_excluded
+'
+
+
+test_expect_success 'Exclusion of a file in the XDG ignore file' '
+ mkdir -p "$HOME"/.config/git/ &&
+ echo to_be_excluded >"$HOME"/.config/git/ignore &&
+ test_must_fail git add to_be_excluded
+'
+
+test_expect_success '$XDG_CONFIG_HOME overrides $HOME/.config/git/ignore' '
+ mkdir -p "$HOME"/xdg/git &&
+ echo content >excluded_by_xdg_only &&
+ echo excluded_by_xdg_only >"$HOME"/xdg/git/ignore &&
+ test_when_finished "git read-tree --empty" &&
+ (XDG_CONFIG_HOME="$HOME/xdg" &&
+ export XDG_CONFIG_HOME &&
+ git add to_be_excluded &&
+ test_must_fail git add excluded_by_xdg_only
+ )
+'
+
+test_expect_success 'Exclusion in both XDG and local ignore files' '
+ echo to_be_excluded >.gitignore &&
+ test_must_fail git add to_be_excluded
+'
+
+
+test_expect_success 'Exclusion in a non-XDG global ignore file' '
+ rm .gitignore &&
+ echo >"$HOME"/.config/git/ignore &&
+ echo to_be_excluded >"$HOME"/my_gitignore &&
+ git config core.excludesfile "$HOME"/my_gitignore &&
+ test_must_fail git add to_be_excluded
+'
+
+test_expect_success 'Checking XDG ignore file when HOME is unset' '
+ >expected &&
+ (sane_unset HOME &&
+ git config --unset core.excludesfile &&
+ git ls-files --exclude-standard --ignored >actual) &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Checking attributes in the XDG attributes file' '
+ echo foo >f &&
+ git check-attr -a f >actual &&
+ test_line_count -eq 0 actual &&
+ echo "f attr_f" >"$HOME"/.config/git/attributes &&
+ echo "f: attr_f: set" >expected &&
+ git check-attr -a f >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Checking XDG attributes when HOME is unset' '
+ >expected &&
+ (sane_unset HOME &&
+ git check-attr -a f >actual) &&
+ test_cmp expected actual
+'
+
+test_expect_success '$XDG_CONFIG_HOME overrides $HOME/.config/git/attributes' '
+ mkdir -p "$HOME"/xdg/git &&
+ echo "f attr_f=xdg" >"$HOME"/xdg/git/attributes &&
+ echo "f: attr_f: xdg" >expected &&
+ XDG_CONFIG_HOME="$HOME/xdg" git check-attr -a f >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Checking attributes in both XDG and local attributes files' '
+ echo "f -attr_f" >.gitattributes &&
+ echo "f: attr_f: unset" >expected &&
+ git check-attr -a f >actual &&
+ test_cmp expected actual
+'
+
+
+test_expect_success 'Checking attributes in a non-XDG global attributes file' '
+ test_might_fail rm .gitattributes &&
+ echo "f attr_f=test" >"$HOME"/my_gitattributes &&
+ git config core.attributesfile "$HOME"/my_gitattributes &&
+ echo "f: attr_f: test" >expected &&
+ git check-attr -a f >actual &&
+ test_cmp expected actual
+'
+
+
+test_expect_success 'write: xdg file exists and ~/.gitconfig doesn'\''t' '
+ mkdir -p "$HOME"/.config/git &&
+ >"$HOME"/.config/git/config &&
+ test_might_fail rm "$HOME"/.gitconfig &&
+ git config --global user.name "write_config" &&
+ echo "[user]" >expected &&
+ echo " name = write_config" >>expected &&
+ test_cmp expected "$HOME"/.config/git/config
+'
+
+
+test_expect_success 'write: xdg file exists and ~/.gitconfig exists' '
+ >"$HOME"/.gitconfig &&
+ git config --global user.name "write_gitconfig" &&
+ echo "[user]" >expected &&
+ echo " name = write_gitconfig" >>expected &&
+ test_cmp expected "$HOME"/.gitconfig
+'
+
+
+test_expect_success 'write: ~/.config/git/ exists and config file doesn'\''t' '
+ test_might_fail rm "$HOME"/.gitconfig &&
+ test_might_fail rm "$HOME"/.config/git/config &&
+ git config --global user.name "write_gitconfig" &&
+ echo "[user]" >expected &&
+ echo " name = write_gitconfig" >>expected &&
+ test_cmp expected "$HOME"/.gitconfig
+'
+
+
+test_done
diff --git a/t/t1307-config-blob.sh b/t/t1307-config-blob.sh
new file mode 100755
index 0000000..3c6791e
--- /dev/null
+++ b/t/t1307-config-blob.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+test_description='support for reading config from a blob'
+. ./test-lib.sh
+
+test_expect_success 'create config blob' '
+ cat >config <<-\EOF &&
+ [some]
+ value = 1
+ EOF
+ git add config &&
+ git commit -m foo
+'
+
+test_expect_success 'list config blob contents' '
+ echo some.value=1 >expect &&
+ git config --blob=HEAD:config --list >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fetch value from blob' '
+ echo true >expect &&
+ git config --blob=HEAD:config --bool some.value >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'reading non-existing value from blob is an error' '
+ test_must_fail git config --blob=HEAD:config non.existing
+'
+
+test_expect_success 'reading from blob and file is an error' '
+ test_must_fail git config --blob=HEAD:config --system --list
+'
+
+test_expect_success 'reading from missing ref is an error' '
+ test_must_fail git config --blob=HEAD:doesnotexist --list
+'
+
+test_expect_success 'reading from non-blob is an error' '
+ test_must_fail git config --blob=HEAD --list
+'
+
+test_expect_success 'setting a value in a blob is an error' '
+ test_must_fail git config --blob=HEAD:config some.value foo
+'
+
+test_expect_success 'deleting a value in a blob is an error' '
+ test_must_fail git config --blob=HEAD:config --unset some.value
+'
+
+test_expect_success 'editing a blob is an error' '
+ test_must_fail git config --blob=HEAD:config --edit
+'
+
+test_expect_success 'parse errors in blobs are properly attributed' '
+ cat >config <<-\EOF &&
+ [some]
+ value = "
+ EOF
+ git add config &&
+ git commit -m broken &&
+
+ test_must_fail git config --blob=HEAD:config some.value 2>err &&
+
+ # just grep for our token as the exact error message is likely to
+ # change or be internationalized
+ grep "HEAD:config" err
+'
+
+test_expect_success 'can parse blob ending with CR' '
+ printf "[some]key = value\\r" >config &&
+ git add config &&
+ git commit -m CR &&
+ echo value >expect &&
+ git config --blob=HEAD:config some.key >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh
new file mode 100755
index 0000000..005d66d
--- /dev/null
+++ b/t/t1308-config-set.sh
@@ -0,0 +1,232 @@
+#!/bin/sh
+
+test_description='Test git config-set API in different settings'
+
+. ./test-lib.sh
+
+# 'check_config get_* section.key value' verifies that the entry for
+# section.key is 'value'
+check_config () {
+ if test "$1" = expect_code
+ then
+ expect_code="$2" && shift && shift
+ else
+ expect_code=0
+ fi &&
+ op=$1 key=$2 && shift && shift &&
+ if test $# != 0
+ then
+ printf "%s\n" "$@"
+ fi >expect &&
+ test_expect_code $expect_code test-config "$op" "$key" >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'setup default config' '
+ cat >.git/config <<-\EOF
+ [case]
+ penguin = very blue
+ Movie = BadPhysics
+ UPPERCASE = true
+ MixedCase = true
+ my =
+ foo
+ baz = sam
+ [Cores]
+ WhatEver = Second
+ baz = bar
+ [cores]
+ baz = bat
+ [CORES]
+ baz = ball
+ [my "Foo bAr"]
+ hi = mixed-case
+ [my "FOO BAR"]
+ hi = upper-case
+ [my "foo bar"]
+ hi = lower-case
+ [case]
+ baz = bat
+ baz = hask
+ [lamb]
+ chop = 65
+ head = none
+ [goat]
+ legs = 4
+ head = true
+ skin = false
+ nose = 1
+ horns
+ EOF
+'
+
+test_expect_success 'get value for a simple key' '
+ check_config get_value case.penguin "very blue"
+'
+
+test_expect_success 'get value for a key with value as an empty string' '
+ check_config get_value case.my ""
+'
+
+test_expect_success 'get value for a key with value as NULL' '
+ check_config get_value case.foo "(NULL)"
+'
+
+test_expect_success 'upper case key' '
+ check_config get_value case.UPPERCASE "true" &&
+ check_config get_value case.uppercase "true"
+'
+
+test_expect_success 'mixed case key' '
+ check_config get_value case.MixedCase "true" &&
+ check_config get_value case.MIXEDCASE "true" &&
+ check_config get_value case.mixedcase "true"
+'
+
+test_expect_success 'key and value with mixed case' '
+ check_config get_value case.Movie "BadPhysics"
+'
+
+test_expect_success 'key with case sensitive subsection' '
+ check_config get_value "my.Foo bAr.hi" "mixed-case" &&
+ check_config get_value "my.FOO BAR.hi" "upper-case" &&
+ check_config get_value "my.foo bar.hi" "lower-case"
+'
+
+test_expect_success 'key with case insensitive section header' '
+ check_config get_value cores.baz "ball" &&
+ check_config get_value Cores.baz "ball" &&
+ check_config get_value CORES.baz "ball" &&
+ check_config get_value coreS.baz "ball"
+'
+
+test_expect_success 'key with case insensitive section header & variable' '
+ check_config get_value CORES.BAZ "ball" &&
+ check_config get_value cores.baz "ball" &&
+ check_config get_value cores.BaZ "ball" &&
+ check_config get_value cOreS.bAz "ball"
+'
+
+test_expect_success 'find value with misspelled key' '
+ check_config expect_code 1 get_value "my.fOo Bar.hi" "Value not found for \"my.fOo Bar.hi\""
+'
+
+test_expect_success 'find value with the highest priority' '
+ check_config get_value case.baz "hask"
+'
+
+test_expect_success 'find integer value for a key' '
+ check_config get_int lamb.chop 65
+'
+
+test_expect_success 'find string value for a key' '
+ check_config get_string case.baz hask &&
+ check_config expect_code 1 get_string case.ba "Value not found for \"case.ba\""
+'
+
+test_expect_success 'check line error when NULL string is queried' '
+ test_expect_code 128 test-config get_string case.foo 2>result &&
+ test_i18ngrep "fatal: .*case\.foo.*\.git/config.*line 7" result
+'
+
+test_expect_success 'find integer if value is non parse-able' '
+ check_config expect_code 128 get_int lamb.head
+'
+
+test_expect_success 'find bool value for the entered key' '
+ check_config get_bool goat.head 1 &&
+ check_config get_bool goat.skin 0 &&
+ check_config get_bool goat.nose 1 &&
+ check_config get_bool goat.horns 1 &&
+ check_config get_bool goat.legs 1
+'
+
+test_expect_success 'find multiple values' '
+ check_config get_value_multi case.baz sam bat hask
+'
+
+test_expect_success 'find value from a configset' '
+ cat >config2 <<-\EOF &&
+ [case]
+ baz = lama
+ [my]
+ new = silk
+ [case]
+ baz = ball
+ EOF
+ echo silk >expect &&
+ test-config configset_get_value my.new config2 .git/config >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'find value with highest priority from a configset' '
+ echo hask >expect &&
+ test-config configset_get_value case.baz config2 .git/config >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'find value_list for a key from a configset' '
+ cat >except <<-\EOF &&
+ sam
+ bat
+ hask
+ lama
+ ball
+ EOF
+ test-config configset_get_value case.baz config2 .git/config >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'proper error on non-existent files' '
+ echo "Error (-1) reading configuration file non-existent-file." >expect &&
+ test_expect_code 2 test-config configset_get_value foo.bar non-existent-file 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success POSIXPERM,SANITY 'proper error on non-accessible files' '
+ chmod -r .git/config &&
+ test_when_finished "chmod +r .git/config" &&
+ echo "Error (-1) reading configuration file .git/config." >expect &&
+ test_expect_code 2 test-config configset_get_value foo.bar .git/config 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'proper error on error in default config files' '
+ cp .git/config .git/config.old &&
+ test_when_finished "mv .git/config.old .git/config" &&
+ echo "[" >>.git/config &&
+ echo "fatal: bad config line 34 in file .git/config" >expect &&
+ test_expect_code 128 test-config get_value foo.bar 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'proper error on error in custom config files' '
+ echo "[" >>syntax-error &&
+ echo "fatal: bad config line 1 in file syntax-error" >expect &&
+ test_expect_code 128 test-config configset_get_value foo.bar syntax-error 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check line errors for malformed values' '
+ mv .git/config .git/config.old &&
+ test_when_finished "mv .git/config.old .git/config" &&
+ cat >.git/config <<-\EOF &&
+ [alias]
+ br
+ EOF
+ test_expect_code 128 git br 2>result &&
+ test_i18ngrep "fatal: .*alias\.br.*\.git/config.*line 2" result
+'
+
+test_expect_success 'error on modifying repo config without repo' '
+ mkdir no-repo &&
+ (
+ GIT_CEILING_DIRECTORIES=$(pwd) &&
+ export GIT_CEILING_DIRECTORIES &&
+ cd no-repo &&
+ test_must_fail git config a.b c 2>err &&
+ grep "not in a git directory" err
+ )
+'
+
+test_done
diff --git a/t/t1350-config-hooks-path.sh b/t/t1350-config-hooks-path.sh
new file mode 100755
index 0000000..5e3fb3a
--- /dev/null
+++ b/t/t1350-config-hooks-path.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+test_description='Test the core.hooksPath configuration variable'
+
+. ./test-lib.sh
+
+test_expect_success 'set up a pre-commit hook in core.hooksPath' '
+ mkdir -p .git/custom-hooks .git/hooks &&
+ write_script .git/custom-hooks/pre-commit <<-\EOF &&
+ echo CUSTOM >>actual
+ EOF
+ write_script .git/hooks/pre-commit <<-\EOF
+ echo NORMAL >>actual
+ EOF
+'
+
+test_expect_success 'Check that various forms of specifying core.hooksPath work' '
+ test_commit no_custom_hook &&
+ git config core.hooksPath .git/custom-hooks &&
+ test_commit have_custom_hook &&
+ git config core.hooksPath .git/custom-hooks/ &&
+ test_commit have_custom_hook_trailing_slash &&
+ git config core.hooksPath "$PWD/.git/custom-hooks" &&
+ test_commit have_custom_hook_abs_path &&
+ git config core.hooksPath "$PWD/.git/custom-hooks/" &&
+ test_commit have_custom_hook_abs_path_trailing_slash &&
+ cat >expect <<-\EOF &&
+ NORMAL
+ CUSTOM
+ CUSTOM
+ CUSTOM
+ CUSTOM
+ EOF
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 54ba3df..af1b20d 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -6,7 +6,7 @@
test_description='Test git update-ref and basic ref logging'
. ./test-lib.sh
-Z=0000000000000000000000000000000000000000
+Z=$_z40
test_expect_success setup '
@@ -23,6 +23,7 @@ test_expect_success setup '
m=refs/heads/master
n_dir=refs/heads/gu
n=$n_dir/fixes
+outside=foo
test_expect_success \
"create $m" \
@@ -52,9 +53,8 @@ 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'
+ "touch .git/$n_dir &&
+ test_must_fail git update-ref $n $A >out 2>err"
rm -f .git/$n_dir out err
test_expect_success \
@@ -75,6 +75,42 @@ test_expect_success "delete $m (by HEAD)" '
'
rm -f .git/$m
+test_expect_success 'update-ref does not create reflogs by default' '
+ test_when_finished "git update-ref -d $outside" &&
+ git update-ref $outside $A &&
+ git rev-parse $A >expect &&
+ git rev-parse $outside >actual &&
+ test_cmp expect actual &&
+ test_must_fail git reflog exists $outside
+'
+
+test_expect_success 'update-ref creates reflogs with --create-reflog' '
+ test_when_finished "git update-ref -d $outside" &&
+ git update-ref --create-reflog $outside $A &&
+ git rev-parse $A >expect &&
+ git rev-parse $outside >actual &&
+ test_cmp expect actual &&
+ git reflog exists $outside
+'
+
+test_expect_success \
+ "create $m (by HEAD)" \
+ "git update-ref HEAD $A &&
+ test $A"' = $(cat .git/'"$m"')'
+test_expect_success \
+ "pack refs" \
+ "git pack-refs --all"
+test_expect_success \
+ "move $m (by HEAD)" \
+ "git update-ref HEAD $B $A &&
+ test $B"' = $(cat .git/'"$m"')'
+test_expect_success "delete $m (by HEAD) should remove both packed and loose $m" '
+ git update-ref -d HEAD $B &&
+ ! grep "$m" .git/packed-refs &&
+ ! test -f .git/$m
+'
+rm -f .git/$m
+
cp -f .git/HEAD .git/HEAD.orig
test_expect_success "delete symref without dereference" '
git update-ref --no-deref -d HEAD &&
@@ -93,6 +129,32 @@ test_expect_success "delete symref without dereference when the referred ref is
cp -f .git/HEAD.orig .git/HEAD
git update-ref -d $m
+test_expect_success 'update-ref -d is not confused by self-reference' '
+ git symbolic-ref refs/heads/self refs/heads/self &&
+ test_when_finished "rm -f .git/refs/heads/self" &&
+ test_path_is_file .git/refs/heads/self &&
+ test_must_fail git update-ref -d refs/heads/self &&
+ test_path_is_file .git/refs/heads/self
+'
+
+test_expect_success 'update-ref --no-deref -d can delete self-reference' '
+ git symbolic-ref refs/heads/self refs/heads/self &&
+ test_when_finished "rm -f .git/refs/heads/self" &&
+ test_path_is_file .git/refs/heads/self &&
+ git update-ref --no-deref -d refs/heads/self &&
+ test_path_is_missing .git/refs/heads/self
+'
+
+test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '
+ >.git/refs/heads/bad &&
+ test_when_finished "rm -f .git/refs/heads/bad" &&
+ git symbolic-ref refs/heads/ref-to-bad refs/heads/bad &&
+ test_when_finished "rm -f .git/refs/heads/ref-to-bad" &&
+ test_path_is_file .git/refs/heads/ref-to-bad &&
+ git update-ref --no-deref -d refs/heads/ref-to-bad &&
+ test_path_is_missing .git/refs/heads/ref-to-bad
+'
+
test_expect_success '(not) create HEAD with old sha1' "
test_must_fail git update-ref HEAD $A $B
"
@@ -112,12 +174,11 @@ test_expect_success "(not) changed .git/$m" "
'
rm -f .git/$m
-: a repository with working tree always has reflog these days...
-: >.git/logs/refs/heads/master
+rm -f .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" &&
+ git update-ref --create-reflog HEAD '"$A"' -m "Initial Creation" &&
test '"$A"' = $(cat .git/'"$m"')'
test_expect_success \
"update $m (logged by touch)" \
@@ -185,58 +246,58 @@ 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
+ '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
+ '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
+ '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
+ '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
+ '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
+ '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 "warning: Log for ref '"$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
+ '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
+ '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
+ '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)"'
+ test "warning: Log for ref '"$m unexpectedly ended on $ld"'." = "$(cat e)"'
rm -f .git/$m .git/logs/$m expect
@@ -247,7 +308,7 @@ test_expect_success \
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)
+ 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 &&
@@ -285,4 +346,807 @@ test_expect_success \
'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER)' \
'test OTHER = $(git cat-file blob "master@{2005-05-26 23:42}:F")'
+a=refs/heads/a
+b=refs/heads/b
+c=refs/heads/c
+E='""'
+F='%s\0'
+pws='path with space'
+
+test_expect_success 'stdin test setup' '
+ echo "$pws" >"$pws" &&
+ git add -- "$pws" &&
+ git commit -m "$pws"
+'
+
+test_expect_success '-z fails without --stdin' '
+ test_must_fail git update-ref -z $m $m $m 2>err &&
+ grep "usage: git update-ref" err
+'
+
+test_expect_success 'stdin works with no input' '
+ >stdin &&
+ git update-ref --stdin <stdin &&
+ git rev-parse --verify -q $m
+'
+
+test_expect_success 'stdin fails on empty line' '
+ echo "" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: empty command in input" err
+'
+
+test_expect_success 'stdin fails on only whitespace' '
+ echo " " >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: whitespace before command: " err
+'
+
+test_expect_success 'stdin fails on leading whitespace' '
+ echo " create $a $m" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: whitespace before command: create $a $m" err
+'
+
+test_expect_success 'stdin fails on unknown command' '
+ echo "unknown $a" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: unknown command: unknown $a" err
+'
+
+test_expect_success 'stdin fails on unbalanced quotes' '
+ echo "create $a \"master" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: badly quoted argument: \\\"master" err
+'
+
+test_expect_success 'stdin fails on invalid escape' '
+ echo "create $a \"ma\zter\"" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: badly quoted argument: \\\"ma\\\\zter\\\"" err
+'
+
+test_expect_success 'stdin fails on junk after quoted argument' '
+ echo "create \"$a\"master" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: unexpected character after quoted argument: \\\"$a\\\"master" err
+'
+
+test_expect_success 'stdin fails create with no ref' '
+ echo "create " >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: create: missing <ref>" err
+'
+
+test_expect_success 'stdin fails create with no new value' '
+ echo "create $a" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: create $a: missing <newvalue>" err
+'
+
+test_expect_success 'stdin fails create with too many arguments' '
+ echo "create $a $m $m" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: create $a: extra input: $m" err
+'
+
+test_expect_success 'stdin fails update with no ref' '
+ echo "update " >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: update: missing <ref>" err
+'
+
+test_expect_success 'stdin fails update with no new value' '
+ echo "update $a" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: update $a: missing <newvalue>" err
+'
+
+test_expect_success 'stdin fails update with too many arguments' '
+ echo "update $a $m $m $m" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: update $a: extra input: $m" err
+'
+
+test_expect_success 'stdin fails delete with no ref' '
+ echo "delete " >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: delete: missing <ref>" err
+'
+
+test_expect_success 'stdin fails delete with too many arguments' '
+ echo "delete $a $m $m" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: delete $a: extra input: $m" err
+'
+
+test_expect_success 'stdin fails verify with too many arguments' '
+ echo "verify $a $m $m" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: verify $a: extra input: $m" err
+'
+
+test_expect_success 'stdin fails option with unknown name' '
+ echo "option unknown" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: option unknown: unknown" err
+'
+
+test_expect_success 'stdin fails with duplicate refs' '
+ cat >stdin <<-EOF &&
+ create $a $m
+ create $b $m
+ create $a $m
+ EOF
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: Multiple updates for ref '"'"'$a'"'"' not allowed." err
+'
+
+test_expect_success 'stdin create ref works' '
+ echo "create $a $m" >stdin &&
+ git update-ref --stdin <stdin &&
+ git rev-parse $m >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin does not create reflogs by default' '
+ test_when_finished "git update-ref -d $outside" &&
+ echo "create $outside $m" >stdin &&
+ git update-ref --stdin <stdin &&
+ git rev-parse $m >expect &&
+ git rev-parse $outside >actual &&
+ test_cmp expect actual &&
+ test_must_fail git reflog exists $outside
+'
+
+test_expect_success 'stdin creates reflogs with --create-reflog' '
+ echo "create $outside $m" >stdin &&
+ git update-ref --create-reflog --stdin <stdin &&
+ git rev-parse $m >expect &&
+ git rev-parse $outside >actual &&
+ test_cmp expect actual &&
+ git reflog exists $outside
+'
+
+test_expect_success 'stdin succeeds with quoted argument' '
+ git update-ref -d $a &&
+ echo "create $a \"$m\"" >stdin &&
+ git update-ref --stdin <stdin &&
+ git rev-parse $m >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin succeeds with escaped character' '
+ git update-ref -d $a &&
+ echo "create $a \"ma\\163ter\"" >stdin &&
+ git update-ref --stdin <stdin &&
+ git rev-parse $m >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin update ref creates with zero old value' '
+ echo "update $b $m $Z" >stdin &&
+ git update-ref --stdin <stdin &&
+ git rev-parse $m >expect &&
+ git rev-parse $b >actual &&
+ test_cmp expect actual &&
+ git update-ref -d $b
+'
+
+test_expect_success 'stdin update ref creates with empty old value' '
+ echo "update $b $m $E" >stdin &&
+ git update-ref --stdin <stdin &&
+ git rev-parse $m >expect &&
+ git rev-parse $b >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin create ref works with path with space to blob' '
+ echo "create refs/blobs/pws \"$m:$pws\"" >stdin &&
+ git update-ref --stdin <stdin &&
+ git rev-parse "$m:$pws" >expect &&
+ git rev-parse refs/blobs/pws >actual &&
+ test_cmp expect actual &&
+ git update-ref -d refs/blobs/pws
+'
+
+test_expect_success 'stdin update ref fails with wrong old value' '
+ echo "update $c $m $m~1" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: cannot lock ref '"'"'$c'"'"'" err &&
+ test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin update ref fails with bad old value' '
+ echo "update $c $m does-not-exist" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: update $c: invalid <oldvalue>: does-not-exist" err &&
+ test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin create ref fails with bad new value' '
+ echo "create $c does-not-exist" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: create $c: invalid <newvalue>: does-not-exist" err &&
+ test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin create ref fails with zero new value' '
+ echo "create $c " >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: create $c: zero <newvalue>" err &&
+ test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin update ref works with right old value' '
+ echo "update $b $m~1 $m" >stdin &&
+ git update-ref --stdin <stdin &&
+ git rev-parse $m~1 >expect &&
+ git rev-parse $b >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin delete ref fails with wrong old value' '
+ echo "delete $a $m~1" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: cannot lock ref '"'"'$a'"'"'" err &&
+ git rev-parse $m >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin delete ref fails with zero old value' '
+ echo "delete $a " >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: delete $a: zero <oldvalue>" err &&
+ git rev-parse $m >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin update symref works option no-deref' '
+ git symbolic-ref TESTSYMREF $b &&
+ cat >stdin <<-EOF &&
+ option no-deref
+ update TESTSYMREF $a $b
+ EOF
+ git update-ref --stdin <stdin &&
+ git rev-parse TESTSYMREF >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual &&
+ git rev-parse $m~1 >expect &&
+ git rev-parse $b >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin delete symref works option no-deref' '
+ git symbolic-ref TESTSYMREF $b &&
+ cat >stdin <<-EOF &&
+ option no-deref
+ delete TESTSYMREF $b
+ EOF
+ git update-ref --stdin <stdin &&
+ test_must_fail git rev-parse --verify -q TESTSYMREF &&
+ git rev-parse $m~1 >expect &&
+ git rev-parse $b >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin delete ref works with right old value' '
+ echo "delete $b $m~1" >stdin &&
+ git update-ref --stdin <stdin &&
+ test_must_fail git rev-parse --verify -q $b
+'
+
+test_expect_success 'stdin update/create/verify combination works' '
+ cat >stdin <<-EOF &&
+ update $a $m
+ create $b $m
+ verify $c
+ EOF
+ git update-ref --stdin <stdin &&
+ git rev-parse $m >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual &&
+ git rev-parse $b >actual &&
+ test_cmp expect actual &&
+ test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin verify succeeds for correct value' '
+ git rev-parse $m >expect &&
+ echo "verify $m $m" >stdin &&
+ git update-ref --stdin <stdin &&
+ git rev-parse $m >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin verify succeeds for missing reference' '
+ echo "verify refs/heads/missing $Z" >stdin &&
+ git update-ref --stdin <stdin &&
+ test_must_fail git rev-parse --verify -q refs/heads/missing
+'
+
+test_expect_success 'stdin verify treats no value as missing' '
+ echo "verify refs/heads/missing" >stdin &&
+ git update-ref --stdin <stdin &&
+ test_must_fail git rev-parse --verify -q refs/heads/missing
+'
+
+test_expect_success 'stdin verify fails for wrong value' '
+ git rev-parse $m >expect &&
+ echo "verify $m $m~1" >stdin &&
+ test_must_fail git update-ref --stdin <stdin &&
+ git rev-parse $m >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin verify fails for mistaken null value' '
+ git rev-parse $m >expect &&
+ echo "verify $m $Z" >stdin &&
+ test_must_fail git update-ref --stdin <stdin &&
+ git rev-parse $m >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin verify fails for mistaken empty value' '
+ M=$(git rev-parse $m) &&
+ test_when_finished "git update-ref $m $M" &&
+ git rev-parse $m >expect &&
+ echo "verify $m" >stdin &&
+ test_must_fail git update-ref --stdin <stdin &&
+ git rev-parse $m >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin update refs works with identity updates' '
+ cat >stdin <<-EOF &&
+ update $a $m $m
+ update $b $m $m
+ update $c $Z $E
+ EOF
+ git update-ref --stdin <stdin &&
+ git rev-parse $m >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual &&
+ git rev-parse $b >actual &&
+ test_cmp expect actual &&
+ test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin update refs fails with wrong old value' '
+ git update-ref $c $m &&
+ cat >stdin <<-EOF &&
+ update $a $m $m
+ update $b $m $m
+ update $c ''
+ EOF
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: cannot lock ref '"'"'$c'"'"'" err &&
+ git rev-parse $m >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual &&
+ git rev-parse $b >actual &&
+ test_cmp expect actual &&
+ git rev-parse $c >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin delete refs works with packed and loose refs' '
+ git pack-refs --all &&
+ git update-ref $c $m~1 &&
+ cat >stdin <<-EOF &&
+ delete $a $m
+ update $b $Z $m
+ update $c $E $m~1
+ EOF
+ git update-ref --stdin <stdin &&
+ test_must_fail git rev-parse --verify -q $a &&
+ test_must_fail git rev-parse --verify -q $b &&
+ test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin -z works on empty input' '
+ >stdin &&
+ git update-ref -z --stdin <stdin &&
+ git rev-parse --verify -q $m
+'
+
+test_expect_success 'stdin -z fails on empty line' '
+ echo "" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: whitespace before command: " err
+'
+
+test_expect_success 'stdin -z fails on empty command' '
+ printf $F "" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: empty command in input" err
+'
+
+test_expect_success 'stdin -z fails on only whitespace' '
+ printf $F " " >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: whitespace before command: " err
+'
+
+test_expect_success 'stdin -z fails on leading whitespace' '
+ printf $F " create $a" "$m" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: whitespace before command: create $a" err
+'
+
+test_expect_success 'stdin -z fails on unknown command' '
+ printf $F "unknown $a" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: unknown command: unknown $a" err
+'
+
+test_expect_success 'stdin -z fails create with no ref' '
+ printf $F "create " >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: create: missing <ref>" err
+'
+
+test_expect_success 'stdin -z fails create with no new value' '
+ printf $F "create $a" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: create $a: unexpected end of input when reading <newvalue>" err
+'
+
+test_expect_success 'stdin -z fails create with too many arguments' '
+ printf $F "create $a" "$m" "$m" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: unknown command: $m" err
+'
+
+test_expect_success 'stdin -z fails update with no ref' '
+ printf $F "update " >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: update: missing <ref>" err
+'
+
+test_expect_success 'stdin -z fails update with too few args' '
+ printf $F "update $a" "$m" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
+'
+
+test_expect_success 'stdin -z emits warning with empty new value' '
+ git update-ref $a $m &&
+ printf $F "update $a" "" "" >stdin &&
+ git update-ref -z --stdin <stdin 2>err &&
+ grep "warning: update $a: missing <newvalue>, treating as zero" err &&
+ test_must_fail git rev-parse --verify -q $a
+'
+
+test_expect_success 'stdin -z fails update with no new value' '
+ printf $F "update $a" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: update $a: unexpected end of input when reading <newvalue>" err
+'
+
+test_expect_success 'stdin -z fails update with no old value' '
+ printf $F "update $a" "$m" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
+'
+
+test_expect_success 'stdin -z fails update with too many arguments' '
+ printf $F "update $a" "$m" "$m" "$m" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: unknown command: $m" err
+'
+
+test_expect_success 'stdin -z fails delete with no ref' '
+ printf $F "delete " >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: delete: missing <ref>" err
+'
+
+test_expect_success 'stdin -z fails delete with no old value' '
+ printf $F "delete $a" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: delete $a: unexpected end of input when reading <oldvalue>" err
+'
+
+test_expect_success 'stdin -z fails delete with too many arguments' '
+ printf $F "delete $a" "$m" "$m" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: unknown command: $m" err
+'
+
+test_expect_success 'stdin -z fails verify with too many arguments' '
+ printf $F "verify $a" "$m" "$m" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: unknown command: $m" err
+'
+
+test_expect_success 'stdin -z fails verify with no old value' '
+ printf $F "verify $a" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: verify $a: unexpected end of input when reading <oldvalue>" err
+'
+
+test_expect_success 'stdin -z fails option with unknown name' '
+ printf $F "option unknown" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: option unknown: unknown" err
+'
+
+test_expect_success 'stdin -z fails with duplicate refs' '
+ printf $F "create $a" "$m" "create $b" "$m" "create $a" "$m" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: Multiple updates for ref '"'"'$a'"'"' not allowed." err
+'
+
+test_expect_success 'stdin -z create ref works' '
+ printf $F "create $a" "$m" >stdin &&
+ git update-ref -z --stdin <stdin &&
+ git rev-parse $m >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin -z update ref creates with zero old value' '
+ printf $F "update $b" "$m" "$Z" >stdin &&
+ git update-ref -z --stdin <stdin &&
+ git rev-parse $m >expect &&
+ git rev-parse $b >actual &&
+ test_cmp expect actual &&
+ git update-ref -d $b
+'
+
+test_expect_success 'stdin -z update ref creates with empty old value' '
+ printf $F "update $b" "$m" "" >stdin &&
+ git update-ref -z --stdin <stdin &&
+ git rev-parse $m >expect &&
+ git rev-parse $b >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin -z create ref works with path with space to blob' '
+ printf $F "create refs/blobs/pws" "$m:$pws" >stdin &&
+ git update-ref -z --stdin <stdin &&
+ git rev-parse "$m:$pws" >expect &&
+ git rev-parse refs/blobs/pws >actual &&
+ test_cmp expect actual &&
+ git update-ref -d refs/blobs/pws
+'
+
+test_expect_success 'stdin -z update ref fails with wrong old value' '
+ printf $F "update $c" "$m" "$m~1" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: cannot lock ref '"'"'$c'"'"'" err &&
+ test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin -z update ref fails with bad old value' '
+ printf $F "update $c" "$m" "does-not-exist" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: update $c: invalid <oldvalue>: does-not-exist" err &&
+ test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin -z create ref fails when ref exists' '
+ git update-ref $c $m &&
+ git rev-parse "$c" >expect &&
+ printf $F "create $c" "$m~1" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: cannot lock ref '"'"'$c'"'"'" err &&
+ git rev-parse "$c" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin -z create ref fails with bad new value' '
+ git update-ref -d "$c" &&
+ printf $F "create $c" "does-not-exist" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: create $c: invalid <newvalue>: does-not-exist" err &&
+ test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin -z create ref fails with empty new value' '
+ printf $F "create $c" "" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: create $c: missing <newvalue>" err &&
+ test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin -z update ref works with right old value' '
+ printf $F "update $b" "$m~1" "$m" >stdin &&
+ git update-ref -z --stdin <stdin &&
+ git rev-parse $m~1 >expect &&
+ git rev-parse $b >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin -z delete ref fails with wrong old value' '
+ printf $F "delete $a" "$m~1" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: cannot lock ref '"'"'$a'"'"'" err &&
+ git rev-parse $m >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin -z delete ref fails with zero old value' '
+ printf $F "delete $a" "$Z" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: delete $a: zero <oldvalue>" err &&
+ git rev-parse $m >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin -z update symref works option no-deref' '
+ git symbolic-ref TESTSYMREF $b &&
+ printf $F "option no-deref" "update TESTSYMREF" "$a" "$b" >stdin &&
+ git update-ref -z --stdin <stdin &&
+ git rev-parse TESTSYMREF >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual &&
+ git rev-parse $m~1 >expect &&
+ git rev-parse $b >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin -z delete symref works option no-deref' '
+ git symbolic-ref TESTSYMREF $b &&
+ printf $F "option no-deref" "delete TESTSYMREF" "$b" >stdin &&
+ git update-ref -z --stdin <stdin &&
+ test_must_fail git rev-parse --verify -q TESTSYMREF &&
+ git rev-parse $m~1 >expect &&
+ git rev-parse $b >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin -z delete ref works with right old value' '
+ printf $F "delete $b" "$m~1" >stdin &&
+ git update-ref -z --stdin <stdin &&
+ test_must_fail git rev-parse --verify -q $b
+'
+
+test_expect_success 'stdin -z update/create/verify combination works' '
+ printf $F "update $a" "$m" "" "create $b" "$m" "verify $c" "" >stdin &&
+ git update-ref -z --stdin <stdin &&
+ git rev-parse $m >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual &&
+ git rev-parse $b >actual &&
+ test_cmp expect actual &&
+ test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin -z verify succeeds for correct value' '
+ git rev-parse $m >expect &&
+ printf $F "verify $m" "$m" >stdin &&
+ git update-ref -z --stdin <stdin &&
+ git rev-parse $m >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin -z verify succeeds for missing reference' '
+ printf $F "verify refs/heads/missing" "$Z" >stdin &&
+ git update-ref -z --stdin <stdin &&
+ test_must_fail git rev-parse --verify -q refs/heads/missing
+'
+
+test_expect_success 'stdin -z verify treats no value as missing' '
+ printf $F "verify refs/heads/missing" "" >stdin &&
+ git update-ref -z --stdin <stdin &&
+ test_must_fail git rev-parse --verify -q refs/heads/missing
+'
+
+test_expect_success 'stdin -z verify fails for wrong value' '
+ git rev-parse $m >expect &&
+ printf $F "verify $m" "$m~1" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin &&
+ git rev-parse $m >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin -z verify fails for mistaken null value' '
+ git rev-parse $m >expect &&
+ printf $F "verify $m" "$Z" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin &&
+ git rev-parse $m >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin -z verify fails for mistaken empty value' '
+ M=$(git rev-parse $m) &&
+ test_when_finished "git update-ref $m $M" &&
+ git rev-parse $m >expect &&
+ printf $F "verify $m" "" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin &&
+ git rev-parse $m >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin -z update refs works with identity updates' '
+ printf $F "update $a" "$m" "$m" "update $b" "$m" "$m" "update $c" "$Z" "" >stdin &&
+ git update-ref -z --stdin <stdin &&
+ git rev-parse $m >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual &&
+ git rev-parse $b >actual &&
+ test_cmp expect actual &&
+ test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin -z update refs fails with wrong old value' '
+ git update-ref $c $m &&
+ printf $F "update $a" "$m" "$m" "update $b" "$m" "$m" "update $c" "$m" "$Z" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: cannot lock ref '"'"'$c'"'"'" err &&
+ git rev-parse $m >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual &&
+ git rev-parse $b >actual &&
+ test_cmp expect actual &&
+ git rev-parse $c >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stdin -z delete refs works with packed and loose refs' '
+ git pack-refs --all &&
+ git update-ref $c $m~1 &&
+ printf $F "delete $a" "$m" "update $b" "$Z" "$m" "update $c" "" "$m~1" >stdin &&
+ git update-ref -z --stdin <stdin &&
+ test_must_fail git rev-parse --verify -q $a &&
+ test_must_fail git rev-parse --verify -q $b &&
+ test_must_fail git rev-parse --verify -q $c
+'
+
+run_with_limited_open_files () {
+ (ulimit -n 32 && "$@")
+}
+
+test_lazy_prereq ULIMIT_FILE_DESCRIPTORS 'run_with_limited_open_files true'
+
+test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction creating branches does not burst open file limit' '
+(
+ for i in $(test_seq 33)
+ do
+ echo "create refs/heads/$i HEAD"
+ done >large_input &&
+ run_with_limited_open_files git update-ref --stdin <large_input &&
+ git rev-parse --verify -q refs/heads/33
+)
+'
+
+test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction deleting branches does not burst open file limit' '
+(
+ for i in $(test_seq 33)
+ do
+ echo "delete refs/heads/$i HEAD"
+ done >large_input &&
+ run_with_limited_open_files git update-ref --stdin <large_input &&
+ test_must_fail git rev-parse --verify -q refs/heads/33
+)
+'
+
+test_expect_success 'handle per-worktree refs in refs/bisect' '
+ git commit --allow-empty -m "initial commit" &&
+ git worktree add -b branch worktree &&
+ (
+ cd worktree &&
+ git commit --allow-empty -m "test commit" &&
+ git for-each-ref >for-each-ref.out &&
+ ! grep refs/bisect for-each-ref.out &&
+ git update-ref refs/bisect/something HEAD &&
+ git rev-parse refs/bisect/something >../worktree-head &&
+ git for-each-ref | grep refs/bisect/something
+ ) &&
+ test_path_is_missing .git/refs/bisect &&
+ test_must_fail git rev-parse refs/bisect/something &&
+ git update-ref refs/bisect/something HEAD &&
+ git rev-parse refs/bisect/something >main-head &&
+ ! test_cmp main-head worktree-head
+'
+
test_done
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
index 7fa5f5b..ca3fa40 100755
--- a/t/t1401-symbolic-ref.sh
+++ b/t/t1401-symbolic-ref.sh
@@ -28,9 +28,105 @@ test_expect_success 'symbolic-ref refuses non-ref for HEAD' '
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`
+ 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_expect_success 'symbolic-ref deletes HEAD' '
+ git symbolic-ref -d HEAD &&
+ test_path_is_file .git/refs/heads/foo &&
+ test_path_is_missing .git/HEAD
+'
+reset_to_sane
+
+test_expect_success 'symbolic-ref deletes dangling HEAD' '
+ git symbolic-ref HEAD refs/heads/missing &&
+ git symbolic-ref -d HEAD &&
+ test_path_is_missing .git/refs/heads/missing &&
+ test_path_is_missing .git/HEAD
+'
+reset_to_sane
+
+test_expect_success 'symbolic-ref fails to delete missing FOO' '
+ echo "fatal: Cannot delete FOO, not a symbolic ref" >expect &&
+ test_must_fail git symbolic-ref -d FOO >actual 2>&1 &&
+ test_cmp expect actual
+'
+reset_to_sane
+
+test_expect_success 'symbolic-ref fails to delete real ref' '
+ echo "fatal: Cannot delete refs/heads/foo, not a symbolic ref" >expect &&
+ test_must_fail git symbolic-ref -d refs/heads/foo >actual 2>&1 &&
+ test_path_is_file .git/refs/heads/foo &&
+ test_cmp expect actual
+'
+reset_to_sane
+
+test_expect_success 'create large ref name' '
+ # make 256+ character ref; some systems may not handle that,
+ # so be gentle
+ long=0123456789abcdef &&
+ long=$long/$long/$long/$long &&
+ long=$long/$long/$long/$long &&
+ long_ref=refs/heads/$long &&
+ tree=$(git write-tree) &&
+ commit=$(echo foo | git commit-tree $tree) &&
+ if git update-ref $long_ref $commit; then
+ test_set_prereq LONG_REF
+ else
+ echo >&2 "long refs not supported"
+ fi
+'
+
+test_expect_success LONG_REF 'symbolic-ref can point to large ref name' '
+ git symbolic-ref HEAD $long_ref &&
+ echo $long_ref >expect &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success LONG_REF 'we can parse long symbolic ref' '
+ echo $commit >expect &&
+ git rev-parse --verify HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'symbolic-ref reports failure in exit code' '
+ test_when_finished "rm -f .git/HEAD.lock" &&
+ >.git/HEAD.lock &&
+ test_must_fail git symbolic-ref HEAD refs/heads/whatever
+'
+
+test_expect_success 'symbolic-ref writes reflog entry' '
+ git checkout -b log1 &&
+ test_commit one &&
+ git checkout -b log2 &&
+ test_commit two &&
+ git checkout --orphan orphan &&
+ git symbolic-ref -m create HEAD refs/heads/log1 &&
+ git symbolic-ref -m update HEAD refs/heads/log2 &&
+ cat >expect <<-\EOF &&
+ update
+ create
+ EOF
+ git log --format=%gs -g -2 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'symbolic-ref does not create ref d/f conflicts' '
+ git checkout -b df &&
+ test_commit df &&
+ test_must_fail git symbolic-ref refs/heads/df/conflict refs/heads/df &&
+ git pack-refs --all --prune &&
+ test_must_fail git symbolic-ref refs/heads/df/conflict refs/heads/df
+'
+
+test_expect_success 'symbolic-ref handles existing pointer to invalid name' '
+ head=$(git rev-parse HEAD) &&
+ git symbolic-ref HEAD refs/heads/outer &&
+ git update-ref refs/heads/outer/inner $head &&
+ git symbolic-ref HEAD refs/heads/unrelated
+'
+
test_done
diff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh
index eb45afb..0790edf 100755
--- a/t/t1402-check-ref-format.sh
+++ b/t/t1402-check-ref-format.sh
@@ -5,34 +5,137 @@ 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'"
+ prereq=
+ case $1 in
+ [A-Z!]*)
+ prereq=$1
+ shift
+ esac
+ desc="ref name '$1' is valid${2:+ with options $2}"
+ test_expect_success $prereq "$desc" "
+ git check-ref-format $2 '$1'
+ "
}
invalid_ref() {
- test_expect_success "ref name '$1' is not valid" \
- "test_must_fail git check-ref-format '$1'"
+ prereq=
+ case $1 in
+ [A-Z!]*)
+ prereq=$1
+ shift
+ esac
+ desc="ref name '$1' is invalid${2:+ with options $2}"
+ test_expect_success $prereq "$desc" "
+ test_must_fail git check-ref-format $2 '$1'
+ "
}
-valid_ref 'heads/foo'
-invalid_ref 'foo'
+invalid_ref ''
+invalid_ref !MINGW '/'
+invalid_ref !MINGW '/' --allow-onelevel
+invalid_ref !MINGW '/' --normalize
+invalid_ref !MINGW '/' '--allow-onelevel --normalize'
valid_ref 'foo/bar/baz'
-valid_ref 'refs///heads/foo'
+valid_ref 'foo/bar/baz' --normalize
+invalid_ref 'refs///heads/foo'
+valid_ref 'refs///heads/foo' --normalize
invalid_ref 'heads/foo/'
+invalid_ref !MINGW '/heads/foo'
+valid_ref !MINGW '/heads/foo' --normalize
+invalid_ref '///heads/foo'
+valid_ref '///heads/foo' --normalize
invalid_ref './foo'
+invalid_ref './foo/bar'
+invalid_ref 'foo/./bar'
+invalid_ref 'foo/bar/.'
invalid_ref '.refs/foo'
+invalid_ref 'refs/heads/foo.'
invalid_ref 'heads/foo..bar'
invalid_ref 'heads/foo?bar'
valid_ref 'foo./bar'
invalid_ref 'heads/foo.lock'
+invalid_ref 'heads///foo.lock'
+invalid_ref 'foo.lock/bar'
+invalid_ref 'foo.lock///bar'
valid_ref 'heads/foo@bar'
invalid_ref 'heads/v@{ation'
invalid_ref 'heads/foo\bar'
+invalid_ref "$(printf 'heads/foo\t')"
+invalid_ref "$(printf 'heads/foo\177')"
+valid_ref "$(printf 'heads/fu\303\237')"
+valid_ref 'heads/*foo/bar' --refspec-pattern
+valid_ref 'heads/foo*/bar' --refspec-pattern
+valid_ref 'heads/f*o/bar' --refspec-pattern
+invalid_ref 'heads/f*o*/bar' --refspec-pattern
+invalid_ref 'heads/foo*/bar*' --refspec-pattern
+
+ref='foo'
+invalid_ref "$ref"
+valid_ref "$ref" --allow-onelevel
+invalid_ref "$ref" --refspec-pattern
+valid_ref "$ref" '--refspec-pattern --allow-onelevel'
+invalid_ref "$ref" --normalize
+valid_ref "$ref" '--allow-onelevel --normalize'
+
+ref='foo/bar'
+valid_ref "$ref"
+valid_ref "$ref" --allow-onelevel
+valid_ref "$ref" --refspec-pattern
+valid_ref "$ref" '--refspec-pattern --allow-onelevel'
+valid_ref "$ref" --normalize
+
+ref='foo/*'
+invalid_ref "$ref"
+invalid_ref "$ref" --allow-onelevel
+valid_ref "$ref" --refspec-pattern
+valid_ref "$ref" '--refspec-pattern --allow-onelevel'
+
+ref='*/foo'
+invalid_ref "$ref"
+invalid_ref "$ref" --allow-onelevel
+valid_ref "$ref" --refspec-pattern
+valid_ref "$ref" '--refspec-pattern --allow-onelevel'
+invalid_ref "$ref" --normalize
+valid_ref "$ref" '--refspec-pattern --normalize'
+
+ref='foo/*/bar'
+invalid_ref "$ref"
+invalid_ref "$ref" --allow-onelevel
+valid_ref "$ref" --refspec-pattern
+valid_ref "$ref" '--refspec-pattern --allow-onelevel'
+
+ref='*'
+invalid_ref "$ref"
+invalid_ref "$ref" --allow-onelevel
+invalid_ref "$ref" --refspec-pattern
+valid_ref "$ref" '--refspec-pattern --allow-onelevel'
+
+ref='foo/*/*'
+invalid_ref "$ref" --refspec-pattern
+invalid_ref "$ref" '--refspec-pattern --allow-onelevel'
+
+ref='*/foo/*'
+invalid_ref "$ref" --refspec-pattern
+invalid_ref "$ref" '--refspec-pattern --allow-onelevel'
+
+ref='*/*/foo'
+invalid_ref "$ref" --refspec-pattern
+invalid_ref "$ref" '--refspec-pattern --allow-onelevel'
+
+ref='/foo'
+invalid_ref !MINGW "$ref"
+invalid_ref !MINGW "$ref" --allow-onelevel
+invalid_ref !MINGW "$ref" --refspec-pattern
+invalid_ref !MINGW "$ref" '--refspec-pattern --allow-onelevel'
+invalid_ref !MINGW "$ref" --normalize
+valid_ref !MINGW "$ref" '--allow-onelevel --normalize'
+invalid_ref !MINGW "$ref" '--refspec-pattern --normalize'
+valid_ref !MINGW "$ref" '--refspec-pattern --allow-onelevel --normalize'
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 update-ref refs/remotes/origin/master $sha1 &&
git checkout master &&
git checkout origin/master &&
git checkout master &&
@@ -41,21 +144,59 @@ test_expect_success "check-ref-format --branch @{-1}" '
refname2=$(git check-ref-format --branch @{-2}) &&
test "$refname2" = master'
+test_expect_success 'check-ref-format --branch from subdir' '
+ mkdir subdir &&
+
+ 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=$(
+ cd subdir &&
+ git check-ref-format --branch @{-1}
+ ) &&
+ test "$refname" = "$sha1"
+'
+
valid_ref_normalized() {
- test_expect_success "ref name '$1' simplifies to '$2'" "
- refname=\$(git check-ref-format --print '$1') &&
- test \"\$refname\" = '$2'"
+ prereq=
+ case $1 in
+ [A-Z!]*)
+ prereq=$1
+ shift
+ esac
+ test_expect_success $prereq "ref name '$1' simplifies to '$2'" "
+ refname=\$(git check-ref-format --normalize '$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'"
+ prereq=
+ case $1 in
+ [A-Z!]*)
+ prereq=$1
+ shift
+ esac
+ test_expect_success $prereq "check-ref-format --normalize rejects '$1'" "
+ test_must_fail git check-ref-format --normalize '$1'
+ "
}
valid_ref_normalized 'heads/foo' 'heads/foo'
valid_ref_normalized 'refs///heads/foo' 'refs/heads/foo'
+valid_ref_normalized !MINGW '/heads/foo' 'heads/foo'
+valid_ref_normalized '///heads/foo' 'heads/foo'
invalid_ref_normalized 'foo'
+invalid_ref_normalized !MINGW '/foo'
invalid_ref_normalized 'heads/foo/../bar'
invalid_ref_normalized 'heads/./foo'
invalid_ref_normalized 'heads\foo'
+invalid_ref_normalized 'heads/foo.lock'
+invalid_ref_normalized 'heads///foo.lock'
+invalid_ref_normalized 'foo.lock/bar'
+invalid_ref_normalized 'foo.lock///bar'
test_done
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
new file mode 100755
index 0000000..7e10bcf
--- /dev/null
+++ b/t/t1403-show-ref.sh
@@ -0,0 +1,167 @@
+#!/bin/sh
+
+test_description='show-ref'
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit A &&
+ git tag -f -a -m "annotated A" A &&
+ git checkout -b side &&
+ test_commit B &&
+ git tag -f -a -m "annotated B" B &&
+ git checkout master &&
+ test_commit C &&
+ git branch B A^0
+'
+
+test_expect_success 'show-ref' '
+ echo $(git rev-parse refs/tags/A) refs/tags/A >expect &&
+
+ git show-ref A >actual &&
+ test_cmp expect actual &&
+
+ git show-ref tags/A >actual &&
+ test_cmp expect actual &&
+
+ git show-ref refs/tags/A >actual &&
+ test_cmp expect actual &&
+
+ >expect &&
+
+ test_must_fail git show-ref D >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'show-ref -q' '
+ >expect &&
+
+ git show-ref -q A >actual &&
+ test_cmp expect actual &&
+
+ git show-ref -q tags/A >actual &&
+ test_cmp expect actual &&
+
+ git show-ref -q refs/tags/A >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git show-ref -q D >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'show-ref --verify' '
+ echo $(git rev-parse refs/tags/A) refs/tags/A >expect &&
+
+ git show-ref --verify refs/tags/A >actual &&
+ test_cmp expect actual &&
+
+ >expect &&
+
+ test_must_fail git show-ref --verify A >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git show-ref --verify tags/A >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git show-ref --verify D >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'show-ref --verify -q' '
+ >expect &&
+
+ git show-ref --verify -q refs/tags/A >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git show-ref --verify -q A >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git show-ref --verify -q tags/A >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git show-ref --verify -q D >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'show-ref -d' '
+ {
+ echo $(git rev-parse refs/tags/A) refs/tags/A &&
+ echo $(git rev-parse refs/tags/A^0) "refs/tags/A^{}"
+ echo $(git rev-parse refs/tags/C) refs/tags/C
+ } >expect &&
+ git show-ref -d A C >actual &&
+ test_cmp expect actual &&
+
+ git show-ref -d tags/A tags/C >actual &&
+ test_cmp expect actual &&
+
+ git show-ref -d refs/tags/A refs/tags/C >actual &&
+ test_cmp expect actual &&
+
+ echo $(git rev-parse refs/heads/master) refs/heads/master >expect &&
+ git show-ref -d master >actual &&
+ test_cmp expect actual &&
+
+ git show-ref -d heads/master >actual &&
+ test_cmp expect actual &&
+
+ git show-ref -d refs/heads/master >actual &&
+ test_cmp expect actual &&
+
+ git show-ref -d --verify refs/heads/master >actual &&
+ test_cmp expect actual &&
+
+ >expect &&
+
+ test_must_fail git show-ref -d --verify master >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git show-ref -d --verify heads/master >actual &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'show-ref --heads, --tags, --head, pattern' '
+ for branch in B master side
+ do
+ echo $(git rev-parse refs/heads/$branch) refs/heads/$branch
+ done >expect.heads &&
+ git show-ref --heads >actual &&
+ test_cmp expect.heads actual &&
+
+ for tag in A B C
+ do
+ echo $(git rev-parse refs/tags/$tag) refs/tags/$tag
+ done >expect.tags &&
+ git show-ref --tags >actual &&
+ test_cmp expect.tags actual &&
+
+ cat expect.heads expect.tags >expect &&
+ git show-ref --heads --tags >actual &&
+ test_cmp expect actual &&
+
+ {
+ echo $(git rev-parse HEAD) HEAD &&
+ cat expect.heads expect.tags
+ } >expect &&
+ git show-ref --heads --tags --head >actual &&
+ test_cmp expect actual &&
+
+ {
+ echo $(git rev-parse HEAD) HEAD &&
+ echo $(git rev-parse refs/heads/B) refs/heads/B
+ echo $(git rev-parse refs/tags/B) refs/tags/B
+ } >expect &&
+ git show-ref --head B >actual &&
+ test_cmp expect actual &&
+
+ {
+ echo $(git rev-parse HEAD) HEAD &&
+ echo $(git rev-parse refs/heads/B) refs/heads/B
+ echo $(git rev-parse refs/tags/B) refs/tags/B
+ echo $(git rev-parse refs/tags/B^0) "refs/tags/B^{}"
+ } >expect &&
+ git show-ref --head -d B >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1404-update-ref-df-conflicts.sh b/t/t1404-update-ref-df-conflicts.sh
new file mode 100755
index 0000000..66bafb5
--- /dev/null
+++ b/t/t1404-update-ref-df-conflicts.sh
@@ -0,0 +1,107 @@
+#!/bin/sh
+
+test_description='Test git update-ref with D/F conflicts'
+. ./test-lib.sh
+
+test_update_rejected () {
+ prefix="$1" &&
+ before="$2" &&
+ pack="$3" &&
+ create="$4" &&
+ error="$5" &&
+ printf "create $prefix/%s $C\n" $before |
+ git update-ref --stdin &&
+ git for-each-ref $prefix >unchanged &&
+ if $pack
+ then
+ git pack-refs --all
+ fi &&
+ printf "create $prefix/%s $C\n" $create >input &&
+ test_must_fail git update-ref --stdin <input 2>output.err &&
+ grep -F "$error" output.err &&
+ git for-each-ref $prefix >actual &&
+ test_cmp unchanged actual
+}
+
+Q="'"
+
+test_expect_success 'setup' '
+
+ git commit --allow-empty -m Initial &&
+ C=$(git rev-parse HEAD)
+
+'
+
+test_expect_success 'existing loose ref is a simple prefix of new' '
+
+ prefix=refs/1l &&
+ test_update_rejected $prefix "a c e" false "b c/x d" \
+ "$Q$prefix/c$Q exists; cannot create $Q$prefix/c/x$Q"
+
+'
+
+test_expect_success 'existing packed ref is a simple prefix of new' '
+
+ prefix=refs/1p &&
+ test_update_rejected $prefix "a c e" true "b c/x d" \
+ "$Q$prefix/c$Q exists; cannot create $Q$prefix/c/x$Q"
+
+'
+
+test_expect_success 'existing loose ref is a deeper prefix of new' '
+
+ prefix=refs/2l &&
+ test_update_rejected $prefix "a c e" false "b c/x/y d" \
+ "$Q$prefix/c$Q exists; cannot create $Q$prefix/c/x/y$Q"
+
+'
+
+test_expect_success 'existing packed ref is a deeper prefix of new' '
+
+ prefix=refs/2p &&
+ test_update_rejected $prefix "a c e" true "b c/x/y d" \
+ "$Q$prefix/c$Q exists; cannot create $Q$prefix/c/x/y$Q"
+
+'
+
+test_expect_success 'new ref is a simple prefix of existing loose' '
+
+ prefix=refs/3l &&
+ test_update_rejected $prefix "a c/x e" false "b c d" \
+ "$Q$prefix/c/x$Q exists; cannot create $Q$prefix/c$Q"
+
+'
+
+test_expect_success 'new ref is a simple prefix of existing packed' '
+
+ prefix=refs/3p &&
+ test_update_rejected $prefix "a c/x e" true "b c d" \
+ "$Q$prefix/c/x$Q exists; cannot create $Q$prefix/c$Q"
+
+'
+
+test_expect_success 'new ref is a deeper prefix of existing loose' '
+
+ prefix=refs/4l &&
+ test_update_rejected $prefix "a c/x/y e" false "b c d" \
+ "$Q$prefix/c/x/y$Q exists; cannot create $Q$prefix/c$Q"
+
+'
+
+test_expect_success 'new ref is a deeper prefix of existing packed' '
+
+ prefix=refs/4p &&
+ test_update_rejected $prefix "a c/x/y e" true "b c d" \
+ "$Q$prefix/c/x/y$Q exists; cannot create $Q$prefix/c$Q"
+
+'
+
+test_expect_success 'one new ref is a simple prefix of another' '
+
+ prefix=refs/5 &&
+ test_update_rejected $prefix "a e" false "b c c/x d" \
+ "cannot process $Q$prefix/c$Q and $Q$prefix/c/x$Q at the same time"
+
+'
+
+test_done
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index 25046c4..dd2be04 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -62,18 +62,18 @@ test_expect_success setup '
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` &&
+ 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` &&
+ L=$(git rev-parse --verify HEAD) &&
check_fsck &&
rm -f C A/B/E &&
@@ -81,15 +81,15 @@ test_expect_success setup '
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` &&
+ 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` &&
+ K=$(git rev-parse --verify HEAD) &&
check_fsck &&
check_have A B C D E F G H I J K L &&
@@ -100,8 +100,8 @@ test_expect_success setup '
check_fsck &&
- loglen=$(wc -l <.git/logs/refs/heads/master) &&
- test $loglen = 4
+ git reflog refs/heads/master >output &&
+ test_line_count = 4 output
'
test_expect_success rewind '
@@ -117,8 +117,8 @@ test_expect_success rewind '
check_have A B C D E F G H I J K L &&
- loglen=$(wc -l <.git/logs/refs/heads/master) &&
- test $loglen = 5
+ git reflog refs/heads/master >output &&
+ test_line_count = 5 output
'
test_expect_success 'corrupt and check' '
@@ -136,8 +136,8 @@ test_expect_success 'reflog expire --dry-run should not touch reflog' '
--stale-fix \
--all &&
- loglen=$(wc -l <.git/logs/refs/heads/master) &&
- test $loglen = 5 &&
+ git reflog refs/heads/master >output &&
+ test_line_count = 5 output &&
check_fsck "missing blob $F"
'
@@ -150,8 +150,8 @@ test_expect_success 'reflog expire' '
--stale-fix \
--all &&
- loglen=$(wc -l <.git/logs/refs/heads/master) &&
- test $loglen = 2 &&
+ git reflog refs/heads/master >output &&
+ test_line_count = 2 output &&
check_fsck "dangling commit $K"
'
@@ -186,8 +186,8 @@ test_expect_success 'delete' '
test_tick &&
git commit -m tiger C &&
- HEAD_entry_count=$(git reflog | wc -l)
- master_entry_count=$(git reflog show master | wc -l)
+ 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 &&
@@ -199,13 +199,13 @@ test_expect_success 'delete' '
test $HEAD_entry_count = $(git reflog | wc -l) &&
! grep ox < output &&
- master_entry_count=$(wc -l < 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)
+ HEAD_entry_count=$(git reflog | wc -l) &&
git reflog delete master@{07.04.2005.15:15:00.-0700} &&
git reflog show master > output &&
@@ -217,9 +217,8 @@ test_expect_success 'delete' '
test_expect_success 'rewind2' '
test_tick && git reset --hard HEAD~2 &&
- loglen=$(wc -l <.git/logs/refs/heads/master) &&
- test $loglen = 4
-
+ git reflog refs/heads/master >output &&
+ test_line_count = 4 output
'
test_expect_success '--expire=never' '
@@ -228,9 +227,8 @@ test_expect_success '--expire=never' '
--expire=never \
--expire-unreachable=never \
--all &&
- loglen=$(wc -l <.git/logs/refs/heads/master) &&
- test $loglen = 4
-
+ git reflog refs/heads/master >output &&
+ test_line_count = 4 output
'
test_expect_success 'gc.reflogexpire=never' '
@@ -238,8 +236,8 @@ 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
+ git reflog refs/heads/master >output &&
+ test_line_count = 4 output
'
test_expect_success 'gc.reflogexpire=false' '
@@ -247,12 +245,129 @@ 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 reflog refs/heads/master >output &&
+ test_line_count = 4 output &&
git config --unset gc.reflogexpire &&
git config --unset gc.reflogexpireunreachable
'
+test_expect_success 'checkout should not delete log for packed ref' '
+ test $(git reflog master | wc -l) = 4 &&
+ git branch foo &&
+ git pack-refs --all &&
+ git checkout foo &&
+ test $(git reflog master | wc -l) = 4
+'
+
+test_expect_success 'stale dirs do not cause d/f conflicts (reflogs on)' '
+ test_when_finished "git branch -d one || git branch -d one/two" &&
+
+ git branch one/two master &&
+ echo "one/two@{0} branch: Created from master" >expect &&
+ git log -g --format="%gd %gs" one/two >actual &&
+ test_cmp expect actual &&
+ git branch -d one/two &&
+
+ # now logs/refs/heads/one is a stale directory, but
+ # we should move it out of the way to create "one" reflog
+ git branch one master &&
+ echo "one@{0} branch: Created from master" >expect &&
+ git log -g --format="%gd %gs" one >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stale dirs do not cause d/f conflicts (reflogs off)' '
+ test_when_finished "git branch -d one || git branch -d one/two" &&
+
+ git branch one/two master &&
+ echo "one/two@{0} branch: Created from master" >expect &&
+ git log -g --format="%gd %gs" one/two >actual &&
+ test_cmp expect actual &&
+ git branch -d one/two &&
+
+ # same as before, but we only create a reflog for "one" if
+ # it already exists, which it does not
+ git -c core.logallrefupdates=false branch one master &&
+ : >expect &&
+ git log -g --format="%gd %gs" one >actual &&
+ test_cmp expect actual
+'
+
+# Triggering the bug detected by this test requires a newline to fall
+# exactly BUFSIZ-1 bytes from the end of the file. We don't know
+# what that value is, since it's platform dependent. However, if
+# we choose some value N, we also catch any D which divides N evenly
+# (since we will read backwards in chunks of D). So we choose 8K,
+# which catches glibc (with an 8K BUFSIZ) and *BSD (1K).
+#
+# Each line is 114 characters, so we need 75 to still have a few before the
+# last 8K. The 89-character padding on the final entry lines up our
+# newline exactly.
+test_expect_success 'parsing reverse reflogs at BUFSIZ boundaries' '
+ git checkout -b reflogskip &&
+ z38=00000000000000000000000000000000000000 &&
+ ident="abc <xyz> 0000000001 +0000" &&
+ for i in $(test_seq 1 75); do
+ printf "$z38%02d $z38%02d %s\t" $i $(($i+1)) "$ident" &&
+ if test $i = 75; then
+ for j in $(test_seq 1 89); do
+ printf X
+ done
+ else
+ printf X
+ fi &&
+ printf "\n"
+ done >.git/logs/refs/heads/reflogskip &&
+ git rev-parse reflogskip@{73} >actual &&
+ echo ${z38}03 >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no segfaults for reflog containing non-commit sha1s' '
+ git update-ref --create-reflog -m "Creating ref" \
+ refs/tests/tree-in-reflog HEAD &&
+ git update-ref -m "Forcing tree" refs/tests/tree-in-reflog HEAD^{tree} &&
+ git update-ref -m "Restoring to commit" refs/tests/tree-in-reflog HEAD &&
+ git reflog refs/tests/tree-in-reflog
+'
+
+test_expect_failure 'reflog with non-commit entries displays all entries' '
+ git reflog refs/tests/tree-in-reflog >actual &&
+ test_line_count = 3 actual
+'
+
+test_expect_success 'reflog expire operates on symref not referrent' '
+ git branch -l the_symref &&
+ git branch -l referrent &&
+ git update-ref referrent HEAD &&
+ git symbolic-ref refs/heads/the_symref refs/heads/referrent &&
+ test_when_finished "rm -f .git/refs/heads/referrent.lock" &&
+ touch .git/refs/heads/referrent.lock &&
+ git reflog expire --expire=all the_symref
+'
+
+test_expect_success 'continue walking past root commits' '
+ git init orphanage &&
+ (
+ cd orphanage &&
+ cat >expect <<-\EOF &&
+ HEAD@{0} commit (initial): orphan2-1
+ HEAD@{1} commit: orphan1-2
+ HEAD@{2} commit (initial): orphan1-1
+ HEAD@{3} commit (initial): initial
+ EOF
+ test_commit initial &&
+ git reflog &&
+ git checkout --orphan orphan1 &&
+ test_commit orphan1-1 &&
+ test_commit orphan1-2 &&
+ git checkout --orphan orphan2 &&
+ test_commit orphan2-1 &&
+ git log -g --format="%gd %gs" >actual &&
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index ba25ff3..6ac7734 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -28,6 +28,24 @@ test_expect_success 'oneline reflog format' '
test_cmp expect actual
'
+test_expect_success 'reflog default format' '
+ git reflog -1 >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+commit e46513e
+Reflog: HEAD@{0} (C O Mitter <committer@example.com>)
+Reflog message: commit (initial): one
+Author: A U Thor <author@example.com>
+
+ one
+EOF
+test_expect_success 'override reflog default format' '
+ git reflog --format=short -1 >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
@@ -47,30 +65,110 @@ test_expect_success 'using @{now} syntax shows reflog date (oneline)' '
'
cat >expect <<'EOF'
-Reflog: HEAD@{1112911993 -0700} (C O Mitter <committer@example.com>)
+HEAD@{Thu Apr 7 15:13:13 2005 -0700}
+EOF
+test_expect_success 'using @{now} syntax shows reflog date (format=%gd)' '
+ git log -g -1 --format=%gd HEAD@{now} >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{Thu Apr 7 15:13:13 2005 -0700} (C O Mitter <committer@example.com>)
Reflog message: commit (initial): one
EOF
test_expect_success 'using --date= shows reflog date (multiline)' '
- git log -g -1 --date=raw >tmp &&
+ git log -g -1 --date=default >tmp &&
grep ^Reflog <tmp >actual &&
test_cmp expect actual
'
cat >expect <<'EOF'
-e46513e HEAD@{1112911993 -0700}: commit (initial): one
+e46513e HEAD@{Thu Apr 7 15:13:13 2005 -0700}: commit (initial): one
EOF
test_expect_success 'using --date= shows reflog date (oneline)' '
- git log -g -1 --oneline --date=raw >actual &&
+ git log -g -1 --oneline --date=default >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+HEAD@{1112911993 -0700}
+EOF
+test_expect_success 'using --date= shows reflog date (format=%gd)' '
+ git log -g -1 --format=%gd --date=raw >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{0} (C O Mitter <committer@example.com>)
+Reflog message: commit (initial): one
+EOF
+test_expect_success 'log.date does not invoke "--date" magic (multiline)' '
+ test_config log.date raw &&
+ git log -g -1 >tmp &&
+ grep ^Reflog <tmp >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+e46513e HEAD@{0}: commit (initial): one
+EOF
+test_expect_success 'log.date does not invoke "--date" magic (oneline)' '
+ test_config log.date raw &&
+ git log -g -1 --oneline >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+HEAD@{0}
+EOF
+test_expect_success 'log.date does not invoke "--date" magic (format=%gd)' '
+ test_config log.date raw &&
+ git log -g -1 --format=%gd >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+HEAD@{0}
+EOF
+test_expect_success '--date magic does not override explicit @{0} syntax' '
+ git log -g -1 --format=%gd --date=raw HEAD@{0} >actual &&
test_cmp expect actual
'
: >expect
test_expect_success 'empty reflog file' '
git branch empty &&
- : >.git/logs/refs/heads/empty &&
+ git reflog expire --expire=all refs/heads/empty &&
git log -g empty >actual &&
test_cmp expect actual
'
+# This guards against the alternative of showing the diffs vs. the
+# reflog ancestor. The reflog used is designed to list the commits
+# more than once, so as to exercise the corresponding logic.
+test_expect_success 'git log -g -p shows diffs vs. parents' '
+ test_commit two &&
+ git branch flipflop &&
+ git update-ref refs/heads/flipflop -m flip1 HEAD^ &&
+ git update-ref refs/heads/flipflop -m flop1 HEAD &&
+ git update-ref refs/heads/flipflop -m flip2 HEAD^ &&
+ git log -g -p flipflop >reflog &&
+ grep -v ^Reflog reflog >actual &&
+ git log -1 -p HEAD^ >log.one &&
+ git log -1 -p HEAD >log.two &&
+ (
+ cat log.one; echo
+ cat log.two; echo
+ cat log.one; echo
+ cat log.two
+ ) >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'reflog exists works' '
+ git reflog exists refs/heads/master &&
+ ! git reflog exists refs/heads/nonexistent
+'
+
test_done
diff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh
new file mode 100755
index 0000000..3acd895
--- /dev/null
+++ b/t/t1412-reflog-loop.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+test_description='reflog walk shows repeated commits again'
+. ./test-lib.sh
+
+test_expect_success 'setup commits' '
+ test_tick &&
+ echo content >file && git add file && git commit -m one &&
+ git tag one &&
+ echo content >>file && git add file && git commit -m two &&
+ git tag two
+'
+
+test_expect_success 'setup reflog with alternating commits' '
+ git checkout -b topic &&
+ git reset one &&
+ git reset two &&
+ git reset one &&
+ git reset two
+'
+
+test_expect_success 'reflog shows all entries' '
+ cat >expect <<-\EOF &&
+ topic@{0} reset: moving to two
+ topic@{1} reset: moving to one
+ topic@{2} reset: moving to two
+ topic@{3} reset: moving to one
+ topic@{4} branch: Created from HEAD
+ EOF
+ git log -g --format="%gd %gs" topic >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1413-reflog-detach.sh b/t/t1413-reflog-detach.sh
new file mode 100755
index 0000000..c730600
--- /dev/null
+++ b/t/t1413-reflog-detach.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='Test reflog interaction with detached HEAD'
+. ./test-lib.sh
+
+reset_state () {
+ git checkout master &&
+ cp saved_reflog .git/logs/HEAD
+}
+
+test_expect_success setup '
+ test_tick &&
+ git commit --allow-empty -m initial &&
+ git branch side &&
+ test_tick &&
+ git commit --allow-empty -m second &&
+ cat .git/logs/HEAD >saved_reflog
+'
+
+test_expect_success baseline '
+ reset_state &&
+ git rev-parse master master^ >expect &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'switch to branch' '
+ reset_state &&
+ git rev-parse side master master^ >expect &&
+ git checkout side &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'detach to other' '
+ reset_state &&
+ git rev-parse master side master master^ >expect &&
+ git checkout side &&
+ git checkout master^0 &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'detach to self' '
+ reset_state &&
+ git rev-parse master master master^ >expect &&
+ git checkout master^0 &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'attach to self' '
+ reset_state &&
+ git rev-parse master master master master^ >expect &&
+ git checkout master^0 &&
+ git checkout master &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'attach to other' '
+ reset_state &&
+ git rev-parse side master master master^ >expect &&
+ git checkout master^0 &&
+ git checkout side &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh
new file mode 100755
index 0000000..25ddab4
--- /dev/null
+++ b/t/t1430-bad-ref-name.sh
@@ -0,0 +1,334 @@
+#!/bin/sh
+
+test_description='Test handling of ref names that check-ref-format rejects'
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit one &&
+ test_commit two
+'
+
+test_expect_success 'fast-import: fail on invalid branch name ".badbranchname"' '
+ test_when_finished "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_must_fail git fast-import <input
+'
+
+test_expect_success 'fast-import: fail on invalid branch name "bad[branch]name"' '
+ test_when_finished "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_must_fail git fast-import <input
+'
+
+test_expect_success 'git branch shows badly named ref as warning' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch >output 2>error &&
+ test_i18ngrep -e "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -d can delete badly named ref' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch -d broken...ref &&
+ git branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -D can delete badly named ref' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch -D broken...ref &&
+ git branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -D cannot delete non-ref in .git dir' '
+ echo precious >.git/my-private-file &&
+ echo precious >expect &&
+ test_must_fail git branch -D ../../my-private-file &&
+ test_cmp expect .git/my-private-file
+'
+
+test_expect_success 'branch -D cannot delete ref in .git dir' '
+ git rev-parse HEAD >.git/my-private-file &&
+ git rev-parse HEAD >expect &&
+ git branch foo/legit &&
+ test_must_fail git branch -D foo////./././../../../my-private-file &&
+ test_cmp expect .git/my-private-file
+'
+
+test_expect_success 'branch -D cannot delete absolute path' '
+ git branch -f extra &&
+ test_must_fail git branch -D "$(pwd)/.git/refs/heads/extra" &&
+ test_cmp_rev HEAD extra
+'
+
+test_expect_success 'git branch cannot create a badly named ref' '
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test_must_fail git branch broken...ref &&
+ git branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -m cannot rename to a bad ref name' '
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test_might_fail git branch -D goodref &&
+ git branch goodref &&
+ test_must_fail git branch -m goodref broken...ref &&
+ test_cmp_rev master goodref &&
+ git branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_failure 'branch -m can rename from a bad ref name' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch -m broken...ref renamed &&
+ test_cmp_rev master renamed &&
+ git branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'push cannot create a badly named ref' '
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test_must_fail git push "file://$(pwd)" HEAD:refs/heads/broken...ref &&
+ git branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_failure 'push --mirror can delete badly named ref' '
+ top=$(pwd) &&
+ git init src &&
+ git init dest &&
+
+ (
+ cd src &&
+ test_commit one
+ ) &&
+ (
+ cd dest &&
+ test_commit two &&
+ git checkout --detach &&
+ cp .git/refs/heads/master .git/refs/heads/broken...ref
+ ) &&
+ git -C src push --mirror "file://$top/dest" &&
+ git -C dest branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'rev-parse skips symref pointing to broken name' '
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch shadow one &&
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ printf "ref: refs/heads/broken...ref\n" >.git/refs/tags/shadow &&
+ test_when_finished "rm -f .git/refs/tags/shadow" &&
+ git rev-parse --verify one >expect &&
+ git rev-parse --verify shadow >actual 2>err &&
+ test_cmp expect actual &&
+ test_i18ngrep "ignoring dangling symref refs/tags/shadow" err
+'
+
+test_expect_success 'for-each-ref emits warnings for broken names' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
+ test_when_finished "rm -f .git/refs/heads/badname" &&
+ printf "ref: refs/heads/master\n" >.git/refs/heads/broken...symref &&
+ test_when_finished "rm -f .git/refs/heads/broken...symref" &&
+ git for-each-ref >output 2>error &&
+ ! grep -e "broken\.\.\.ref" output &&
+ ! grep -e "badname" output &&
+ ! grep -e "broken\.\.\.symref" output &&
+ test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
+ test_i18ngrep "ignoring broken ref refs/heads/badname" error &&
+ test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.symref" error
+'
+
+test_expect_success 'update-ref -d can delete broken name' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git update-ref -d refs/heads/broken...ref >output 2>error &&
+ test_must_be_empty output &&
+ test_must_be_empty error &&
+ git branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -d can delete broken name' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch -d broken...ref >output 2>error &&
+ test_i18ngrep "Deleted branch broken...ref (was broken)" output &&
+ test_must_be_empty error &&
+ git branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'update-ref --no-deref -d can delete symref to broken name' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
+ test_when_finished "rm -f .git/refs/heads/badname" &&
+ git update-ref --no-deref -d refs/heads/badname >output 2>error &&
+ test_path_is_missing .git/refs/heads/badname &&
+ test_must_be_empty output &&
+ test_must_be_empty error
+'
+
+test_expect_success 'branch -d can delete symref to broken name' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
+ test_when_finished "rm -f .git/refs/heads/badname" &&
+ git branch -d badname >output 2>error &&
+ test_path_is_missing .git/refs/heads/badname &&
+ test_i18ngrep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
+ test_must_be_empty error
+'
+
+test_expect_success 'update-ref --no-deref -d can delete dangling symref to broken name' '
+ printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
+ test_when_finished "rm -f .git/refs/heads/badname" &&
+ git update-ref --no-deref -d refs/heads/badname >output 2>error &&
+ test_path_is_missing .git/refs/heads/badname &&
+ test_must_be_empty output &&
+ test_must_be_empty error
+'
+
+test_expect_success 'branch -d can delete dangling symref to broken name' '
+ printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
+ test_when_finished "rm -f .git/refs/heads/badname" &&
+ git branch -d badname >output 2>error &&
+ test_path_is_missing .git/refs/heads/badname &&
+ test_i18ngrep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
+ test_must_be_empty error
+'
+
+test_expect_success 'update-ref -d can delete broken name through symref' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
+ test_when_finished "rm -f .git/refs/heads/badname" &&
+ git update-ref -d refs/heads/badname >output 2>error &&
+ test_path_is_missing .git/refs/heads/broken...ref &&
+ test_must_be_empty output &&
+ test_must_be_empty error
+'
+
+test_expect_success 'update-ref --no-deref -d can delete symref with broken name' '
+ printf "ref: refs/heads/master\n" >.git/refs/heads/broken...symref &&
+ test_when_finished "rm -f .git/refs/heads/broken...symref" &&
+ git update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&
+ test_path_is_missing .git/refs/heads/broken...symref &&
+ test_must_be_empty output &&
+ test_must_be_empty error
+'
+
+test_expect_success 'branch -d can delete symref with broken name' '
+ printf "ref: refs/heads/master\n" >.git/refs/heads/broken...symref &&
+ test_when_finished "rm -f .git/refs/heads/broken...symref" &&
+ git branch -d broken...symref >output 2>error &&
+ test_path_is_missing .git/refs/heads/broken...symref &&
+ test_i18ngrep "Deleted branch broken...symref (was refs/heads/master)" output &&
+ test_must_be_empty error
+'
+
+test_expect_success 'update-ref --no-deref -d can delete dangling symref with broken name' '
+ printf "ref: refs/heads/idonotexist\n" >.git/refs/heads/broken...symref &&
+ test_when_finished "rm -f .git/refs/heads/broken...symref" &&
+ git update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&
+ test_path_is_missing .git/refs/heads/broken...symref &&
+ test_must_be_empty output &&
+ test_must_be_empty error
+'
+
+test_expect_success 'branch -d can delete dangling symref with broken name' '
+ printf "ref: refs/heads/idonotexist\n" >.git/refs/heads/broken...symref &&
+ test_when_finished "rm -f .git/refs/heads/broken...symref" &&
+ git branch -d broken...symref >output 2>error &&
+ test_path_is_missing .git/refs/heads/broken...symref &&
+ test_i18ngrep "Deleted branch broken...symref (was refs/heads/idonotexist)" output &&
+ test_must_be_empty error
+'
+
+test_expect_success 'update-ref -d cannot delete non-ref in .git dir' '
+ echo precious >.git/my-private-file &&
+ echo precious >expect &&
+ test_must_fail git update-ref -d my-private-file >output 2>error &&
+ test_must_be_empty output &&
+ test_i18ngrep -e "cannot lock .*: unable to resolve reference" error &&
+ test_cmp expect .git/my-private-file
+'
+
+test_expect_success 'update-ref -d cannot delete absolute path' '
+ git branch -f extra &&
+ test_must_fail git update-ref -d "$(pwd)/.git/refs/heads/extra" &&
+ test_cmp_rev HEAD extra
+'
+
+test_expect_success 'update-ref --stdin fails create with bad ref name' '
+ echo "create ~a refs/heads/master" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'update-ref --stdin fails update with bad ref name' '
+ echo "update ~a refs/heads/master" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'update-ref --stdin fails delete with bad ref name' '
+ echo "delete ~a refs/heads/master" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'update-ref --stdin -z fails create with bad ref name' '
+ printf "%s\0" "create ~a " refs/heads/master >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a " err
+'
+
+test_expect_success 'update-ref --stdin -z fails update with bad ref name' '
+ printf "%s\0" "update ~a" refs/heads/master "" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'update-ref --stdin -z fails delete with bad ref name' '
+ printf "%s\0" "delete ~a" refs/heads/master >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_done
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 759cf12..7ee8ea0 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -1,21 +1,23 @@
#!/bin/sh
-test_description='git fsck random collection of tests'
+test_description='git fsck random collection of tests
+
+* (HEAD) B
+* (master) A
+'
. ./test-lib.sh
test_expect_success setup '
+ git config gc.auto 0 &&
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)
+ git reflog expire --expire=now --all &&
+ >empty
'
test_expect_success 'loose objects borrowed from alternate are not missing' '
@@ -25,110 +27,500 @@ test_expect_success 'loose objects borrowed from alternate are not missing' '
git init &&
echo ../../../.git/objects >.git/objects/info/alternates &&
test_commit C fileC one &&
- git fsck >out &&
- ! grep "missing blob" out
- )
+ git fsck --no-dangling >../actual 2>&1
+ ) &&
+ test_cmp empty actual
'
-test_expect_success 'valid objects appear valid' '
- { git fsck 2>out; true; } &&
- ! grep error out &&
- ! grep fatal out
+test_expect_success 'HEAD is part of refs, valid objects appear valid' '
+ git fsck >actual 2>&1 &&
+ test_cmp empty actual
'
# 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 'setup: helpers for corruption tests' '
+ sha1_file() {
+ echo "$*" | sed "s#..#.git/objects/&/#"
+ } &&
+
+ remove_object() {
+ file=$(sha1_file "$*") &&
+ test -e "$file" &&
+ rm -f "$file"
+ }
+'
+
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)"
+ sha="$(dirname $new)$(basename $new)" &&
mv .git/objects/$old .git/objects/$new &&
+ test_when_finished "remove_object $sha" &&
git update-index --add --cacheinfo 100644 $sha foo &&
+ test_when_finished "git read-tree -u --reset HEAD" &&
tree=$(git write-tree) &&
+ test_when_finished "remove_object $tree" &&
cmt=$(echo bogus | git commit-tree $tree) &&
+ test_when_finished "remove_object $cmt" &&
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_when_finished "git update-ref -d refs/heads/bogus" &&
+
+ test_must_fail git fsck 2>out &&
+ cat out &&
+ grep "$sha.*corrupt" out
'
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
+ git rev-parse HEAD^{tree} >.git/refs/heads/invalid &&
+ test_when_finished "git update-ref -d refs/heads/invalid" &&
+ test_must_fail git fsck 2>out &&
+ cat out &&
+ grep "not a commit" out
+'
+
+test_expect_success 'HEAD link pointing at a funny object' '
+ test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
+ mv .git/HEAD .git/SAVED_HEAD &&
+ echo 0000000000000000000000000000000000000000 >.git/HEAD &&
+ # avoid corrupt/broken HEAD from interfering with repo discovery
+ test_must_fail env GIT_DIR=.git git fsck 2>out &&
+ cat out &&
+ grep "detached HEAD points" out
+'
+
+test_expect_success 'HEAD link pointing at a funny place' '
+ test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
+ mv .git/HEAD .git/SAVED_HEAD &&
+ echo "ref: refs/funny/place" >.git/HEAD &&
+ # avoid corrupt/broken HEAD from interfering with repo discovery
+ test_must_fail env GIT_DIR=.git git fsck 2>out &&
+ cat out &&
+ grep "HEAD points to something strange" out
'
-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" &&
+ test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
+ test_when_finished "git update-ref -d refs/heads/bogus" &&
git fsck 2>out &&
cat out &&
- ! grep "error in commit $new" out
+ ! grep "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" &&
+ test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
- git fsck 2>out &&
+ test_when_finished "git update-ref -d refs/heads/bogus" &&
+ test_must_fail 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
+test_expect_success 'missing < email delimiter is reported nicely' '
+ git cat-file commit HEAD >basis &&
+ sed "s/<//" basis >bad-email-2 &&
+ new=$(git hash-object -t commit -w --stdin <bad-email-2) &&
+ test_when_finished "remove_object $new" &&
+ git update-ref refs/heads/bogus "$new" &&
+ test_when_finished "git update-ref -d refs/heads/bogus" &&
+ test_must_fail git fsck 2>out &&
+ cat out &&
+ grep "error in commit $new.* - bad name" out
+'
-This is an invalid tag.
-EOF
+test_expect_success 'missing email is reported nicely' '
+ git cat-file commit HEAD >basis &&
+ sed "s/[a-z]* <[^>]*>//" basis >bad-email-3 &&
+ new=$(git hash-object -t commit -w --stdin <bad-email-3) &&
+ test_when_finished "remove_object $new" &&
+ git update-ref refs/heads/bogus "$new" &&
+ test_when_finished "git update-ref -d refs/heads/bogus" &&
+ test_must_fail git fsck 2>out &&
+ cat out &&
+ grep "error in commit $new.* - missing email" out
+'
+
+test_expect_success '> in name is reported' '
+ git cat-file commit HEAD >basis &&
+ sed "s/ </> </" basis >bad-email-4 &&
+ new=$(git hash-object -t commit -w --stdin <bad-email-4) &&
+ test_when_finished "remove_object $new" &&
+ git update-ref refs/heads/bogus "$new" &&
+ test_when_finished "git update-ref -d refs/heads/bogus" &&
+ test_must_fail git fsck 2>out &&
+ cat out &&
+ grep "error in commit $new" out
+'
+
+# date is 2^64 + 1
+test_expect_success 'integer overflow in timestamps is reported' '
+ git cat-file commit HEAD >basis &&
+ sed "s/^\\(author .*>\\) [0-9]*/\\1 18446744073709551617/" \
+ <basis >bad-timestamp &&
+ new=$(git hash-object -t commit -w --stdin <bad-timestamp) &&
+ test_when_finished "remove_object $new" &&
+ git update-ref refs/heads/bogus "$new" &&
+ test_when_finished "git update-ref -d refs/heads/bogus" &&
+ test_must_fail git fsck 2>out &&
+ cat out &&
+ grep "error in commit $new.*integer overflow" out
+'
+
+test_expect_success 'commit with NUL in header' '
+ git cat-file commit HEAD >basis &&
+ sed "s/author ./author Q/" <basis | q_to_nul >commit-NUL-header &&
+ new=$(git hash-object -t commit -w --stdin <commit-NUL-header) &&
+ test_when_finished "remove_object $new" &&
+ git update-ref refs/heads/bogus "$new" &&
+ test_when_finished "git update-ref -d refs/heads/bogus" &&
+ test_must_fail git fsck 2>out &&
+ cat out &&
+ grep "error in commit $new.*unterminated header: NUL at offset" out
+'
+
+test_expect_success 'malformatted tree object' '
+ test_when_finished "git update-ref -d refs/tags/wrong" &&
+ test_when_finished "remove_object \$T" &&
+ T=$(
+ GIT_INDEX_FILE=test-index &&
+ export GIT_INDEX_FILE &&
+ rm -f test-index &&
+ >x &&
+ git add x &&
+ T=$(git write-tree) &&
+ (
+ git cat-file tree $T &&
+ git cat-file tree $T
+ ) |
+ git hash-object -w -t tree --stdin
+ ) &&
+ test_must_fail git fsck 2>out &&
+ grep "error in tree .*contains duplicate file entries" out
+'
test_expect_success 'tag pointing to nonexistent' '
- tag=$(git hash-object -t tag -w --stdin < invalid-tag) &&
- echo $tag > .git/refs/tags/invalid &&
+ 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
+
+ tag=$(git hash-object -t tag -w --stdin <invalid-tag) &&
+ test_when_finished "remove_object $tag" &&
+ echo $tag >.git/refs/tags/invalid &&
+ test_when_finished "git update-ref -d refs/tags/invalid" &&
test_must_fail git fsck --tags >out &&
cat out &&
- grep "broken link" out &&
- rm .git/refs/tags/invalid
+ grep "broken link" out
'
-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
+test_expect_success 'tag pointing to something else than its type' '
+ sha=$(echo blob | git hash-object -w --stdin) &&
+ test_when_finished "remove_object $sha" &&
+ cat >wrong-tag <<-EOF &&
+ object $sha
+ type commit
+ tag wrong
+ tagger T A Gger <tagger@example.com> 1234567890 -0000
-This is an invalid tag.
-EOF
+ 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 &&
+ tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
+ test_when_finished "remove_object $tag" &&
+ echo $tag >.git/refs/tags/wrong &&
+ test_when_finished "git update-ref -d refs/tags/wrong" &&
+ test_must_fail git fsck --tags
+'
+
+test_expect_success 'tag with incorrect tag name & missing tagger' '
+ sha=$(git rev-parse HEAD) &&
+ cat >wrong-tag <<-EOF &&
+ object $sha
+ type commit
+ tag wrong name format
+
+ This is an invalid tag.
+ EOF
+
+ tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
+ test_when_finished "remove_object $tag" &&
+ echo $tag >.git/refs/tags/wrong &&
+ test_when_finished "git update-ref -d refs/tags/wrong" &&
+ git fsck --tags 2>out &&
+
+ cat >expect <<-EOF &&
+ warning in tag $tag: badTagName: invalid '\''tag'\'' name: wrong name format
+ warning in tag $tag: missingTaggerEntry: invalid format - expected '\''tagger'\'' line
+ EOF
+ test_cmp expect out
+'
+
+test_expect_success 'tag with bad tagger' '
+ sha=$(git rev-parse HEAD) &&
+ cat >wrong-tag <<-EOF &&
+ object $sha
+ type commit
+ tag not-quite-wrong
+ tagger Bad Tagger Name
+
+ This is an invalid tag.
+ EOF
+
+ tag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&
+ test_when_finished "remove_object $tag" &&
+ echo $tag >.git/refs/tags/wrong &&
+ test_when_finished "git update-ref -d refs/tags/wrong" &&
+ test_must_fail git fsck --tags 2>out &&
+ grep "error in tag .*: invalid author/committer" out
+'
+
+test_expect_success 'tag with NUL in header' '
+ sha=$(git rev-parse HEAD) &&
+ q_to_nul >tag-NUL-header <<-EOF &&
+ object $sha
+ type commit
+ tag contains-Q-in-header
+ tagger T A Gger <tagger@example.com> 1234567890 -0000
+
+ This is an invalid tag.
+ EOF
+
+ tag=$(git hash-object --literally -t tag -w --stdin <tag-NUL-header) &&
+ test_when_finished "remove_object $tag" &&
+ echo $tag >.git/refs/tags/wrong &&
+ test_when_finished "git update-ref -d refs/tags/wrong" &&
test_must_fail git fsck --tags 2>out &&
cat out &&
- grep "error in tag.*broken links" out &&
- rm .git/refs/tags/wrong
+ grep "error in tag $tag.*unterminated header: NUL at offset" out
+'
+
+test_expect_success 'cleaned up' '
+ git fsck >actual 2>&1 &&
+ test_cmp empty actual
+'
+
+test_expect_success 'rev-list --verify-objects' '
+ git rev-list --verify-objects --all >/dev/null 2>out &&
+ test_cmp empty out
'
+test_expect_success 'rev-list --verify-objects with bad sha1' '
+ sha=$(echo blob | git hash-object -w --stdin) &&
+ old=$(echo $sha | sed "s+^..+&/+") &&
+ new=$(dirname $old)/ffffffffffffffffffffffffffffffffffffff &&
+ sha="$(dirname $new)$(basename $new)" &&
+ mv .git/objects/$old .git/objects/$new &&
+ test_when_finished "remove_object $sha" &&
+ git update-index --add --cacheinfo 100644 $sha foo &&
+ test_when_finished "git read-tree -u --reset HEAD" &&
+ tree=$(git write-tree) &&
+ test_when_finished "remove_object $tree" &&
+ cmt=$(echo bogus | git commit-tree $tree) &&
+ test_when_finished "remove_object $cmt" &&
+ git update-ref refs/heads/bogus $cmt &&
+ test_when_finished "git update-ref -d refs/heads/bogus" &&
+ test_might_fail git rev-list --verify-objects refs/heads/bogus >/dev/null 2>out &&
+ cat out &&
+ grep -q "error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff" out
+'
+
+test_expect_success 'force fsck to ignore double author' '
+ git cat-file commit HEAD >basis &&
+ sed "s/^author .*/&,&/" <basis | tr , \\n >multiple-authors &&
+ new=$(git hash-object -t commit -w --stdin <multiple-authors) &&
+ test_when_finished "remove_object $new" &&
+ git update-ref refs/heads/bogus "$new" &&
+ test_when_finished "git update-ref -d refs/heads/bogus" &&
+ test_must_fail git fsck &&
+ git -c fsck.multipleAuthors=ignore fsck
+'
+
+_bz='\0'
+_bz5="$_bz$_bz$_bz$_bz$_bz"
+_bz20="$_bz5$_bz5$_bz5$_bz5"
+
+test_expect_success 'fsck notices blob entry pointing to null sha1' '
+ (git init null-blob &&
+ cd null-blob &&
+ sha=$(printf "100644 file$_bz$_bz20" |
+ git hash-object -w --stdin -t tree) &&
+ git fsck 2>out &&
+ cat out &&
+ grep "warning.*null sha1" out
+ )
+'
+
+test_expect_success 'fsck notices submodule entry pointing to null sha1' '
+ (git init null-commit &&
+ cd null-commit &&
+ sha=$(printf "160000 submodule$_bz$_bz20" |
+ git hash-object -w --stdin -t tree) &&
+ git fsck 2>out &&
+ cat out &&
+ grep "warning.*null sha1" out
+ )
+'
+
+while read name path pretty; do
+ while read mode type; do
+ : ${pretty:=$path}
+ test_expect_success "fsck notices $pretty as $type" '
+ (
+ git init $name-$type &&
+ cd $name-$type &&
+ echo content >file &&
+ git add file &&
+ git commit -m base &&
+ blob=$(git rev-parse :file) &&
+ tree=$(git rev-parse HEAD^{tree}) &&
+ value=$(eval "echo \$$type") &&
+ printf "$mode $type %s\t%s" "$value" "$path" >bad &&
+ bad_tree=$(git mktree <bad) &&
+ git fsck 2>out &&
+ cat out &&
+ grep "warning.*tree $bad_tree" out
+ )'
+ done <<-\EOF
+ 100644 blob
+ 040000 tree
+ EOF
+done <<-EOF
+dot .
+dotdot ..
+dotgit .git
+dotgit-case .GIT
+dotgit-unicode .gI${u200c}T .gI{u200c}T
+dotgit-case2 .Git
+git-tilde1 git~1
+dotgitdot .git.
+dot-backslash-case .\\\\.GIT\\\\foobar
+dotgit-case-backslash .git\\\\foobar
+EOF
+
+test_expect_success 'fsck allows .Ňit' '
+ (
+ git init not-dotgit &&
+ cd not-dotgit &&
+ echo content >file &&
+ git add file &&
+ git commit -m base &&
+ blob=$(git rev-parse :file) &&
+ printf "100644 blob $blob\t.\\305\\207it" >tree &&
+ tree=$(git mktree <tree) &&
+ git fsck 2>err &&
+ test_line_count = 0 err
+ )
+'
+
+test_expect_success 'NUL in commit' '
+ rm -fr nul-in-commit &&
+ git init nul-in-commit &&
+ (
+ cd nul-in-commit &&
+ git commit --allow-empty -m "initial commitQNUL after message" &&
+ git cat-file commit HEAD >original &&
+ q_to_nul <original >munged &&
+ git hash-object -w -t commit --stdin <munged >name &&
+ git branch bad $(cat name) &&
+
+ test_must_fail git -c fsck.nulInCommit=error fsck 2>warn.1 &&
+ grep nulInCommit warn.1 &&
+ git fsck 2>warn.2 &&
+ grep nulInCommit warn.2
+ )
+'
+
+# create a static test repo which is broken by omitting
+# one particular object ($1, which is looked up via rev-parse
+# in the new repository).
+create_repo_missing () {
+ rm -rf missing &&
+ git init missing &&
+ (
+ cd missing &&
+ git commit -m one --allow-empty &&
+ mkdir subdir &&
+ echo content >subdir/file &&
+ git add subdir/file &&
+ git commit -m two &&
+ unrelated=$(echo unrelated | git hash-object --stdin -w) &&
+ git tag -m foo tag $unrelated &&
+ sha1=$(git rev-parse --verify "$1") &&
+ path=$(echo $sha1 | sed 's|..|&/|') &&
+ rm .git/objects/$path
+ )
+}
+
+test_expect_success 'fsck notices missing blob' '
+ create_repo_missing HEAD:subdir/file &&
+ test_must_fail git -C missing fsck
+'
+
+test_expect_success 'fsck notices missing subtree' '
+ create_repo_missing HEAD:subdir &&
+ test_must_fail git -C missing fsck
+'
+
+test_expect_success 'fsck notices missing root tree' '
+ create_repo_missing HEAD^{tree} &&
+ test_must_fail git -C missing fsck
+'
+
+test_expect_success 'fsck notices missing parent' '
+ create_repo_missing HEAD^ &&
+ test_must_fail git -C missing fsck
+'
+
+test_expect_success 'fsck notices missing tagged object' '
+ create_repo_missing tag^{blob} &&
+ test_must_fail git -C missing fsck
+'
+
+test_expect_success 'fsck notices ref pointing to missing commit' '
+ create_repo_missing HEAD &&
+ test_must_fail git -C missing fsck
+'
+
+test_expect_success 'fsck notices ref pointing to missing tag' '
+ create_repo_missing tag &&
+ test_must_fail git -C missing fsck
+'
+
+test_expect_success 'fsck --connectivity-only' '
+ rm -rf connectivity-only &&
+ git init connectivity-only &&
+ (
+ cd connectivity-only &&
+ touch empty &&
+ git add empty &&
+ test_commit empty &&
+ empty=.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 &&
+ rm -f $empty &&
+ echo invalid >$empty &&
+ test_must_fail git fsck --strict &&
+ git fsck --strict --connectivity-only &&
+ tree=$(git rev-parse HEAD:) &&
+ suffix=${tree#??} &&
+ tree=.git/objects/${tree%$suffix}/$suffix &&
+ rm -f $tree &&
+ echo invalid >$tree &&
+ test_must_fail git fsck --strict --connectivity-only
+ )
+'
test_done
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 48ee077..038e24c 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -3,85 +3,88 @@
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
+# usage: [options] label is-bare is-inside-git is-inside-work prefix git-dir
+test_rev_parse () {
+ d=
+ bare=
+ gitdir=
+ while :
+ do
+ case "$1" in
+ -C) d="$2"; shift; shift ;;
+ -b) case "$2" in
+ [tfu]*) bare="$2"; shift; shift ;;
+ *) error "test_rev_parse: bogus core.bare value '$2'" ;;
+ esac ;;
+ -g) gitdir="$2"; shift; shift ;;
+ -*) error "test_rev_parse: unrecognized option '$1'" ;;
+ *) break ;;
+ esac
+ done
- 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)\""
+ name=$1
shift
- [ $# -eq 0 ] && return
- test_expect_success "$name: git-dir" \
- "test '$1' = \"\$(git rev-parse --git-dir)\""
- shift
- [ $# -eq 0 ] && return
+ for o in --is-bare-repository \
+ --is-inside-git-dir \
+ --is-inside-work-tree \
+ --show-prefix \
+ --git-dir
+ do
+ test $# -eq 0 && break
+ expect="$1"
+ test_expect_success "$name: $o" '
+ if test -n "$gitdir"
+ then
+ test_when_finished "unset GIT_DIR" &&
+ GIT_DIR="$gitdir" &&
+ export GIT_DIR
+ fi &&
+
+ case "$bare" in
+ t*) test_config ${d:+-C} ${d:+"$d"} core.bare true ;;
+ f*) test_config ${d:+-C} ${d:+"$d"} core.bare false ;;
+ u*) test_unconfig ${d:+-C} ${d:+"$d"} core.bare ;;
+ esac &&
+
+ echo "$expect" >expect &&
+ git ${d:+-C} ${d:+"$d"} rev-parse $o >actual &&
+ test_cmp expect actual
+ '
+ shift
+ done
}
-# label is-bare is-inside-git is-inside-work prefix git-dir
-
ROOT=$(pwd)
+test_expect_success 'setup' '
+ mkdir -p sub/dir work &&
+ cp -R .git repo.git
+'
+
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
+test_rev_parse -C .git .git/ false true false '' .
+test_rev_parse -C .git/objects .git/objects/ false true false '' "$ROOT/.git"
-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
+test_rev_parse -C sub/dir subdirectory false false true sub/dir/ "$ROOT/.git"
-git config core.bare true
-test_rev_parse 'core.bare = true' true false false
+test_rev_parse -b t 'core.bare = true' true false false
-git config --unset core.bare
-test_rev_parse 'core.bare undefined' false false true
+test_rev_parse -b u '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 ''
+test_rev_parse -C work -g ../.git -b f '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 ''
+test_rev_parse -C work -g ../.git -b t '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 ''
+test_rev_parse -C work -g ../.git -b u '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 ''
+test_rev_parse -C work -g ../repo.git -b f '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 ''
+test_rev_parse -C work -g ../repo.git -b t '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_rev_parse -C work -g ../repo.git -b u 'GIT_DIR=../repo.git, core.bare undefined' false false true ''
test_done
diff --git a/t/t1501-work-tree.sh b/t/t1501-work-tree.sh
new file mode 100755
index 0000000..cc5b870
--- /dev/null
+++ b/t/t1501-work-tree.sh
@@ -0,0 +1,426 @@
+#!/bin/sh
+
+test_description='test separate work tree'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ EMPTY_TREE=$(git write-tree) &&
+ EMPTY_BLOB=$(git hash-object -t blob --stdin </dev/null) &&
+ CHANGED_BLOB=$(echo changed | git hash-object -t blob --stdin) &&
+ EMPTY_BLOB7=$(echo $EMPTY_BLOB | sed "s/\(.......\).*/\1/") &&
+ CHANGED_BLOB7=$(echo $CHANGED_BLOB | sed "s/\(.......\).*/\1/") &&
+
+ mkdir -p work/sub/dir &&
+ mkdir -p work2 &&
+ mv .git repo.git
+'
+
+test_expect_success 'setup: helper for testing rev-parse' '
+ test_rev_parse() {
+ echo $1 >expected.bare &&
+ echo $2 >expected.inside-git &&
+ echo $3 >expected.inside-worktree &&
+ if test $# -ge 4
+ then
+ echo $4 >expected.prefix
+ fi &&
+
+ git rev-parse --is-bare-repository >actual.bare &&
+ git rev-parse --is-inside-git-dir >actual.inside-git &&
+ git rev-parse --is-inside-work-tree >actual.inside-worktree &&
+ if test $# -ge 4
+ then
+ git rev-parse --show-prefix >actual.prefix
+ fi &&
+
+ test_cmp expected.bare actual.bare &&
+ test_cmp expected.inside-git actual.inside-git &&
+ test_cmp expected.inside-worktree actual.inside-worktree &&
+ if test $# -ge 4
+ then
+ # rev-parse --show-prefix should output
+ # a single newline when at the top of the work tree,
+ # but we test for that separately.
+ test -z "$4" && ! test -s actual.prefix ||
+ test_cmp expected.prefix actual.prefix
+ fi
+ }
+'
+
+test_expect_success 'setup: core.worktree = relative path' '
+ sane_unset GIT_WORK_TREE &&
+ GIT_DIR=repo.git &&
+ GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
+ export GIT_DIR GIT_CONFIG &&
+ git config core.worktree ../work
+'
+
+test_expect_success 'outside' '
+ test_rev_parse false false false
+'
+
+test_expect_success 'inside work tree' '
+ (
+ cd work &&
+ GIT_DIR=../repo.git &&
+ GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
+ test_rev_parse false false true ""
+ )
+'
+
+test_expect_success 'empty prefix is actually written out' '
+ echo >expected &&
+ (
+ cd work &&
+ GIT_DIR=../repo.git &&
+ GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
+ git rev-parse --show-prefix >../actual
+ ) &&
+ test_cmp expected actual
+'
+
+test_expect_success 'subdir of work tree' '
+ (
+ cd work/sub/dir &&
+ GIT_DIR=../../../repo.git &&
+ GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
+ test_rev_parse false false true sub/dir/
+ )
+'
+
+test_expect_success 'setup: core.worktree = absolute path' '
+ sane_unset GIT_WORK_TREE &&
+ GIT_DIR=$(pwd)/repo.git &&
+ GIT_CONFIG=$GIT_DIR/config &&
+ export GIT_DIR GIT_CONFIG &&
+ git config core.worktree "$(pwd)/work"
+'
+
+test_expect_success 'outside' '
+ test_rev_parse false false false &&
+ (
+ cd work2 &&
+ test_rev_parse false false false
+ )
+'
+
+test_expect_success 'inside work tree' '
+ (
+ cd work &&
+ test_rev_parse false false true ""
+ )
+'
+
+test_expect_success 'subdir of work tree' '
+ (
+ cd work/sub/dir &&
+ test_rev_parse false false true sub/dir/
+ )
+'
+
+test_expect_success 'setup: GIT_WORK_TREE=relative (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_DIR GIT_CONFIG GIT_WORK_TREE
+'
+
+test_expect_success 'outside' '
+ test_rev_parse false false false &&
+ (
+ cd work2 &&
+ test_rev_parse false false false
+ )
+'
+
+test_expect_success 'inside work tree' '
+ (
+ cd work &&
+ GIT_WORK_TREE=. &&
+ test_rev_parse false false true ""
+ )
+'
+
+test_expect_success 'subdir of work tree' '
+ (
+ cd work/sub/dir &&
+ GIT_WORK_TREE=../.. &&
+ test_rev_parse false false true sub/dir/
+ )
+'
+
+test_expect_success 'setup: GIT_WORK_TREE=absolute, below git dir' '
+ mv work repo.git/work &&
+ mv work2 repo.git/work2 &&
+ GIT_DIR=$(pwd)/repo.git &&
+ GIT_CONFIG=$GIT_DIR/config &&
+ GIT_WORK_TREE=$(pwd)/repo.git/work &&
+ export GIT_DIR GIT_CONFIG GIT_WORK_TREE
+'
+
+test_expect_success 'outside' '
+ echo outside &&
+ test_rev_parse false false false
+'
+
+test_expect_success 'in repo.git' '
+ (
+ cd repo.git &&
+ test_rev_parse false true false
+ ) &&
+ (
+ cd repo.git/objects &&
+ test_rev_parse false true false
+ ) &&
+ (
+ cd repo.git/work2 &&
+ test_rev_parse false true false
+ )
+'
+
+test_expect_success 'inside work tree' '
+ (
+ cd repo.git/work &&
+ test_rev_parse false true true ""
+ )
+'
+
+test_expect_success 'subdir of work tree' '
+ (
+ cd repo.git/work/sub/dir &&
+ test_rev_parse false true true sub/dir/
+ )
+'
+
+test_expect_success 'find work tree from repo' '
+ echo sub/dir/untracked >expected &&
+ cat <<-\EOF >repo.git/work/.gitignore &&
+ expected.*
+ actual.*
+ .gitignore
+ EOF
+ >repo.git/work/sub/dir/untracked &&
+ (
+ cd repo.git &&
+ git ls-files --others --exclude-standard >../actual
+ ) &&
+ test_cmp expected actual
+'
+
+test_expect_success 'find work tree from work tree' '
+ echo sub/dir/tracked >expected &&
+ >repo.git/work/sub/dir/tracked &&
+ (
+ cd repo.git/work/sub/dir &&
+ git --git-dir=../../.. add tracked
+ ) &&
+ (
+ cd repo.git &&
+ git ls-files >../actual
+ ) &&
+ test_cmp expected actual
+'
+
+test_expect_success '_gently() groks relative GIT_DIR & GIT_WORK_TREE' '
+ (
+ cd repo.git/work/sub/dir &&
+ GIT_DIR=../../.. &&
+ GIT_WORK_TREE=../.. &&
+ GIT_PAGER= &&
+ export GIT_DIR GIT_WORK_TREE GIT_PAGER &&
+
+ git diff --exit-code tracked &&
+ echo changed >tracked &&
+ test_must_fail git diff --exit-code tracked
+ )
+'
+
+test_expect_success 'diff-index respects work tree under .git dir' '
+ cat >diff-index-cached.expected <<-EOF &&
+ :000000 100644 $_z40 $EMPTY_BLOB A sub/dir/tracked
+ EOF
+ cat >diff-index.expected <<-EOF &&
+ :000000 100644 $_z40 $_z40 A sub/dir/tracked
+ EOF
+
+ (
+ GIT_DIR=repo.git &&
+ GIT_WORK_TREE=repo.git/work &&
+ export GIT_DIR GIT_WORK_TREE &&
+ git diff-index $EMPTY_TREE >diff-index.actual &&
+ git diff-index --cached $EMPTY_TREE >diff-index-cached.actual
+ ) &&
+ test_cmp diff-index.expected diff-index.actual &&
+ test_cmp diff-index-cached.expected diff-index-cached.actual
+'
+
+test_expect_success 'diff-files respects work tree under .git dir' '
+ cat >diff-files.expected <<-EOF &&
+ :100644 100644 $EMPTY_BLOB $_z40 M sub/dir/tracked
+ EOF
+
+ (
+ GIT_DIR=repo.git &&
+ GIT_WORK_TREE=repo.git/work &&
+ export GIT_DIR GIT_WORK_TREE &&
+ git diff-files >diff-files.actual
+ ) &&
+ test_cmp diff-files.expected diff-files.actual
+'
+
+test_expect_success 'git diff respects work tree under .git dir' '
+ cat >diff-TREE.expected <<-EOF &&
+ diff --git a/sub/dir/tracked b/sub/dir/tracked
+ new file mode 100644
+ index 0000000..$CHANGED_BLOB7
+ --- /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..$EMPTY_BLOB7
+ EOF
+ cat >diff-FILES.expected <<-EOF &&
+ diff --git a/sub/dir/tracked b/sub/dir/tracked
+ index $EMPTY_BLOB7..$CHANGED_BLOB7 100644
+ --- a/sub/dir/tracked
+ +++ b/sub/dir/tracked
+ @@ -0,0 +1 @@
+ +changed
+ EOF
+
+ (
+ GIT_DIR=repo.git &&
+ GIT_WORK_TREE=repo.git/work &&
+ export GIT_DIR GIT_WORK_TREE &&
+ git diff $EMPTY_TREE >diff-TREE.actual &&
+ git diff --cached $EMPTY_TREE >diff-TREE-cached.actual &&
+ git diff >diff-FILES.actual
+ ) &&
+ test_cmp diff-TREE.expected diff-TREE.actual &&
+ test_cmp diff-TREE-cached.expected diff-TREE-cached.actual &&
+ test_cmp diff-FILES.expected diff-FILES.actual
+'
+
+test_expect_success 'git grep' '
+ echo dir/tracked >expected.grep &&
+ (
+ cd repo.git/work/sub &&
+ GIT_DIR=../.. &&
+ GIT_WORK_TREE=.. &&
+ export GIT_DIR GIT_WORK_TREE &&
+ git grep -l changed >../../../actual.grep
+ ) &&
+ test_cmp expected.grep actual.grep
+'
+
+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 &&
+ test_might_fail 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_expect_success 'relative $GIT_WORK_TREE and git subprocesses' '
+ GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work \
+ test-subprocess --setup-work-tree rev-parse --show-toplevel >actual &&
+ echo "$(pwd)/repo.git/work" >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Multi-worktree setup' '
+ mkdir work &&
+ mkdir -p repo.git/repos/foo &&
+ cp repo.git/HEAD repo.git/index repo.git/repos/foo &&
+ test_might_fail cp repo.git/sharedindex.* repo.git/repos/foo &&
+ sane_unset GIT_DIR GIT_CONFIG GIT_WORK_TREE
+'
+
+test_expect_success 'GIT_DIR set (1)' '
+ echo "gitdir: repo.git/repos/foo" >gitfile &&
+ echo ../.. >repo.git/repos/foo/commondir &&
+ (
+ cd work &&
+ GIT_DIR=../gitfile git rev-parse --git-common-dir >actual &&
+ test-path-utils real_path "$TRASH_DIRECTORY/repo.git" >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'GIT_DIR set (2)' '
+ echo "gitdir: repo.git/repos/foo" >gitfile &&
+ echo "$(pwd)/repo.git" >repo.git/repos/foo/commondir &&
+ (
+ cd work &&
+ GIT_DIR=../gitfile git rev-parse --git-common-dir >actual &&
+ test-path-utils real_path "$TRASH_DIRECTORY/repo.git" >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'Auto discovery' '
+ echo "gitdir: repo.git/repos/foo" >.git &&
+ echo ../.. >repo.git/repos/foo/commondir &&
+ (
+ cd work &&
+ git rev-parse --git-common-dir >actual &&
+ test-path-utils real_path "$TRASH_DIRECTORY/repo.git" >expect &&
+ test_cmp expect actual &&
+ echo haha >data1 &&
+ git add data1 &&
+ git ls-files --full-name :/ | grep data1 >actual &&
+ echo work/data1 >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success '$GIT_DIR/common overrides core.worktree' '
+ mkdir elsewhere &&
+ git --git-dir=repo.git config core.worktree "$TRASH_DIRECTORY/elsewhere" &&
+ echo "gitdir: repo.git/repos/foo" >.git &&
+ echo ../.. >repo.git/repos/foo/commondir &&
+ (
+ cd work &&
+ git rev-parse --git-common-dir >actual &&
+ test-path-utils real_path "$TRASH_DIRECTORY/repo.git" >expect &&
+ test_cmp expect actual &&
+ echo haha >data2 &&
+ git add data2 &&
+ git ls-files --full-name :/ | grep data2 >actual &&
+ echo work/data2 >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success '$GIT_WORK_TREE overrides $GIT_DIR/common' '
+ echo "gitdir: repo.git/repos/foo" >.git &&
+ echo ../.. >repo.git/repos/foo/commondir &&
+ (
+ cd work &&
+ echo haha >data3 &&
+ git --git-dir=../.git --work-tree=. add data3 &&
+ git ls-files --full-name -- :/ | grep data3 >actual &&
+ echo data3 >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_done
diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh
deleted file mode 100755
index bd8b607..0000000
--- a/t/t1501-worktree.sh
+++ /dev/null
@@ -1,206 +0,0 @@
-#!/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
index b3195c4..310f93f 100755
--- a/t/t1502-rev-parse-parseopt.sh
+++ b/t/t1502-rev-parse-parseopt.sh
@@ -3,64 +3,97 @@
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
+test_expect_success 'setup optionspec' '
+ sed -e "s/^|//" >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
+|b,baz a short and long option
+|
+| An option group Header
+|C? option C with an optional argument
+|d,data? short and long option with an optional argument
+|
+| Argument hints
+|B=arg short option required argument
+|bar2=arg long option required argument
+|e,fuz=with-space short and long option required argument
+|s?some short option optional argument
+|long?data long option optional argument
+|g,fluf?path short and long option optional argument
+|longest=very-long-argument-hint a very long argument hint
+|pair=key=value with an equals sign in the hint
+|short-hint=a with a one symbol hint
+|
+|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
+ sed -e "s/^|//" >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
+| -b, --baz a short and long option
+|
+|An option group Header
+| -C[...] option C with an optional argument
+| -d, --data[=...] short and long option with an optional argument
+|
+|Argument hints
+| -B <arg> short option required argument
+| --bar2 <arg> long option required argument
+| -e, --fuz <with-space>
+| short and long option required argument
+| -s[<some>] short option optional argument
+| --long[=<data>] long option optional argument
+| -g, --fluf[=<path>] short and long option optional argument
+| --longest <very-long-argument-hint>
+| a very long argument hint
+| --pair <key=value> with an equals sign in the hint
+| --short-hint <a> with a one symbol hint
+|
+|Extras
+| --extra1 line above used to cause a segfault but no longer does
+|
+|EOF
+END_EXPECT
+ test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec &&
+ test_i18ncmp expect output
'
-cat > expect <<EOF
-set -- --foo --bar 'ham' -- 'arg'
+test_expect_success 'setup expect.1' "
+ cat > expect <<EOF
+set -- --foo --bar 'ham' -b -- 'arg'
EOF
+"
test_expect_success 'test --parseopt' '
- git rev-parse --parseopt -- --foo --bar=ham arg < optionspec > output &&
+ git rev-parse --parseopt -- --foo --bar=ham --baz 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 &&
+ git rev-parse --parseopt -- --foo arg --bar=ham --baz < optionspec > output &&
test_cmp expect output
'
-cat > expect <<EOF
+test_expect_success 'setup expect.2' "
+ 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 &&
@@ -72,31 +105,75 @@ test_expect_success 'test --parseopt --stop-at-non-option' '
test_cmp expect output
'
-cat > expect <<EOF
+test_expect_success 'setup expect.3' "
+ 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
+test_expect_success 'setup expect.4' "
+ 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
+test_expect_success 'setup expect.5' "
+ 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_expect_success 'setup expect.6' "
+ cat > expect <<EOF
+set -- --foo --bar='z' --baz -C'Z' --data='A' -- 'arg'
+EOF
+"
+
+test_expect_success 'test --parseopt --stuck-long' '
+ git rev-parse --parseopt --stuck-long -- --foo --bar=z -b arg -CZ -dA <optionspec >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'setup expect.7' "
+ cat > expect <<EOF
+set -- --data='' -C --baz -- 'arg'
+EOF
+"
+
+test_expect_success 'test --parseopt --stuck-long and empty optional argument' '
+ git rev-parse --parseopt --stuck-long -- --data= arg -C -b <optionspec >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'setup expect.8' "
+ cat > expect <<EOF
+set -- --data --baz -- 'arg'
+EOF
+"
+
+test_expect_success 'test --parseopt --stuck-long and long option with unset optional argument' '
+ git rev-parse --parseopt --stuck-long -- --data arg -b <optionspec >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'test --parseopt --stuck-long and short option with unset optional argument' '
+ git rev-parse --parseopt --stuck-long -- -d arg -b <optionspec >output &&
+ test_cmp expect output
+'
+
test_done
diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh
index cc65394..ab27d0d 100755
--- a/t/t1503-rev-parse-verify.sh
+++ b/t/t1503-rev-parse-verify.sh
@@ -72,15 +72,39 @@ test_expect_success 'fails with any bad rev or many good revs' '
test_expect_success 'fails silently when using -q' '
test_must_fail git rev-parse --verify --quiet 2>error &&
- test -z "$(cat error)" &&
+ test_must_be_empty error &&
test_must_fail git rev-parse -q --verify foo 2>error &&
- test -z "$(cat error)" &&
+ test_must_be_empty error &&
test_must_fail git rev-parse --verify -q HEAD bar 2>error &&
- test -z "$(cat error)" &&
+ test_must_be_empty error &&
test_must_fail git rev-parse --quiet --verify baz HEAD 2>error &&
- test -z "$(cat error)" &&
+ test_must_be_empty error &&
test_must_fail git rev-parse -q --verify $HASH2 HEAD 2>error &&
- test -z "$(cat error)"
+ test_must_be_empty error
+'
+
+test_expect_success 'fails silently when using -q with deleted reflogs' '
+ ref=$(git rev-parse HEAD) &&
+ git update-ref --create-reflog -m "message for refs/test" refs/test "$ref" &&
+ git reflog delete --updateref --rewrite refs/test@{0} &&
+ test_must_fail git rev-parse -q --verify refs/test@{0} >error 2>&1 &&
+ test_must_be_empty error
+'
+
+test_expect_success 'fails silently when using -q with not enough reflogs' '
+ ref=$(git rev-parse HEAD) &&
+ git update-ref --create-reflog -m "message for refs/test2" refs/test2 "$ref" &&
+ test_must_fail git rev-parse -q --verify refs/test2@{999} >error 2>&1 &&
+ test_must_be_empty error
+'
+
+test_expect_success 'succeeds silently with -q and reflogs that do not go far back enough in time' '
+ ref=$(git rev-parse HEAD) &&
+ git update-ref --create-reflog -m "message for refs/test3" refs/test3 "$ref" &&
+ git rev-parse -q --verify refs/test3@{1.year.ago} >actual 2>error &&
+ test_must_be_empty error &&
+ echo "$ref" >expect &&
+ test_cmp expect actual
'
test_expect_success 'no stdout output on error' '
@@ -104,4 +128,15 @@ test_expect_success 'use --default' '
test_must_fail git rev-parse --verify --default bar
'
+test_expect_success 'master@{n} for various n' '
+ N=$(git reflog | wc -l) &&
+ Nm1=$(($N-1)) &&
+ Np1=$(($N+1)) &&
+ git rev-parse --verify master@{0} &&
+ git rev-parse --verify master@{1} &&
+ git rev-parse --verify master@{$Nm1} &&
+ test_must_fail git rev-parse --verify master@{$N} &&
+ test_must_fail git rev-parse --verify master@{$Np1}
+'
+
test_done
diff --git a/t/t1504-ceiling-dirs.sh b/t/t1504-ceiling-dirs.sh
index df5ad8c..3d51615 100755
--- a/t/t1504-ceiling-dirs.sh
+++ b/t/t1504-ceiling-dirs.sh
@@ -9,8 +9,9 @@ test_prefix() {
}
test_fail() {
- test_expect_code 128 "$1: prefix" \
- "git rev-parse --show-prefix"
+ test_expect_success "$1: prefix" '
+ test_expect_code 128 git rev-parse --show-prefix
+ '
}
TRASH_ROOT="$PWD"
@@ -43,6 +44,10 @@ test_prefix ceil_at_sub ""
GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/"
test_prefix ceil_at_sub_slash ""
+if test_have_prereq SYMLINKS
+then
+ ln -s sub top
+fi
mkdir -p sub/dir || exit 1
cd sub/dir || exit 1
@@ -67,6 +72,19 @@ test_fail subdir_ceil_at_sub
GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/"
test_fail subdir_ceil_at_sub_slash
+if test_have_prereq SYMLINKS
+then
+ GIT_CEILING_DIRECTORIES="$TRASH_ROOT/top"
+ test_fail subdir_ceil_at_top
+ GIT_CEILING_DIRECTORIES="$TRASH_ROOT/top/"
+ test_fail subdir_ceil_at_top_slash
+
+ GIT_CEILING_DIRECTORIES=":$TRASH_ROOT/top"
+ test_prefix subdir_ceil_at_top_no_resolve "sub/dir/"
+ GIT_CEILING_DIRECTORIES=":$TRASH_ROOT/top/"
+ test_prefix subdir_ceil_at_top_slash_no_resolve "sub/dir/"
+fi
+
GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/dir"
test_prefix subdir_ceil_at_subdir "sub/dir/"
diff --git a/t/t1505-rev-parse-last.sh b/t/t1505-rev-parse-last.sh
index d709ecf..4969edb 100755
--- a/t/t1505-rev-parse-last.sh
+++ b/t/t1505-rev-parse-last.sh
@@ -32,32 +32,24 @@ test_expect_success 'setup' '
#
# and 'side' should be the last branch
-test_rev_equivalent () {
-
- git rev-parse "$1" > expect &&
- git rev-parse "$2" > output &&
- test_cmp expect output
-
-}
-
test_expect_success '@{-1} works' '
- test_rev_equivalent side @{-1}
+ test_cmp_rev side @{-1}
'
test_expect_success '@{-1}~2 works' '
- test_rev_equivalent side~2 @{-1}~2
+ test_cmp_rev side~2 @{-1}~2
'
test_expect_success '@{-1}^2 works' '
- test_rev_equivalent side^2 @{-1}^2
+ test_cmp_rev side^2 @{-1}^2
'
test_expect_success '@{-1}@{1} works' '
- test_rev_equivalent side@{1} @{-1}@{1}
+ test_cmp_rev side@{1} @{-1}@{1}
'
test_expect_success '@{-2} works' '
- test_rev_equivalent master @{-2}
+ test_cmp_rev master @{-2}
'
test_expect_success '@{-3} fails' '
diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh
index af721f9..86c2ff2 100755
--- a/t/t1506-rev-parse-diagnosis.sh
+++ b/t/t1506-rev-parse-diagnosis.sh
@@ -6,6 +6,16 @@ exec </dev/null
. ./test-lib.sh
+test_did_you_mean ()
+{
+ sq="'" &&
+ cat >expected <<-EOF &&
+ fatal: Path '$2$3' $4, but not ${5:-$sq$3$sq}.
+ Did you mean '$1:$2$3'${2:+ aka $sq$1:./$3$sq}?
+ EOF
+ test_cmp expected error
+}
+
HASH_file=
test_expect_success 'set up basic repo' '
@@ -31,6 +41,67 @@ test_expect_success 'correct file objects' '
test $HASH_file = $(git rev-parse :0:file.txt) )
'
+test_expect_success 'correct relative file objects (0)' '
+ git rev-parse :file.txt >expected &&
+ git rev-parse :./file.txt >result &&
+ test_cmp expected result &&
+ git rev-parse :0:./file.txt >result &&
+ test_cmp expected result
+'
+
+test_expect_success 'correct relative file objects (1)' '
+ git rev-parse HEAD:file.txt >expected &&
+ git rev-parse HEAD:./file.txt >result &&
+ test_cmp expected result
+'
+
+test_expect_success 'correct relative file objects (2)' '
+ (
+ cd subdir &&
+ git rev-parse HEAD:../file.txt >result &&
+ test_cmp ../expected result
+ )
+'
+
+test_expect_success 'correct relative file objects (3)' '
+ (
+ cd subdir &&
+ git rev-parse HEAD:../subdir/../file.txt >result &&
+ test_cmp ../expected result
+ )
+'
+
+test_expect_success 'correct relative file objects (4)' '
+ git rev-parse HEAD:subdir/file.txt >expected &&
+ (
+ cd subdir &&
+ git rev-parse HEAD:./file.txt >result &&
+ test_cmp ../expected result
+ )
+'
+
+test_expect_success 'correct relative file objects (5)' '
+ git rev-parse :subdir/file.txt >expected &&
+ (
+ cd subdir &&
+ git rev-parse :./file.txt >result &&
+ test_cmp ../expected result &&
+ git rev-parse :0:./file.txt >result &&
+ test_cmp ../expected result
+ )
+'
+
+test_expect_success 'correct relative file objects (6)' '
+ git rev-parse :file.txt >expected &&
+ (
+ cd subdir &&
+ git rev-parse :../file.txt >result &&
+ test_cmp ../expected result &&
+ git rev-parse :0:../file.txt >result &&
+ test_cmp ../expected result
+ )
+'
+
test_expect_success 'incorrect revision id' '
test_must_fail git rev-parse foobar:file.txt 2>error &&
grep "Invalid object name '"'"'foobar'"'"'." error &&
@@ -45,7 +116,7 @@ test_expect_success 'incorrect file in sha1:path' '
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_did_you_mean HEAD subdir/ file2.txt exists )
'
test_expect_success 'incorrect file in :path and :N:path' '
@@ -54,16 +125,94 @@ test_expect_success 'incorrect file in :path and :N:path' '
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 &&
+ test_did_you_mean ":0" "" file.txt "is in the index" "at stage 1" &&
(cd subdir &&
test_must_fail git rev-parse :1:file.txt 2> error &&
- grep "Did you mean '"'"':0:file.txt'"'"'?" error &&
+ test_did_you_mean ":0" "" file.txt "is in the index" "at stage 1" &&
test_must_fail git rev-parse :file2.txt 2> error &&
- grep "Did you mean '"'"':0:subdir/file2.txt'"'"'?" error &&
+ test_did_you_mean ":0" subdir/ file2.txt "is in the index" &&
test_must_fail git rev-parse :2:file2.txt 2> error &&
- grep "Did you mean '"'"':0:subdir/file2.txt'"'"'?" error) &&
+ test_did_you_mean :0 subdir/ file2.txt "is in the index") &&
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_expect_success 'invalid @{n} reference' '
+ test_must_fail git rev-parse master@{99999} >output 2>error &&
+ test -z "$(cat output)" &&
+ grep "fatal: Log for [^ ]* only has [0-9][0-9]* entries." error &&
+ test_must_fail git rev-parse --verify master@{99999} >output 2>error &&
+ test -z "$(cat output)" &&
+ grep "fatal: Log for [^ ]* only has [0-9][0-9]* entries." error
+'
+
+test_expect_success 'relative path not found' '
+ (
+ cd subdir &&
+ test_must_fail git rev-parse HEAD:./nonexistent.txt 2>error &&
+ grep subdir/nonexistent.txt error
+ )
+'
+
+test_expect_success 'relative path outside worktree' '
+ test_must_fail git rev-parse HEAD:../file.txt >output 2>error &&
+ test -z "$(cat output)" &&
+ grep "outside repository" error
+'
+
+test_expect_success 'relative path when cwd is outside worktree' '
+ test_must_fail git --git-dir=.git --work-tree=subdir rev-parse HEAD:./file.txt >output 2>error &&
+ test -z "$(cat output)" &&
+ grep "relative path syntax can.t be used outside working tree." error
+'
+
+test_expect_success '<commit>:file correctly diagnosed after a pathname' '
+ test_must_fail git rev-parse file.txt HEAD:file.txt 1>actual 2>error &&
+ test_i18ngrep ! "exists on disk" error &&
+ test_i18ngrep "no such path in the working tree" error &&
+ cat >expect <<-\EOF &&
+ file.txt
+ HEAD:file.txt
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'dotdot is not an empty set' '
+ ( H=$(git rev-parse HEAD) && echo $H && echo ^$H ) >expect &&
+
+ git rev-parse HEAD.. >actual &&
+ test_cmp expect actual &&
+
+ git rev-parse ..HEAD >actual &&
+ test_cmp expect actual &&
+
+ echo .. >expect &&
+ git rev-parse .. >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'arg before dashdash must be a revision (missing)' '
+ test_must_fail git rev-parse foobar -- 2>stderr &&
+ test_i18ngrep "bad revision" stderr
+'
+
+test_expect_success 'arg before dashdash must be a revision (file)' '
+ >foobar &&
+ test_must_fail git rev-parse foobar -- 2>stderr &&
+ test_i18ngrep "bad revision" stderr
+'
+
+test_expect_success 'arg before dashdash must be a revision (ambiguous)' '
+ >foobar &&
+ git update-ref refs/heads/foobar HEAD &&
+ {
+ # we do not want to use rev-parse here, because
+ # we are testing it
+ cat .git/refs/heads/foobar &&
+ printf "%s\n" --
+ } >expect &&
+ git rev-parse foobar -- >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
index 8c8dfda..46ef1f2 100755
--- a/t/t1507-rev-parse-upstream.sh
+++ b/t/t1507-rev-parse-upstream.sh
@@ -15,10 +15,21 @@ test_expect_success 'setup' '
test_commit 3 &&
(cd clone &&
test_commit 4 &&
- git branch --track my-side origin/side)
-
+ git branch --track my-side origin/side &&
+ git branch --track local-master master &&
+ git branch --track fun@ny origin/side &&
+ git branch --track @funny origin/side &&
+ git branch --track funny@ origin/side &&
+ git remote add -t master master-only .. &&
+ git fetch master-only &&
+ git branch bad-upstream &&
+ git config branch.bad-upstream.remote master-only &&
+ git config branch.bad-upstream.merge refs/heads/side
+ )
'
+sq="'"
+
full_name () {
(cd clone &&
git rev-parse --symbolic-full-name "$@")
@@ -29,6 +40,11 @@ commit_subject () {
git show -s --pretty=format:%s "$@")
}
+error_message () {
+ (cd clone &&
+ test_must_fail git rev-parse --verify "$@")
+}
+
test_expect_success '@{upstream} resolves to correct full name' '
test refs/remotes/origin/master = "$(full_name @{upstream})"
'
@@ -41,6 +57,28 @@ test_expect_success 'my-side@{upstream} resolves to correct full name' '
test refs/remotes/origin/side = "$(full_name my-side@{u})"
'
+test_expect_success 'upstream of branch with @ in middle' '
+ full_name fun@ny@{u} >actual &&
+ echo refs/remotes/origin/side >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'upstream of branch with @ at start' '
+ full_name @funny@{u} >actual &&
+ echo refs/remotes/origin/side >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'upstream of branch with @ at end' '
+ full_name funny@@{u} >actual &&
+ echo refs/remotes/origin/side >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'refs/heads/my-side@{upstream} does not resolve to my-side{upstream}' '
+ test_must_fail full_name refs/heads/my-side@{upstream}
+'
+
test_expect_success 'my-side@{u} resolves to correct commit' '
git checkout side &&
test_commit 5 &&
@@ -78,14 +116,13 @@ test_expect_success 'checkout -b new my-side@{u} forks from the same' '
test_expect_success 'merge my-side@{u} records the correct name' '
(
- sq="'\''" &&
cd clone || exit
git checkout master || exit
git branch -D new ;# can fail but is ok
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 &&
+ git show -s --pretty=tformat:%s >actual &&
+ echo "Merge remote-tracking branch ${sq}origin/side${sq}" >expect &&
test_cmp expect actual
)
'
@@ -107,6 +144,64 @@ test_expect_success 'checkout other@{u}' '
test_cmp expect actual
'
+test_expect_success 'branch@{u} works when tracking a local branch' '
+ test refs/heads/master = "$(full_name local-master@{u})"
+'
+
+test_expect_success 'branch@{u} error message when no upstream' '
+ cat >expect <<-EOF &&
+ fatal: no upstream configured for branch ${sq}non-tracking${sq}
+ EOF
+ error_message non-tracking@{u} 2>actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success '@{u} error message when no upstream' '
+ cat >expect <<-EOF &&
+ fatal: no upstream configured for branch ${sq}master${sq}
+ EOF
+ test_must_fail git rev-parse --verify @{u} 2>actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'branch@{u} error message with misspelt branch' '
+ cat >expect <<-EOF &&
+ fatal: no such branch: ${sq}no-such-branch${sq}
+ EOF
+ error_message no-such-branch@{u} 2>actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success '@{u} error message when not on a branch' '
+ cat >expect <<-EOF &&
+ fatal: HEAD does not point to a branch
+ EOF
+ git checkout HEAD^0 &&
+ test_must_fail git rev-parse --verify @{u} 2>actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'branch@{u} error message if upstream branch not fetched' '
+ cat >expect <<-EOF &&
+ fatal: upstream branch ${sq}refs/heads/side${sq} not stored as a remote-tracking branch
+ EOF
+ error_message bad-upstream@{u} 2>actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'pull works when tracking a local branch' '
+(
+ cd clone &&
+ git checkout local-master &&
+ git pull
+)
+'
+
+# makes sense if the previous one succeeded
+test_expect_success '@{u} works when tracking a local branch' '
+ test refs/heads/master = "$(full_name @{u})"
+'
+
cat >expect <<EOF
commit 8f489d01d0cc65c3b0f09504ec50b5ed02a70bd5
Reflog: master@{0} (C O Mitter <committer@example.com>)
@@ -136,4 +231,20 @@ test_expect_success 'log -g other@{u}@{now}' '
test_cmp expect actual
'
+test_expect_success '@{reflog}-parsing does not look beyond colon' '
+ echo content >@{yesterday} &&
+ git add @{yesterday} &&
+ git commit -m "funny reflog file" &&
+ git hash-object @{yesterday} >expect &&
+ git rev-parse HEAD:@{yesterday} >actual
+'
+
+test_expect_success '@{upstream}-parsing does not look beyond colon' '
+ echo content >@{upstream} &&
+ git add @{upstream} &&
+ git commit -m "funny upstream file" &&
+ git hash-object @{upstream} >expect &&
+ git rev-parse HEAD:@{upstream} >actual
+'
+
test_done
diff --git a/t/t1508-at-combinations.sh b/t/t1508-at-combinations.sh
index d5d6244..4a9964e 100755
--- a/t/t1508-at-combinations.sh
+++ b/t/t1508-at-combinations.sh
@@ -4,17 +4,27 @@ test_description='test various @{X} syntax combinations together'
. ./test-lib.sh
check() {
-test_expect_${3:-success} "$1 = $2" "
- echo '$2' >expect &&
- git log -1 --format=%s '$1' >actual &&
- test_cmp expect actual
-"
+ test_expect_${4:-success} "$1 = $3" "
+ echo '$3' >expect &&
+ if test '$2' = 'commit'
+ then
+ git log -1 --format=%s '$1' >actual
+ elif test '$2' = 'ref'
+ then
+ git rev-parse --symbolic-full-name '$1' >actual
+ else
+ git cat-file -p '$1' >actual
+ fi &&
+ test_cmp expect actual
+ "
}
+
nonsense() {
-test_expect_${2:-success} "$1 is nonsensical" "
- test_must_fail git log -1 '$1'
-"
+ test_expect_${2:-success} "$1 is nonsensical" "
+ test_must_fail git rev-parse --verify '$1'
+ "
}
+
fail() {
"$@" failure
}
@@ -25,27 +35,68 @@ test_expect_success 'setup' '
git checkout -b upstream-branch &&
test_commit upstream-one &&
test_commit upstream-two &&
+ if test_have_prereq !MINGW
+ then
+ git checkout -b @/at-test
+ fi &&
+ git checkout -b @@/at-test &&
+ git checkout -b @at-test &&
git checkout -b old-branch &&
test_commit old-one &&
test_commit old-two &&
git checkout -b new-branch &&
test_commit new-one &&
test_commit new-two &&
- git config branch.old-branch.remote . &&
- git config branch.old-branch.merge refs/heads/master &&
- git config branch.new-branch.remote . &&
- git config branch.new-branch.merge refs/heads/upstream-branch
+ git branch -u master old-branch &&
+ git branch -u upstream-branch new-branch
'
-check HEAD new-two
-check "@{1}" new-one
-check "@{-1}" old-two
-check "@{-1}@{1}" old-one
-check "@{u}" upstream-two
-check "@{u}@{1}" upstream-one
-check "@{-1}@{u}" master-two
-check "@{-1}@{u}@{1}" master-one
+check HEAD ref refs/heads/new-branch
+check "@{1}" commit new-one
+check "HEAD@{1}" commit new-one
+check "@{now}" commit new-two
+check "HEAD@{now}" commit new-two
+check "@{-1}" ref refs/heads/old-branch
+check "@{-1}@{0}" commit old-two
+check "@{-1}@{1}" commit old-one
+check "@{u}" ref refs/heads/upstream-branch
+check "HEAD@{u}" ref refs/heads/upstream-branch
+check "@{u}@{1}" commit upstream-one
+check "@{-1}@{u}" ref refs/heads/master
+check "@{-1}@{u}@{1}" commit master-one
+check "@" commit new-two
+check "@@{u}" ref refs/heads/upstream-branch
+check "@@/at-test" ref refs/heads/@@/at-test
+test_have_prereq MINGW ||
+check "@/at-test" ref refs/heads/@/at-test
+check "@at-test" ref refs/heads/@at-test
nonsense "@{u}@{-1}"
+nonsense "@{0}@{0}"
nonsense "@{1}@{u}"
+nonsense "HEAD@{-1}"
+nonsense "@{-1}@{-1}"
+
+# @{N} versus HEAD@{N}
+
+check "HEAD@{3}" commit old-two
+nonsense "@{3}"
+
+test_expect_success 'switch to old-branch' '
+ git checkout old-branch
+'
+
+check HEAD ref refs/heads/old-branch
+check "HEAD@{1}" commit new-two
+check "@{1}" commit old-one
+
+test_expect_success 'create path with @' '
+ echo content >normal &&
+ echo content >fun@ny &&
+ git add normal fun@ny &&
+ git commit -m "funny path"
+'
+
+check "@:normal" blob content
+check "@:fun@ny" blob content
test_done
diff --git a/t/t1509-root-worktree.sh b/t/t1509-root-work-tree.sh
index 7f60fd0..553a3f6 100755
--- a/t/t1509-root-worktree.sh
+++ b/t/t1509-root-work-tree.sh
@@ -98,8 +98,16 @@ test_foobar_foobar() {
'
}
-if ! test_have_prereq POSIXPERM || ! [ -w / ]; then
- skip_all="Dangerous test skipped. Read this test if you want to execute it"
+if ! test -w /
+then
+ skip_all="Test requiring writable / skipped. Read this test if you want to run it"
+ test_done
+fi
+
+if test -e /refs || test -e /objects || test -e /info || test -e /hooks ||
+ test -e /.git || test -e /foo || test -e /me
+then
+ skip_all="Skip test that clobbers existing files in /"
test_done
fi
@@ -108,15 +116,16 @@ if [ "$IKNOWWHATIAMDOING" != "YES" ]; then
test_done
fi
-if [ "$UID" = 0 ]; then
- skip_all="No you can't run this with root"
+if ! test_have_prereq NOT_ROOT
+then
+ skip_all="No you can't run this as root"
test_done
fi
ONE_SHA1=d00491fd7e5bb6fa28c517a0bb32b8b506539d4d
test_expect_success 'setup' '
- rm -rf /foo
+ rm -rf /foo &&
mkdir /foo &&
mkdir /foo/bar &&
echo 1 > /foo/foome &&
@@ -134,8 +143,8 @@ cat >ls.expected <<EOF
100644 $ONE_SHA1 0 me
EOF
-export GIT_DIR="$TRASH_DIRECTORY/.git"
-export GIT_WORK_TREE=/
+GIT_DIR="$TRASH_DIRECTORY/.git" && export GIT_DIR
+GIT_WORK_TREE=/ && export GIT_WORK_TREE
test_vars 'abs gitdir, root' "$GIT_DIR" "/" ""
test_foobar_root
@@ -154,24 +163,24 @@ 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=/
+GIT_DIR="$(echo $TRASH_DIRECTORY|sed 's,^/,,')/.git" && export GIT_DIR
+GIT_WORK_TREE=/ && 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=/
+GIT_DIR="../$TRASH_DIRECTORY/.git" && export GIT_DIR
+GIT_WORK_TREE=/ && 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=/
+GIT_DIR="../../$TRASH_DIRECTORY/.git" && export GIT_DIR
+GIT_WORK_TREE=/ && export GIT_WORK_TREE
test_vars 'rel gitdir, foo/bar' "$TRASH_DIRECTORY/.git" "/" "foo/bar/"
test_foobar_foobar
@@ -180,24 +189,24 @@ 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=.
+GIT_DIR="$(echo $TRASH_DIRECTORY|sed 's,^/,,')/.git" && export GIT_DIR
+GIT_WORK_TREE=. && 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=..
+GIT_DIR="../$TRASH_DIRECTORY/.git" && export GIT_DIR
+GIT_WORK_TREE=.. && 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=../..
+GIT_DIR="../../$TRASH_DIRECTORY/.git" && export GIT_DIR
+GIT_WORK_TREE=../.. && export GIT_WORK_TREE
test_vars 'rel gitdir, foo/bar' "$TRASH_DIRECTORY/.git" "/" "foo/bar/"
test_foobar_foobar
@@ -209,7 +218,7 @@ unset GIT_WORK_TREE
test_expect_success 'go to /' 'cd /'
test_expect_success 'setup' '
- rm -rf /.git
+ rm -rf /.git &&
echo "Initialized empty Git repository in /.git/" > expected &&
git init > result &&
test_cmp expected result
@@ -232,8 +241,8 @@ say "auto bare gitdir"
# DESTROYYYYY!!!!!
test_expect_success 'setup' '
- rm -rf /refs /objects /info /hooks
- rm /*
+ rm -rf /refs /objects /info /hooks &&
+ rm -f /expected /ls.expected /me /result &&
cd / &&
echo "Initialized empty Git repository in /" > expected &&
git init --bare > result &&
diff --git a/t/t1509/prepare-chroot.sh b/t/t1509/prepare-chroot.sh
index c5334a8..6d47e2c 100755
--- a/t/t1509/prepare-chroot.sh
+++ b/t/t1509/prepare-chroot.sh
@@ -14,25 +14,45 @@ xmkdir() {
R="$1"
-[ -n "$R" ] || die "Usage: prepare-chroot.sh <root>"
+[ "$(id -u)" -eq 0 ] && die "This script should not be run as root, what if it does rm -rf /?"
+[ -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"
+if [ -x /bin/busybox ]; then
+ BB=/bin/busybox
+elif [ -x /usr/bin/busybox ]; then
+ BB=/usr/bin/busybox
+else
+ die "You need busybox"
+fi
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"
+touch "$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"
+[ -x "$R$BB" ] || cp $BB "$R/bin/busybox"
+for cmd in sh su ls expr tr basename rm mkdir mv id uname dirname cat true sed diff; do
+ ln -f -s /bin/busybox "$R/bin/$cmd"
+done
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"
+# Fake perl to reduce dependency, t1509 does not use perl, but some
+# env might slip through, see test-lib.sh, unset.*PERL_PATH
+sed 's|^PERL_PATH=.*|PERL_PATH=/bin/true|' GIT-BUILD-OPTIONS > "$R$(pwd)/GIT-BUILD-OPTIONS"
+for cmd in git $BB;do
+ ldd $cmd | grep '/' | sed 's,.*\s\(/[^ ]*\).*,\1,' | while read i; do
+ mkdir -p "$R$(dirname $i)"
+ cp "$i" "$R/$i"
+ done
done
-echo "Execute this in root: 'chroot $R /bin/su - $(id -nu)'"
+cat <<EOF
+All is set up in $R, execute t1509 with the following commands:
+
+sudo chroot $R /bin/su - $(id -nu)
+IKNOWWHATIAMDOING=YES ./t1509-root-worktree.sh -v -i
+
+When you are done, simply delete $R to clean up
+EOF
diff --git a/t/t1510-repo-setup.sh b/t/t1510-repo-setup.sh
new file mode 100755
index 0000000..13ae12d
--- /dev/null
+++ b/t/t1510-repo-setup.sh
@@ -0,0 +1,802 @@
+#!/bin/sh
+
+test_description="Tests of cwd/prefix/worktree/gitdir setup in all cases
+
+A few rules for repo setup:
+
+1. GIT_DIR is relative to user's cwd. --git-dir is equivalent to
+ GIT_DIR.
+
+2. .git file is relative to parent directory. .git file is basically
+ symlink in disguise. The directory where .git file points to will
+ become new git_dir.
+
+3. core.worktree is relative to git_dir.
+
+4. GIT_WORK_TREE is relative to user's cwd. --work-tree is
+ equivalent to GIT_WORK_TREE.
+
+5. GIT_WORK_TREE/core.worktree was originally meant to work only if
+ GIT_DIR is set, but earlier git didn't enforce it, and some scripts
+ depend on the implementation that happened to first discover .git by
+ going up from the users $cwd and then using the specified working tree
+ that may or may not have any relation to where .git was found in. This
+ historical behaviour must be kept.
+
+6. Effective GIT_WORK_TREE overrides core.worktree and core.bare
+
+7. Effective core.worktree conflicts with core.bare
+
+8. If GIT_DIR is set but neither worktree nor bare setting is given,
+ original cwd becomes worktree.
+
+9. If .git discovery is done inside a repo, the repo becomes a bare
+ repo. .git discovery is performed if GIT_DIR is not set.
+
+10. If no worktree is available, cwd remains unchanged, prefix is
+ NULL.
+
+11. When user's cwd is outside worktree, cwd remains unchanged,
+ prefix is NULL.
+"
+. ./test-lib.sh
+
+here=$(pwd)
+
+test_repo () {
+ (
+ cd "$1" &&
+ if test -n "$2"
+ then
+ GIT_DIR="$2" &&
+ export GIT_DIR
+ fi &&
+ if test -n "$3"
+ then
+ GIT_WORK_TREE="$3" &&
+ export GIT_WORK_TREE
+ fi &&
+ rm -f trace &&
+ GIT_TRACE_SETUP="$(pwd)/trace" git symbolic-ref HEAD >/dev/null &&
+ grep '^setup: ' trace >result &&
+ test_cmp expected result
+ )
+}
+
+maybe_config () {
+ file=$1 var=$2 value=$3 &&
+ if test "$value" != unset
+ then
+ git config --file="$file" "$var" "$value"
+ fi
+}
+
+setup_repo () {
+ name=$1 worktreecfg=$2 gitfile=$3 barecfg=$4 &&
+ sane_unset GIT_DIR GIT_WORK_TREE &&
+
+ git init "$name" &&
+ maybe_config "$name/.git/config" core.worktree "$worktreecfg" &&
+ maybe_config "$name/.git/config" core.bare "$barecfg" &&
+ mkdir -p "$name/sub/sub" &&
+
+ if test "${gitfile:+set}"
+ then
+ mv "$name/.git" "$name.git" &&
+ echo "gitdir: ../$name.git" >"$name/.git"
+ fi
+}
+
+maybe_set () {
+ var=$1 value=$2 &&
+ if test "$value" != unset
+ then
+ eval "$var=\$value" &&
+ export $var
+ fi
+}
+
+setup_env () {
+ worktreenv=$1 gitdirenv=$2 &&
+ sane_unset GIT_DIR GIT_WORK_TREE &&
+ maybe_set GIT_DIR "$gitdirenv" &&
+ maybe_set GIT_WORK_TREE "$worktreeenv"
+}
+
+expect () {
+ cat >"$1/expected" <<-EOF
+ setup: git_dir: $2
+ setup: git_common_dir: $2
+ setup: worktree: $3
+ setup: cwd: $4
+ setup: prefix: $5
+ EOF
+}
+
+try_case () {
+ name=$1 worktreeenv=$2 gitdirenv=$3 &&
+ setup_env "$worktreeenv" "$gitdirenv" &&
+ expect "$name" "$4" "$5" "$6" "$7" &&
+ test_repo "$name"
+}
+
+run_wt_tests () {
+ N=$1 gitfile=$2
+
+ absgit="$here/$N/.git"
+ dotgit=.git
+ dotdotgit=../../.git
+
+ if test "$gitfile"
+ then
+ absgit="$here/$N.git"
+ dotgit=$absgit dotdotgit=$absgit
+ fi
+
+ test_expect_success "#$N: explicit GIT_WORK_TREE and GIT_DIR at toplevel" '
+ try_case $N "$here/$N" .git \
+ "$dotgit" "$here/$N" "$here/$N" "(null)" &&
+ try_case $N . .git \
+ "$dotgit" "$here/$N" "$here/$N" "(null)" &&
+ try_case $N "$here/$N" "$here/$N/.git" \
+ "$absgit" "$here/$N" "$here/$N" "(null)" &&
+ try_case $N . "$here/$N/.git" \
+ "$absgit" "$here/$N" "$here/$N" "(null)"
+ '
+
+ test_expect_success "#$N: explicit GIT_WORK_TREE and GIT_DIR in subdir" '
+ try_case $N/sub/sub "$here/$N" ../../.git \
+ "$absgit" "$here/$N" "$here/$N" sub/sub/ &&
+ try_case $N/sub/sub ../.. ../../.git \
+ "$absgit" "$here/$N" "$here/$N" sub/sub/ &&
+ try_case $N/sub/sub "$here/$N" "$here/$N/.git" \
+ "$absgit" "$here/$N" "$here/$N" sub/sub/ &&
+ try_case $N/sub/sub ../.. "$here/$N/.git" \
+ "$absgit" "$here/$N" "$here/$N" sub/sub/
+ '
+
+ test_expect_success "#$N: explicit GIT_WORK_TREE from parent of worktree" '
+ try_case $N "$here/$N/wt" .git \
+ "$dotgit" "$here/$N/wt" "$here/$N" "(null)" &&
+ try_case $N wt .git \
+ "$dotgit" "$here/$N/wt" "$here/$N" "(null)" &&
+ try_case $N wt "$here/$N/.git" \
+ "$absgit" "$here/$N/wt" "$here/$N" "(null)" &&
+ try_case $N "$here/$N/wt" "$here/$N/.git" \
+ "$absgit" "$here/$N/wt" "$here/$N" "(null)"
+ '
+
+ test_expect_success "#$N: explicit GIT_WORK_TREE from nephew of worktree" '
+ try_case $N/sub/sub "$here/$N/wt" ../../.git \
+ "$dotdotgit" "$here/$N/wt" "$here/$N/sub/sub" "(null)" &&
+ try_case $N/sub/sub ../../wt ../../.git \
+ "$dotdotgit" "$here/$N/wt" "$here/$N/sub/sub" "(null)" &&
+ try_case $N/sub/sub ../../wt "$here/$N/.git" \
+ "$absgit" "$here/$N/wt" "$here/$N/sub/sub" "(null)" &&
+ try_case $N/sub/sub "$here/$N/wt" "$here/$N/.git" \
+ "$absgit" "$here/$N/wt" "$here/$N/sub/sub" "(null)"
+ '
+
+ test_expect_success "#$N: chdir_to_toplevel uses worktree, not git dir" '
+ try_case $N "$here" .git \
+ "$absgit" "$here" "$here" $N/ &&
+ try_case $N .. .git \
+ "$absgit" "$here" "$here" $N/ &&
+ try_case $N .. "$here/$N/.git" \
+ "$absgit" "$here" "$here" $N/ &&
+ try_case $N "$here" "$here/$N/.git" \
+ "$absgit" "$here" "$here" $N/
+ '
+
+ test_expect_success "#$N: chdir_to_toplevel uses worktree (from subdir)" '
+ try_case $N/sub/sub "$here" ../../.git \
+ "$absgit" "$here" "$here" $N/sub/sub/ &&
+ try_case $N/sub/sub ../../.. ../../.git \
+ "$absgit" "$here" "$here" $N/sub/sub/ &&
+ try_case $N/sub/sub ../../../ "$here/$N/.git" \
+ "$absgit" "$here" "$here" $N/sub/sub/ &&
+ try_case $N/sub/sub "$here" "$here/$N/.git" \
+ "$absgit" "$here" "$here" $N/sub/sub/
+ '
+}
+
+# try_repo #c GIT_WORK_TREE GIT_DIR core.worktree .gitfile? core.bare \
+# (git dir) (work tree) (cwd) (prefix) \ <-- at toplevel
+# (git dir) (work tree) (cwd) (prefix) <-- from subdir
+try_repo () {
+ name=$1 worktreeenv=$2 gitdirenv=$3 &&
+ setup_repo "$name" "$4" "$5" "$6" &&
+ shift 6 &&
+ try_case "$name" "$worktreeenv" "$gitdirenv" \
+ "$1" "$2" "$3" "$4" &&
+ shift 4 &&
+ case "$gitdirenv" in
+ /* | ?:/* | unset) ;;
+ *)
+ gitdirenv=../$gitdirenv ;;
+ esac &&
+ try_case "$name/sub" "$worktreeenv" "$gitdirenv" \
+ "$1" "$2" "$3" "$4"
+}
+
+# Bit 0 = GIT_WORK_TREE
+# Bit 1 = GIT_DIR
+# Bit 2 = core.worktree
+# Bit 3 = .git is a file
+# Bit 4 = bare repo
+# Case# = encoding of the above 5 bits
+
+test_expect_success '#0: nonbare repo, no explicit configuration' '
+ try_repo 0 unset unset unset "" unset \
+ .git "$here/0" "$here/0" "(null)" \
+ .git "$here/0" "$here/0" sub/ 2>message &&
+ ! test -s message
+'
+
+test_expect_success '#1: GIT_WORK_TREE without explicit GIT_DIR is accepted' '
+ mkdir -p wt &&
+ try_repo 1 "$here" unset unset "" unset \
+ "$here/1/.git" "$here" "$here" 1/ \
+ "$here/1/.git" "$here" "$here" 1/sub/ 2>message &&
+ ! test -s message
+'
+
+test_expect_success '#2: worktree defaults to cwd with explicit GIT_DIR' '
+ try_repo 2 unset "$here/2/.git" unset "" unset \
+ "$here/2/.git" "$here/2" "$here/2" "(null)" \
+ "$here/2/.git" "$here/2/sub" "$here/2/sub" "(null)"
+'
+
+test_expect_success '#2b: relative GIT_DIR' '
+ try_repo 2b unset ".git" unset "" unset \
+ ".git" "$here/2b" "$here/2b" "(null)" \
+ "../.git" "$here/2b/sub" "$here/2b/sub" "(null)"
+'
+
+test_expect_success '#3: setup' '
+ setup_repo 3 unset "" unset &&
+ mkdir -p 3/sub/sub 3/wt/sub
+'
+run_wt_tests 3
+
+test_expect_success '#4: core.worktree without GIT_DIR set is accepted' '
+ setup_repo 4 ../sub "" unset &&
+ mkdir -p 4/sub sub &&
+ try_case 4 unset unset \
+ .git "$here/4/sub" "$here/4" "(null)" \
+ "$here/4/.git" "$here/4/sub" "$here/4/sub" "(null)" 2>message &&
+ ! test -s message
+'
+
+test_expect_success '#5: core.worktree + GIT_WORK_TREE is accepted' '
+ # or: you cannot intimidate away the lack of GIT_DIR setting
+ try_repo 5 "$here" unset "$here/5" "" unset \
+ "$here/5/.git" "$here" "$here" 5/ \
+ "$here/5/.git" "$here" "$here" 5/sub/ 2>message &&
+ try_repo 5a .. unset "$here/5a" "" unset \
+ "$here/5a/.git" "$here" "$here" 5a/ \
+ "$here/5a/.git" "$here/5a" "$here/5a" sub/ &&
+ ! test -s message
+'
+
+test_expect_success '#6: setting GIT_DIR brings core.worktree to life' '
+ setup_repo 6 "$here/6" "" unset &&
+ try_case 6 unset .git \
+ .git "$here/6" "$here/6" "(null)" &&
+ try_case 6 unset "$here/6/.git" \
+ "$here/6/.git" "$here/6" "$here/6" "(null)" &&
+ try_case 6/sub/sub unset ../../.git \
+ "$here/6/.git" "$here/6" "$here/6" sub/sub/ &&
+ try_case 6/sub/sub unset "$here/6/.git" \
+ "$here/6/.git" "$here/6" "$here/6" sub/sub/
+'
+
+test_expect_success '#6b: GIT_DIR set, core.worktree relative' '
+ setup_repo 6b .. "" unset &&
+ try_case 6b unset .git \
+ .git "$here/6b" "$here/6b" "(null)" &&
+ try_case 6b unset "$here/6b/.git" \
+ "$here/6b/.git" "$here/6b" "$here/6b" "(null)" &&
+ try_case 6b/sub/sub unset ../../.git \
+ "$here/6b/.git" "$here/6b" "$here/6b" sub/sub/ &&
+ try_case 6b/sub/sub unset "$here/6b/.git" \
+ "$here/6b/.git" "$here/6b" "$here/6b" sub/sub/
+'
+
+test_expect_success '#6c: GIT_DIR set, core.worktree=../wt (absolute)' '
+ setup_repo 6c "$here/6c/wt" "" unset &&
+ mkdir -p 6c/wt/sub &&
+
+ try_case 6c unset .git \
+ .git "$here/6c/wt" "$here/6c" "(null)" &&
+ try_case 6c unset "$here/6c/.git" \
+ "$here/6c/.git" "$here/6c/wt" "$here/6c" "(null)" &&
+ try_case 6c/sub/sub unset ../../.git \
+ ../../.git "$here/6c/wt" "$here/6c/sub/sub" "(null)" &&
+ try_case 6c/sub/sub unset "$here/6c/.git" \
+ "$here/6c/.git" "$here/6c/wt" "$here/6c/sub/sub" "(null)"
+'
+
+test_expect_success '#6d: GIT_DIR set, core.worktree=../wt (relative)' '
+ setup_repo 6d "$here/6d/wt" "" unset &&
+ mkdir -p 6d/wt/sub &&
+
+ try_case 6d unset .git \
+ .git "$here/6d/wt" "$here/6d" "(null)" &&
+ try_case 6d unset "$here/6d/.git" \
+ "$here/6d/.git" "$here/6d/wt" "$here/6d" "(null)" &&
+ try_case 6d/sub/sub unset ../../.git \
+ ../../.git "$here/6d/wt" "$here/6d/sub/sub" "(null)" &&
+ try_case 6d/sub/sub unset "$here/6d/.git" \
+ "$here/6d/.git" "$here/6d/wt" "$here/6d/sub/sub" "(null)"
+'
+
+test_expect_success '#6e: GIT_DIR set, core.worktree=../.. (absolute)' '
+ setup_repo 6e "$here" "" unset &&
+ try_case 6e unset .git \
+ "$here/6e/.git" "$here" "$here" 6e/ &&
+ try_case 6e unset "$here/6e/.git" \
+ "$here/6e/.git" "$here" "$here" 6e/ &&
+ try_case 6e/sub/sub unset ../../.git \
+ "$here/6e/.git" "$here" "$here" 6e/sub/sub/ &&
+ try_case 6e/sub/sub unset "$here/6e/.git" \
+ "$here/6e/.git" "$here" "$here" 6e/sub/sub/
+'
+
+test_expect_success '#6f: GIT_DIR set, core.worktree=../.. (relative)' '
+ setup_repo 6f ../../ "" unset &&
+ try_case 6f unset .git \
+ "$here/6f/.git" "$here" "$here" 6f/ &&
+ try_case 6f unset "$here/6f/.git" \
+ "$here/6f/.git" "$here" "$here" 6f/ &&
+ try_case 6f/sub/sub unset ../../.git \
+ "$here/6f/.git" "$here" "$here" 6f/sub/sub/ &&
+ try_case 6f/sub/sub unset "$here/6f/.git" \
+ "$here/6f/.git" "$here" "$here" 6f/sub/sub/
+'
+
+# case #7: GIT_WORK_TREE overrides core.worktree.
+test_expect_success '#7: setup' '
+ setup_repo 7 non-existent "" unset &&
+ mkdir -p 7/sub/sub 7/wt/sub
+'
+run_wt_tests 7
+
+test_expect_success '#8: gitfile, easy case' '
+ try_repo 8 unset unset unset gitfile unset \
+ "$here/8.git" "$here/8" "$here/8" "(null)" \
+ "$here/8.git" "$here/8" "$here/8" sub/
+'
+
+test_expect_success '#9: GIT_WORK_TREE accepted with gitfile' '
+ mkdir -p 9/wt &&
+ try_repo 9 wt unset unset gitfile unset \
+ "$here/9.git" "$here/9/wt" "$here/9" "(null)" \
+ "$here/9.git" "$here/9/sub/wt" "$here/9/sub" "(null)" 2>message &&
+ ! test -s message
+'
+
+test_expect_success '#10: GIT_DIR can point to gitfile' '
+ try_repo 10 unset "$here/10/.git" unset gitfile unset \
+ "$here/10.git" "$here/10" "$here/10" "(null)" \
+ "$here/10.git" "$here/10/sub" "$here/10/sub" "(null)"
+'
+
+test_expect_success '#10b: relative GIT_DIR can point to gitfile' '
+ try_repo 10b unset .git unset gitfile unset \
+ "$here/10b.git" "$here/10b" "$here/10b" "(null)" \
+ "$here/10b.git" "$here/10b/sub" "$here/10b/sub" "(null)"
+'
+
+# case #11: GIT_WORK_TREE works, gitfile case.
+test_expect_success '#11: setup' '
+ setup_repo 11 unset gitfile unset &&
+ mkdir -p 11/sub/sub 11/wt/sub
+'
+run_wt_tests 11 gitfile
+
+test_expect_success '#12: core.worktree with gitfile is accepted' '
+ try_repo 12 unset unset "$here/12" gitfile unset \
+ "$here/12.git" "$here/12" "$here/12" "(null)" \
+ "$here/12.git" "$here/12" "$here/12" sub/ 2>message &&
+ ! test -s message
+'
+
+test_expect_success '#13: core.worktree+GIT_WORK_TREE accepted (with gitfile)' '
+ # or: you cannot intimidate away the lack of GIT_DIR setting
+ try_repo 13 non-existent-too unset non-existent gitfile unset \
+ "$here/13.git" "$here/13/non-existent-too" "$here/13" "(null)" \
+ "$here/13.git" "$here/13/sub/non-existent-too" "$here/13/sub" "(null)" 2>message &&
+ ! test -s message
+'
+
+# case #14.
+# If this were more table-driven, it could share code with case #6.
+
+test_expect_success '#14: core.worktree with GIT_DIR pointing to gitfile' '
+ setup_repo 14 "$here/14" gitfile unset &&
+ try_case 14 unset .git \
+ "$here/14.git" "$here/14" "$here/14" "(null)" &&
+ try_case 14 unset "$here/14/.git" \
+ "$here/14.git" "$here/14" "$here/14" "(null)" &&
+ try_case 14/sub/sub unset ../../.git \
+ "$here/14.git" "$here/14" "$here/14" sub/sub/ &&
+ try_case 14/sub/sub unset "$here/14/.git" \
+ "$here/14.git" "$here/14" "$here/14" sub/sub/ &&
+
+ setup_repo 14c "$here/14c/wt" gitfile unset &&
+ mkdir -p 14c/wt/sub &&
+
+ try_case 14c unset .git \
+ "$here/14c.git" "$here/14c/wt" "$here/14c" "(null)" &&
+ try_case 14c unset "$here/14c/.git" \
+ "$here/14c.git" "$here/14c/wt" "$here/14c" "(null)" &&
+ try_case 14c/sub/sub unset ../../.git \
+ "$here/14c.git" "$here/14c/wt" "$here/14c/sub/sub" "(null)" &&
+ try_case 14c/sub/sub unset "$here/14c/.git" \
+ "$here/14c.git" "$here/14c/wt" "$here/14c/sub/sub" "(null)" &&
+
+ setup_repo 14d "$here/14d/wt" gitfile unset &&
+ mkdir -p 14d/wt/sub &&
+
+ try_case 14d unset .git \
+ "$here/14d.git" "$here/14d/wt" "$here/14d" "(null)" &&
+ try_case 14d unset "$here/14d/.git" \
+ "$here/14d.git" "$here/14d/wt" "$here/14d" "(null)" &&
+ try_case 14d/sub/sub unset ../../.git \
+ "$here/14d.git" "$here/14d/wt" "$here/14d/sub/sub" "(null)" &&
+ try_case 14d/sub/sub unset "$here/14d/.git" \
+ "$here/14d.git" "$here/14d/wt" "$here/14d/sub/sub" "(null)" &&
+
+ setup_repo 14e "$here" gitfile unset &&
+ try_case 14e unset .git \
+ "$here/14e.git" "$here" "$here" 14e/ &&
+ try_case 14e unset "$here/14e/.git" \
+ "$here/14e.git" "$here" "$here" 14e/ &&
+ try_case 14e/sub/sub unset ../../.git \
+ "$here/14e.git" "$here" "$here" 14e/sub/sub/ &&
+ try_case 14e/sub/sub unset "$here/14e/.git" \
+ "$here/14e.git" "$here" "$here" 14e/sub/sub/
+'
+
+test_expect_success '#14b: core.worktree is relative to actual git dir' '
+ setup_repo 14b ../14b gitfile unset &&
+ try_case 14b unset .git \
+ "$here/14b.git" "$here/14b" "$here/14b" "(null)" &&
+ try_case 14b unset "$here/14b/.git" \
+ "$here/14b.git" "$here/14b" "$here/14b" "(null)" &&
+ try_case 14b/sub/sub unset ../../.git \
+ "$here/14b.git" "$here/14b" "$here/14b" sub/sub/ &&
+ try_case 14b/sub/sub unset "$here/14b/.git" \
+ "$here/14b.git" "$here/14b" "$here/14b" sub/sub/ &&
+
+ setup_repo 14f ../ gitfile unset &&
+ try_case 14f unset .git \
+ "$here/14f.git" "$here" "$here" 14f/ &&
+ try_case 14f unset "$here/14f/.git" \
+ "$here/14f.git" "$here" "$here" 14f/ &&
+ try_case 14f/sub/sub unset ../../.git \
+ "$here/14f.git" "$here" "$here" 14f/sub/sub/ &&
+ try_case 14f/sub/sub unset "$here/14f/.git" \
+ "$here/14f.git" "$here" "$here" 14f/sub/sub/
+'
+
+# case #15: GIT_WORK_TREE overrides core.worktree (gitfile case).
+test_expect_success '#15: setup' '
+ setup_repo 15 non-existent gitfile unset &&
+ mkdir -p 15/sub/sub 15/wt/sub
+'
+run_wt_tests 15 gitfile
+
+test_expect_success '#16a: implicitly bare repo (cwd inside .git dir)' '
+ setup_repo 16a unset "" unset &&
+ mkdir -p 16a/.git/wt/sub &&
+
+ try_case 16a/.git unset unset \
+ . "(null)" "$here/16a/.git" "(null)" &&
+ try_case 16a/.git/wt unset unset \
+ "$here/16a/.git" "(null)" "$here/16a/.git/wt" "(null)" &&
+ try_case 16a/.git/wt/sub unset unset \
+ "$here/16a/.git" "(null)" "$here/16a/.git/wt/sub" "(null)"
+'
+
+test_expect_success '#16b: bare .git (cwd inside .git dir)' '
+ setup_repo 16b unset "" true &&
+ mkdir -p 16b/.git/wt/sub &&
+
+ try_case 16b/.git unset unset \
+ . "(null)" "$here/16b/.git" "(null)" &&
+ try_case 16b/.git/wt unset unset \
+ "$here/16b/.git" "(null)" "$here/16b/.git/wt" "(null)" &&
+ try_case 16b/.git/wt/sub unset unset \
+ "$here/16b/.git" "(null)" "$here/16b/.git/wt/sub" "(null)"
+'
+
+test_expect_success '#16c: bare .git has no worktree' '
+ try_repo 16c unset unset unset "" true \
+ .git "(null)" "$here/16c" "(null)" \
+ "$here/16c/.git" "(null)" "$here/16c/sub" "(null)"
+'
+
+test_expect_success '#16d: bareness preserved across alias' '
+ setup_repo 16d unset "" unset &&
+ (
+ cd 16d/.git &&
+ test_must_fail git status &&
+ git config alias.st status &&
+ test_must_fail git st
+ )
+'
+
+test_expect_success '#16e: bareness preserved by --bare' '
+ setup_repo 16e unset "" unset &&
+ (
+ cd 16e/.git &&
+ test_must_fail git status &&
+ test_must_fail git --bare status
+ )
+'
+
+test_expect_success '#17: GIT_WORK_TREE without explicit GIT_DIR is accepted (bare case)' '
+ # Just like #16.
+ setup_repo 17a unset "" true &&
+ setup_repo 17b unset "" true &&
+ mkdir -p 17a/.git/wt/sub &&
+ mkdir -p 17b/.git/wt/sub &&
+
+ try_case 17a/.git "$here/17a" unset \
+ "$here/17a/.git" "$here/17a" "$here/17a" .git/ \
+ 2>message &&
+ try_case 17a/.git/wt "$here/17a" unset \
+ "$here/17a/.git" "$here/17a" "$here/17a" .git/wt/ &&
+ try_case 17a/.git/wt/sub "$here/17a" unset \
+ "$here/17a/.git" "$here/17a" "$here/17a" .git/wt/sub/ &&
+
+ try_case 17b/.git "$here/17b" unset \
+ "$here/17b/.git" "$here/17b" "$here/17b" .git/ &&
+ try_case 17b/.git/wt "$here/17b" unset \
+ "$here/17b/.git" "$here/17b" "$here/17b" .git/wt/ &&
+ try_case 17b/.git/wt/sub "$here/17b" unset \
+ "$here/17b/.git" "$here/17b" "$here/17b" .git/wt/sub/ &&
+
+ try_repo 17c "$here/17c" unset unset "" true \
+ .git "$here/17c" "$here/17c" "(null)" \
+ "$here/17c/.git" "$here/17c" "$here/17c" sub/ 2>message &&
+ ! test -s message
+'
+
+test_expect_success '#18: bare .git named by GIT_DIR has no worktree' '
+ try_repo 18 unset .git unset "" true \
+ .git "(null)" "$here/18" "(null)" \
+ ../.git "(null)" "$here/18/sub" "(null)" &&
+ try_repo 18b unset "$here/18b/.git" unset "" true \
+ "$here/18b/.git" "(null)" "$here/18b" "(null)" \
+ "$here/18b/.git" "(null)" "$here/18b/sub" "(null)"
+'
+
+# Case #19: GIT_DIR + GIT_WORK_TREE suppresses bareness.
+test_expect_success '#19: setup' '
+ setup_repo 19 unset "" true &&
+ mkdir -p 19/sub/sub 19/wt/sub
+'
+run_wt_tests 19
+
+test_expect_success '#20a: core.worktree without GIT_DIR accepted (inside .git)' '
+ # Unlike case #16a.
+ setup_repo 20a "$here/20a" "" unset &&
+ mkdir -p 20a/.git/wt/sub &&
+ try_case 20a/.git unset unset \
+ "$here/20a/.git" "$here/20a" "$here/20a" .git/ 2>message &&
+ try_case 20a/.git/wt unset unset \
+ "$here/20a/.git" "$here/20a" "$here/20a" .git/wt/ &&
+ try_case 20a/.git/wt/sub unset unset \
+ "$here/20a/.git" "$here/20a" "$here/20a" .git/wt/sub/ &&
+ ! test -s message
+'
+
+test_expect_success '#20b/c: core.worktree and core.bare conflict' '
+ setup_repo 20b non-existent "" true &&
+ mkdir -p 20b/.git/wt/sub &&
+ (
+ cd 20b/.git &&
+ test_must_fail git status >/dev/null
+ ) 2>message &&
+ grep "core.bare and core.worktree" message
+'
+
+test_expect_success '#20d: core.worktree and core.bare OK when working tree not needed' '
+ setup_repo 20d non-existent "" true &&
+ mkdir -p 20d/.git/wt/sub &&
+ (
+ cd 20d/.git &&
+ git config foo.bar value
+ )
+'
+
+# Case #21: core.worktree/GIT_WORK_TREE overrides core.bare' '
+test_expect_success '#21: setup, core.worktree warns before overriding core.bare' '
+ setup_repo 21 non-existent "" unset &&
+ mkdir -p 21/.git/wt/sub &&
+ (
+ cd 21/.git &&
+ GIT_WORK_TREE="$here/21" &&
+ export GIT_WORK_TREE &&
+ git status >/dev/null
+ ) 2>message &&
+ ! test -s message
+
+'
+run_wt_tests 21
+
+test_expect_success '#22a: core.worktree = GIT_DIR = .git dir' '
+ # like case #6.
+
+ setup_repo 22a "$here/22a/.git" "" unset &&
+ setup_repo 22ab . "" unset &&
+ mkdir -p 22a/.git/sub 22a/sub &&
+ mkdir -p 22ab/.git/sub 22ab/sub &&
+ try_case 22a/.git unset . \
+ . "$here/22a/.git" "$here/22a/.git" "(null)" &&
+ try_case 22a/.git unset "$here/22a/.git" \
+ "$here/22a/.git" "$here/22a/.git" "$here/22a/.git" "(null)" &&
+ try_case 22a/.git/sub unset .. \
+ "$here/22a/.git" "$here/22a/.git" "$here/22a/.git" sub/ &&
+ try_case 22a/.git/sub unset "$here/22a/.git" \
+ "$here/22a/.git" "$here/22a/.git" "$here/22a/.git" sub/ &&
+
+ try_case 22ab/.git unset . \
+ . "$here/22ab/.git" "$here/22ab/.git" "(null)" &&
+ try_case 22ab/.git unset "$here/22ab/.git" \
+ "$here/22ab/.git" "$here/22ab/.git" "$here/22ab/.git" "(null)" &&
+ try_case 22ab/.git/sub unset .. \
+ "$here/22ab/.git" "$here/22ab/.git" "$here/22ab/.git" sub/ &&
+ try_case 22ab/.git unset "$here/22ab/.git" \
+ "$here/22ab/.git" "$here/22ab/.git" "$here/22ab/.git" "(null)"
+'
+
+test_expect_success '#22b: core.worktree child of .git, GIT_DIR=.git' '
+ setup_repo 22b "$here/22b/.git/wt" "" unset &&
+ setup_repo 22bb wt "" unset &&
+ mkdir -p 22b/.git/sub 22b/sub 22b/.git/wt/sub 22b/wt/sub &&
+ mkdir -p 22bb/.git/sub 22bb/sub 22bb/.git/wt 22bb/wt &&
+
+ try_case 22b/.git unset . \
+ . "$here/22b/.git/wt" "$here/22b/.git" "(null)" &&
+ try_case 22b/.git unset "$here/22b/.git" \
+ "$here/22b/.git" "$here/22b/.git/wt" "$here/22b/.git" "(null)" &&
+ try_case 22b/.git/sub unset .. \
+ .. "$here/22b/.git/wt" "$here/22b/.git/sub" "(null)" &&
+ try_case 22b/.git/sub unset "$here/22b/.git" \
+ "$here/22b/.git" "$here/22b/.git/wt" "$here/22b/.git/sub" "(null)" &&
+
+ try_case 22bb/.git unset . \
+ . "$here/22bb/.git/wt" "$here/22bb/.git" "(null)" &&
+ try_case 22bb/.git unset "$here/22bb/.git" \
+ "$here/22bb/.git" "$here/22bb/.git/wt" "$here/22bb/.git" "(null)" &&
+ try_case 22bb/.git/sub unset .. \
+ .. "$here/22bb/.git/wt" "$here/22bb/.git/sub" "(null)" &&
+ try_case 22bb/.git/sub unset "$here/22bb/.git" \
+ "$here/22bb/.git" "$here/22bb/.git/wt" "$here/22bb/.git/sub" "(null)"
+'
+
+test_expect_success '#22c: core.worktree = .git/.., GIT_DIR=.git' '
+ setup_repo 22c "$here/22c" "" unset &&
+ setup_repo 22cb .. "" unset &&
+ mkdir -p 22c/.git/sub 22c/sub &&
+ mkdir -p 22cb/.git/sub 22cb/sub &&
+
+ try_case 22c/.git unset . \
+ "$here/22c/.git" "$here/22c" "$here/22c" .git/ &&
+ try_case 22c/.git unset "$here/22c/.git" \
+ "$here/22c/.git" "$here/22c" "$here/22c" .git/ &&
+ try_case 22c/.git/sub unset .. \
+ "$here/22c/.git" "$here/22c" "$here/22c" .git/sub/ &&
+ try_case 22c/.git/sub unset "$here/22c/.git" \
+ "$here/22c/.git" "$here/22c" "$here/22c" .git/sub/ &&
+
+ try_case 22cb/.git unset . \
+ "$here/22cb/.git" "$here/22cb" "$here/22cb" .git/ &&
+ try_case 22cb/.git unset "$here/22cb/.git" \
+ "$here/22cb/.git" "$here/22cb" "$here/22cb" .git/ &&
+ try_case 22cb/.git/sub unset .. \
+ "$here/22cb/.git" "$here/22cb" "$here/22cb" .git/sub/ &&
+ try_case 22cb/.git/sub unset "$here/22cb/.git" \
+ "$here/22cb/.git" "$here/22cb" "$here/22cb" .git/sub/
+'
+
+test_expect_success '#22.2: core.worktree and core.bare conflict' '
+ setup_repo 22 "$here/22" "" true &&
+ (
+ cd 22/.git &&
+ GIT_DIR=. &&
+ export GIT_DIR &&
+ test_must_fail git status 2>result
+ ) &&
+ (
+ cd 22 &&
+ GIT_DIR=.git &&
+ export GIT_DIR &&
+ test_must_fail git status 2>result
+ ) &&
+ grep "core.bare and core.worktree" 22/.git/result &&
+ grep "core.bare and core.worktree" 22/result
+'
+
+# Case #23: GIT_DIR + GIT_WORK_TREE(+core.worktree) suppresses bareness.
+test_expect_success '#23: setup' '
+ setup_repo 23 non-existent "" true &&
+ mkdir -p 23/sub/sub 23/wt/sub
+'
+run_wt_tests 23
+
+test_expect_success '#24: bare repo has no worktree (gitfile case)' '
+ try_repo 24 unset unset unset gitfile true \
+ "$here/24.git" "(null)" "$here/24" "(null)" \
+ "$here/24.git" "(null)" "$here/24/sub" "(null)"
+'
+
+test_expect_success '#25: GIT_WORK_TREE accepted if GIT_DIR unset (bare gitfile case)' '
+ try_repo 25 "$here/25" unset unset gitfile true \
+ "$here/25.git" "$here/25" "$here/25" "(null)" \
+ "$here/25.git" "$here/25" "$here/25" "sub/" 2>message &&
+ ! test -s message
+'
+
+test_expect_success '#26: bare repo has no worktree (GIT_DIR -> gitfile case)' '
+ try_repo 26 unset "$here/26/.git" unset gitfile true \
+ "$here/26.git" "(null)" "$here/26" "(null)" \
+ "$here/26.git" "(null)" "$here/26/sub" "(null)" &&
+ try_repo 26b unset .git unset gitfile true \
+ "$here/26b.git" "(null)" "$here/26b" "(null)" \
+ "$here/26b.git" "(null)" "$here/26b/sub" "(null)"
+'
+
+# Case #27: GIT_DIR + GIT_WORK_TREE suppresses bareness (with gitfile).
+test_expect_success '#27: setup' '
+ setup_repo 27 unset gitfile true &&
+ mkdir -p 27/sub/sub 27/wt/sub
+'
+run_wt_tests 27 gitfile
+
+test_expect_success '#28: core.worktree and core.bare conflict (gitfile case)' '
+ setup_repo 28 "$here/28" gitfile true &&
+ (
+ cd 28 &&
+ test_must_fail git status
+ ) 2>message &&
+ grep "core.bare and core.worktree" message
+'
+
+# Case #29: GIT_WORK_TREE(+core.worktree) overrides core.bare (gitfile case).
+test_expect_success '#29: setup' '
+ setup_repo 29 non-existent gitfile true &&
+ mkdir -p 29/sub/sub 29/wt/sub &&
+ (
+ cd 29 &&
+ GIT_WORK_TREE="$here/29" &&
+ export GIT_WORK_TREE &&
+ git status
+ ) 2>message &&
+ ! test -s message
+'
+run_wt_tests 29 gitfile
+
+test_expect_success '#30: core.worktree and core.bare conflict (gitfile version)' '
+ # Just like case #22.
+ setup_repo 30 "$here/30" gitfile true &&
+ (
+ cd 30 &&
+ test_must_fail env GIT_DIR=.git git status 2>result
+ ) &&
+ grep "core.bare and core.worktree" 30/result
+'
+
+# Case #31: GIT_DIR + GIT_WORK_TREE(+core.worktree) suppresses
+# bareness (gitfile version).
+test_expect_success '#31: setup' '
+ setup_repo 31 non-existent gitfile true &&
+ mkdir -p 31/sub/sub 31/wt/sub
+'
+run_wt_tests 31 gitfile
+
+test_done
diff --git a/t/t1511-rev-parse-caret.sh b/t/t1511-rev-parse-caret.sh
new file mode 100755
index 0000000..e0a49a6
--- /dev/null
+++ b/t/t1511-rev-parse-caret.sh
@@ -0,0 +1,131 @@
+#!/bin/sh
+
+test_description='tests for ref^{stuff}'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo blob >a-blob &&
+ git tag -a -m blob blob-tag $(git hash-object -w a-blob) &&
+ mkdir a-tree &&
+ echo moreblobs >a-tree/another-blob &&
+ git add . &&
+ TREE_SHA1=$(git write-tree) &&
+ git tag -a -m tree tree-tag "$TREE_SHA1" &&
+ git commit -m Initial &&
+ git tag -a -m commit commit-tag &&
+ git branch ref &&
+ git checkout master &&
+ echo modified >>a-blob &&
+ git add -u &&
+ git commit -m Modified &&
+ git branch modref &&
+ echo changed! >>a-blob &&
+ git add -u &&
+ git commit -m !Exp &&
+ git branch expref &&
+ echo changed >>a-blob &&
+ git add -u &&
+ git commit -m Changed &&
+ echo changed-again >>a-blob &&
+ git add -u &&
+ git commit -m Changed-again
+'
+
+test_expect_success 'ref^{non-existent}' '
+ test_must_fail git rev-parse ref^{non-existent}
+'
+
+test_expect_success 'ref^{}' '
+ git rev-parse ref >expected &&
+ git rev-parse ref^{} >actual &&
+ test_cmp expected actual &&
+ git rev-parse commit-tag^{} >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'ref^{commit}' '
+ git rev-parse ref >expected &&
+ git rev-parse ref^{commit} >actual &&
+ test_cmp expected actual &&
+ git rev-parse commit-tag^{commit} >actual &&
+ test_cmp expected actual &&
+ test_must_fail git rev-parse tree-tag^{commit} &&
+ test_must_fail git rev-parse blob-tag^{commit}
+'
+
+test_expect_success 'ref^{tree}' '
+ echo $TREE_SHA1 >expected &&
+ git rev-parse ref^{tree} >actual &&
+ test_cmp expected actual &&
+ git rev-parse commit-tag^{tree} >actual &&
+ test_cmp expected actual &&
+ git rev-parse tree-tag^{tree} >actual &&
+ test_cmp expected actual &&
+ test_must_fail git rev-parse blob-tag^{tree}
+'
+
+test_expect_success 'ref^{tag}' '
+ test_must_fail git rev-parse HEAD^{tag} &&
+ git rev-parse commit-tag >expected &&
+ git rev-parse commit-tag^{tag} >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'ref^{/.}' '
+ git rev-parse master >expected &&
+ git rev-parse master^{/.} >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'ref^{/non-existent}' '
+ test_must_fail git rev-parse master^{/non-existent}
+'
+
+test_expect_success 'ref^{/Initial}' '
+ git rev-parse ref >expected &&
+ git rev-parse master^{/Initial} >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'ref^{/!Exp}' '
+ test_must_fail git rev-parse master^{/!Exp}
+'
+
+test_expect_success 'ref^{/!}' '
+ test_must_fail git rev-parse master^{/!}
+'
+
+test_expect_success 'ref^{/!!Exp}' '
+ git rev-parse expref >expected &&
+ git rev-parse master^{/!!Exp} >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'ref^{/!-}' '
+ test_must_fail git rev-parse master^{/!-}
+'
+
+test_expect_success 'ref^{/!-.}' '
+ test_must_fail git rev-parse master^{/!-.}
+'
+
+test_expect_success 'ref^{/!-non-existent}' '
+ git rev-parse master >expected &&
+ git rev-parse master^{/!-non-existent} >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'ref^{/!-Changed}' '
+ git rev-parse expref >expected &&
+ git rev-parse master^{/!-Changed} >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'ref^{/!-!Exp}' '
+ git rev-parse modref >expected &&
+ git rev-parse expref^{/!-!Exp} >actual &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh
new file mode 100755
index 0000000..e221167
--- /dev/null
+++ b/t/t1512-rev-parse-disambiguation.sh
@@ -0,0 +1,294 @@
+#!/bin/sh
+
+test_description='object name disambiguation
+
+Create blobs, trees, commits and a tag that all share the same
+prefix, and make sure "git rev-parse" can take advantage of
+type information to disambiguate short object names that are
+not necessarily unique.
+
+The final history used in the test has five commits, with the bottom
+one tagged as v1.0.0. They all have one regular file each.
+
+ +-------------------------------------------+
+ | |
+ | .-------b3wettvi---- ad2uee |
+ | / / |
+ | a2onsxbvj---czy8f73t--ioiley5o |
+ | |
+ +-------------------------------------------+
+
+'
+
+. ./test-lib.sh
+
+test_expect_success 'blob and tree' '
+ test_tick &&
+ (
+ for i in 0 1 2 3 4 5 6 7 8 9
+ do
+ echo $i
+ done
+ echo
+ echo b1rwzyc3
+ ) >a0blgqsjc &&
+
+ # create one blob 0000000000b36
+ git add a0blgqsjc &&
+
+ # create one tree 0000000000cdc
+ git write-tree
+'
+
+test_expect_success 'warn ambiguity when no candidate matches type hint' '
+ test_must_fail git rev-parse --verify 000000000^{commit} 2>actual &&
+ grep "short SHA1 000000000 is ambiguous" actual
+'
+
+test_expect_success 'disambiguate tree-ish' '
+ # feed tree-ish in an unambiguous way
+ git rev-parse --verify 0000000000cdc:a0blgqsjc &&
+
+ # ambiguous at the object name level, but there is only one
+ # such tree-ish (the other is a blob)
+ git rev-parse --verify 000000000:a0blgqsjc
+'
+
+test_expect_success 'disambiguate blob' '
+ sed -e "s/|$//" >patch <<-EOF &&
+ diff --git a/frotz b/frotz
+ index 000000000..ffffff 100644
+ --- a/frotz
+ +++ b/frotz
+ @@ -10,3 +10,4 @@
+ 9
+ |
+ b1rwzyc3
+ +irwry
+ EOF
+ (
+ GIT_INDEX_FILE=frotz &&
+ export GIT_INDEX_FILE &&
+ git apply --build-fake-ancestor frotz patch &&
+ git cat-file blob :frotz >actual
+ ) &&
+ test_cmp a0blgqsjc actual
+'
+
+test_expect_success 'disambiguate tree' '
+ commit=$(echo "d7xm" | git commit-tree 000000000) &&
+ # this commit is fffff2e and not ambiguous with the 00000* objects
+ test $(git rev-parse $commit^{tree}) = $(git rev-parse 0000000000cdc)
+'
+
+test_expect_success 'first commit' '
+ # create one commit 0000000000e4f
+ git commit -m a2onsxbvj
+'
+
+test_expect_success 'disambiguate commit-ish' '
+ # feed commit-ish in an unambiguous way
+ git rev-parse --verify 0000000000e4f^{commit} &&
+
+ # ambiguous at the object name level, but there is only one
+ # such commit (the others are tree and blob)
+ git rev-parse --verify 000000000^{commit} &&
+
+ # likewise
+ git rev-parse --verify 000000000^0
+'
+
+test_expect_success 'disambiguate commit' '
+ commit=$(echo "hoaxj" | git commit-tree 0000000000cdc -p 000000000) &&
+ # this commit is ffffffd8 and not ambiguous with the 00000* objects
+ test $(git rev-parse $commit^) = $(git rev-parse 0000000000e4f)
+'
+
+test_expect_success 'log name1..name2 takes only commit-ishes on both ends' '
+ # These are underspecified from the prefix-length point of view
+ # to disambiguate the commit with other objects, but there is only
+ # one commit that has 00000* prefix at this point.
+ git log 000000000..000000000 &&
+ git log ..000000000 &&
+ git log 000000000.. &&
+ git log 000000000...000000000 &&
+ git log ...000000000 &&
+ git log 000000000...
+'
+
+test_expect_success 'rev-parse name1..name2 takes only commit-ishes on both ends' '
+ # Likewise.
+ git rev-parse 000000000..000000000 &&
+ git rev-parse ..000000000 &&
+ git rev-parse 000000000..
+'
+
+test_expect_success 'git log takes only commit-ish' '
+ # Likewise.
+ git log 000000000
+'
+
+test_expect_success 'git reset takes only commit-ish' '
+ # Likewise.
+ git reset 000000000
+'
+
+test_expect_success 'first tag' '
+ # create one tag 0000000000f8f
+ git tag -a -m j7cp83um v1.0.0
+'
+
+test_expect_failure 'two semi-ambiguous commit-ish' '
+ # At this point, we have a tag 0000000000f8f that points
+ # at a commit 0000000000e4f, and a tree and a blob that
+ # share 0000000000 prefix with these tag and commit.
+ #
+ # Once the parser becomes ultra-smart, it could notice that
+ # 0000000000 before ^{commit} name many different objects, but
+ # that only two (HEAD and v1.0.0 tag) can be peeled to commit,
+ # and that peeling them down to commit yield the same commit
+ # without ambiguity.
+ git rev-parse --verify 0000000000^{commit} &&
+
+ # likewise
+ git log 0000000000..0000000000 &&
+ git log ..0000000000 &&
+ git log 0000000000.. &&
+ git log 0000000000...0000000000 &&
+ git log ...0000000000 &&
+ git log 0000000000...
+'
+
+test_expect_failure 'three semi-ambiguous tree-ish' '
+ # Likewise for tree-ish. HEAD, v1.0.0 and HEAD^{tree} share
+ # the prefix but peeling them to tree yields the same thing
+ git rev-parse --verify 0000000000^{tree}
+'
+
+test_expect_success 'parse describe name' '
+ # feed an unambiguous describe name
+ git rev-parse --verify v1.0.0-0-g0000000000e4f &&
+
+ # ambiguous at the object name level, but there is only one
+ # such commit (others are blob, tree and tag)
+ git rev-parse --verify v1.0.0-0-g000000000
+'
+
+test_expect_success 'more history' '
+ # commit 0000000000043
+ git mv a0blgqsjc d12cr3h8t &&
+ echo h62xsjeu >>d12cr3h8t &&
+ git add d12cr3h8t &&
+
+ test_tick &&
+ git commit -m czy8f73t &&
+
+ # commit 00000000008ec
+ git mv d12cr3h8t j000jmpzn &&
+ echo j08bekfvt >>j000jmpzn &&
+ git add j000jmpzn &&
+
+ test_tick &&
+ git commit -m ioiley5o &&
+
+ # commit 0000000005b0
+ git checkout v1.0.0^0 &&
+ git mv a0blgqsjc f5518nwu &&
+
+ for i in h62xsjeu j08bekfvt kg7xflhm
+ do
+ echo $i
+ done >>f5518nwu &&
+ git add f5518nwu &&
+
+ test_tick &&
+ git commit -m b3wettvi &&
+ side=$(git rev-parse HEAD) &&
+
+ # commit 000000000066
+ git checkout master &&
+
+ # If you use recursive, merge will fail and you will need to
+ # clean up a0blgqsjc as well. If you use resolve, merge will
+ # succeed.
+ test_might_fail git merge --no-commit -s recursive $side &&
+ git rm -f f5518nwu j000jmpzn &&
+
+ test_might_fail git rm -f a0blgqsjc &&
+ (
+ git cat-file blob $side:f5518nwu
+ echo j3l0i9s6
+ ) >ab2gs879 &&
+ git add ab2gs879 &&
+
+ test_tick &&
+ git commit -m ad2uee
+
+'
+
+test_expect_failure 'parse describe name taking advantage of generation' '
+ # ambiguous at the object name level, but there is only one
+ # such commit at generation 0
+ git rev-parse --verify v1.0.0-0-g000000000 &&
+
+ # likewise for generation 2 and 4
+ git rev-parse --verify v1.0.0-2-g000000000 &&
+ git rev-parse --verify v1.0.0-4-g000000000
+'
+
+# Note: because rev-parse does not even try to disambiguate based on
+# the generation number, this test currently succeeds for a wrong
+# reason. When it learns to use the generation number, the previous
+# test should succeed, and also this test should fail because the
+# describe name used in the test with generation number can name two
+# commits. Make sure that such a future enhancement does not randomly
+# pick one.
+test_expect_success 'parse describe name not ignoring ambiguity' '
+ # ambiguous at the object name level, and there are two such
+ # commits at generation 1
+ test_must_fail git rev-parse --verify v1.0.0-1-g000000000
+'
+
+test_expect_success 'ambiguous commit-ish' '
+ # Now there are many commits that begin with the
+ # common prefix, none of these should pick one at
+ # random. They all should result in ambiguity errors.
+ test_must_fail git rev-parse --verify 00000000^{commit} &&
+
+ # likewise
+ test_must_fail git log 000000000..000000000 &&
+ test_must_fail git log ..000000000 &&
+ test_must_fail git log 000000000.. &&
+ test_must_fail git log 000000000...000000000 &&
+ test_must_fail git log ...000000000 &&
+ test_must_fail git log 000000000...
+'
+
+test_expect_success 'rev-parse --disambiguate' '
+ # The test creates 16 objects that share the prefix and two
+ # commits created by commit-tree in earlier tests share a
+ # different prefix.
+ git rev-parse --disambiguate=000000000 >actual &&
+ test $(wc -l <actual) = 16 &&
+ test "$(sed -e "s/^\(.........\).*/\1/" actual | sort -u)" = 000000000
+'
+
+test_expect_success 'ambiguous 40-hex ref' '
+ TREE=$(git mktree </dev/null) &&
+ REF=$(git rev-parse HEAD) &&
+ VAL=$(git commit-tree $TREE </dev/null) &&
+ git update-ref refs/heads/$REF $VAL &&
+ test $(git rev-parse $REF 2>err) = $REF &&
+ grep "refname.*${REF}.*ambiguous" err
+'
+
+test_expect_success 'ambiguous short sha1 ref' '
+ TREE=$(git mktree </dev/null) &&
+ REF=$(git rev-parse --short HEAD) &&
+ VAL=$(git commit-tree $TREE </dev/null) &&
+ git update-ref refs/heads/$REF $VAL &&
+ test $(git rev-parse $REF 2>err) = $VAL &&
+ grep "refname.*${REF}.*ambiguous" err
+'
+
+test_done
diff --git a/t/t1513-rev-parse-prefix.sh b/t/t1513-rev-parse-prefix.sh
new file mode 100755
index 0000000..87ec3ae
--- /dev/null
+++ b/t/t1513-rev-parse-prefix.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+test_description='Tests for rev-parse --prefix'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ mkdir -p sub1/sub2 &&
+ echo top >top &&
+ echo file1 >sub1/file1 &&
+ echo file2 >sub1/sub2/file2 &&
+ git add top sub1/file1 sub1/sub2/file2 &&
+ git commit -m commit
+'
+
+test_expect_success 'empty prefix -- file' '
+ git rev-parse --prefix "" -- top sub1/file1 >actual &&
+ cat <<-\EOF >expected &&
+ --
+ top
+ sub1/file1
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'valid prefix -- file' '
+ git rev-parse --prefix sub1/ -- file1 sub2/file2 >actual &&
+ cat <<-\EOF >expected &&
+ --
+ sub1/file1
+ sub1/sub2/file2
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'valid prefix -- ../file' '
+ git rev-parse --prefix sub1/ -- ../top sub2/file2 >actual &&
+ cat <<-\EOF >expected &&
+ --
+ sub1/../top
+ sub1/sub2/file2
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'empty prefix HEAD:./path' '
+ git rev-parse --prefix "" HEAD:./top >actual &&
+ git rev-parse HEAD:top >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'valid prefix HEAD:./path' '
+ git rev-parse --prefix sub1/ HEAD:./file1 >actual &&
+ git rev-parse HEAD:sub1/file1 >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'valid prefix HEAD:../path' '
+ git rev-parse --prefix sub1/ HEAD:../top >actual &&
+ git rev-parse HEAD:top >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'prefix ignored with HEAD:top' '
+ git rev-parse --prefix sub1/ HEAD:top >actual &&
+ git rev-parse HEAD:top >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'disambiguate path with valid prefix' '
+ git rev-parse --prefix sub1/ file1 >actual &&
+ cat <<-\EOF >expected &&
+ sub1/file1
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'file and refs with prefix' '
+ git rev-parse --prefix sub1/ master file1 >actual &&
+ cat <<-EOF >expected &&
+ $(git rev-parse master)
+ sub1/file1
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'two-levels deep' '
+ git rev-parse --prefix sub1/sub2/ -- file2 >actual &&
+ cat <<-\EOF >expected &&
+ --
+ sub1/sub2/file2
+ EOF
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t1514-rev-parse-push.sh b/t/t1514-rev-parse-push.sh
new file mode 100755
index 0000000..7214f5b
--- /dev/null
+++ b/t/t1514-rev-parse-push.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+test_description='test <branch>@{push} syntax'
+. ./test-lib.sh
+
+resolve () {
+ echo "$2" >expect &&
+ git rev-parse --symbolic-full-name "$1" >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'setup' '
+ git init --bare parent.git &&
+ git init --bare other.git &&
+ git remote add origin parent.git &&
+ git remote add other other.git &&
+ test_commit base &&
+ git push origin HEAD &&
+ git branch --set-upstream-to=origin/master master &&
+ git branch --track topic origin/master &&
+ git push origin topic &&
+ git push other topic
+'
+
+test_expect_success '@{push} with default=nothing' '
+ test_config push.default nothing &&
+ test_must_fail git rev-parse master@{push}
+'
+
+test_expect_success '@{push} with default=simple' '
+ test_config push.default simple &&
+ resolve master@{push} refs/remotes/origin/master
+'
+
+test_expect_success 'triangular @{push} fails with default=simple' '
+ test_config push.default simple &&
+ test_must_fail git rev-parse topic@{push}
+'
+
+test_expect_success '@{push} with default=current' '
+ test_config push.default current &&
+ resolve topic@{push} refs/remotes/origin/topic
+'
+
+test_expect_success '@{push} with default=matching' '
+ test_config push.default matching &&
+ resolve topic@{push} refs/remotes/origin/topic
+'
+
+test_expect_success '@{push} with pushremote defined' '
+ test_config push.default current &&
+ test_config branch.topic.pushremote other &&
+ resolve topic@{push} refs/remotes/other/topic
+'
+
+test_expect_success '@{push} with push refspecs' '
+ test_config push.default nothing &&
+ test_config remote.origin.push refs/heads/*:refs/heads/magic/* &&
+ git push &&
+ resolve topic@{push} refs/remotes/origin/magic/topic
+'
+
+test_done
diff --git a/t/t1515-rev-parse-outside-repo.sh b/t/t1515-rev-parse-outside-repo.sh
new file mode 100755
index 0000000..3ec2971
--- /dev/null
+++ b/t/t1515-rev-parse-outside-repo.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='check that certain rev-parse options work outside repo'
+. ./test-lib.sh
+
+test_expect_success 'set up non-repo directory' '
+ GIT_CEILING_DIRECTORIES=$(pwd) &&
+ export GIT_CEILING_DIRECTORIES &&
+ mkdir non-repo &&
+ cd non-repo &&
+ # confirm that git does not find a repo
+ test_must_fail git rev-parse --git-dir
+'
+
+# Rather than directly test the output of sq-quote directly,
+# make sure the shell can read back a tricky case, since
+# that's what we really care about anyway.
+tricky="really tricky with \\ and \" and '"
+dump_args () {
+ for i in "$@"; do
+ echo "arg: $i"
+ done
+}
+test_expect_success 'rev-parse --sq-quote' '
+ dump_args "$tricky" easy >expect &&
+ eval "dump_args $(git rev-parse --sq-quote "$tricky" easy)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rev-parse --local-env-vars' '
+ git rev-parse --local-env-vars >actual &&
+ # we do not want to depend on the complete list here,
+ # so just look for something plausible
+ grep ^GIT_DIR actual
+'
+
+test_expect_success 'rev-parse --resolve-git-dir' '
+ git init --separate-git-dir repo dir &&
+ test_must_fail git rev-parse --resolve-git-dir . &&
+ echo "$(pwd)/repo" >expect &&
+ git rev-parse --resolve-git-dir dir/.git >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1600-index.sh b/t/t1600-index.sh
new file mode 100755
index 0000000..079d241
--- /dev/null
+++ b/t/t1600-index.sh
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+test_description='index file specific tests'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo 1 >a
+'
+
+test_expect_success 'bogus GIT_INDEX_VERSION issues warning' '
+ (
+ rm -f .git/index &&
+ GIT_INDEX_VERSION=2bogus &&
+ export GIT_INDEX_VERSION &&
+ git add a 2>&1 | sed "s/[0-9]//" >actual.err &&
+ sed -e "s/ Z$/ /" <<-\EOF >expect.err &&
+ warning: GIT_INDEX_VERSION set, but the value is invalid.
+ Using version Z
+ EOF
+ test_i18ncmp expect.err actual.err
+ )
+'
+
+test_expect_success 'out of bounds GIT_INDEX_VERSION issues warning' '
+ (
+ rm -f .git/index &&
+ GIT_INDEX_VERSION=1 &&
+ export GIT_INDEX_VERSION &&
+ git add a 2>&1 | sed "s/[0-9]//" >actual.err &&
+ sed -e "s/ Z$/ /" <<-\EOF >expect.err &&
+ warning: GIT_INDEX_VERSION set, but the value is invalid.
+ Using version Z
+ EOF
+ test_i18ncmp expect.err actual.err
+ )
+'
+
+test_expect_success 'no warning with bogus GIT_INDEX_VERSION and existing index' '
+ (
+ GIT_INDEX_VERSION=1 &&
+ export GIT_INDEX_VERSION &&
+ git add a 2>actual.err &&
+ >expect.err &&
+ test_i18ncmp expect.err actual.err
+ )
+'
+
+test_expect_success 'out of bounds index.version issues warning' '
+ (
+ sane_unset GIT_INDEX_VERSION &&
+ rm -f .git/index &&
+ git config --add index.version 1 &&
+ git add a 2>&1 | sed "s/[0-9]//" >actual.err &&
+ sed -e "s/ Z$/ /" <<-\EOF >expect.err &&
+ warning: index.version set, but the value is invalid.
+ Using version Z
+ EOF
+ test_i18ncmp expect.err actual.err
+ )
+'
+
+test_expect_success 'GIT_INDEX_VERSION takes precedence over config' '
+ (
+ rm -f .git/index &&
+ GIT_INDEX_VERSION=4 &&
+ export GIT_INDEX_VERSION &&
+ git config --add index.version 2 &&
+ git add a 2>&1 &&
+ echo 4 >expect &&
+ test-index-version <.git/index >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_done
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
new file mode 100755
index 0000000..8aef49f
--- /dev/null
+++ b/t/t1700-split-index.sh
@@ -0,0 +1,203 @@
+#!/bin/sh
+
+test_description='split index mode tests'
+
+. ./test-lib.sh
+
+# We need total control of index splitting here
+sane_unset GIT_TEST_SPLIT_INDEX
+
+test_expect_success 'enable split index' '
+ git update-index --split-index &&
+ test-dump-split-index .git/index >actual &&
+ indexversion=$(test-index-version <.git/index) &&
+ if test "$indexversion" = "4"
+ then
+ own=432ef4b63f32193984f339431fd50ca796493569
+ base=508851a7f0dfa8691e9f69c7f055865389012491
+ else
+ own=8299b0bcd1ac364e5f1d7768efb62fa2da79a339
+ base=39d890139ee5356c7ef572216cebcd27aa41f9df
+ fi &&
+ cat >expect <<EOF &&
+own $own
+base $base
+replacements:
+deletions:
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'add one file' '
+ : >one &&
+ git update-index --add one &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ cat >expect <<EOF &&
+base $base
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
+replacements:
+deletions:
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'disable split index' '
+ git update-index --no-split-index &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ BASE=$(test-dump-split-index .git/index | grep "^own" | sed "s/own/base/") &&
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ cat >expect <<EOF &&
+not a split index
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'enable split index again, "one" now belongs to base index"' '
+ git update-index --split-index &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ cat >expect <<EOF &&
+$BASE
+replacements:
+deletions:
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'modify original file, base index untouched' '
+ echo modified >one &&
+ git update-index one &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ q_to_tab >expect <<EOF &&
+$BASE
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+replacements: 0
+deletions:
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'add another file, which stays index' '
+ : >two &&
+ git update-index --add two &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ q_to_tab >expect <<EOF &&
+$BASE
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two
+replacements: 0
+deletions:
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'remove file not in base index' '
+ git update-index --force-remove two &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ q_to_tab >expect <<EOF &&
+$BASE
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+replacements: 0
+deletions:
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'remove file in base index' '
+ git update-index --force-remove one &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ cat >expect <<EOF &&
+$BASE
+replacements:
+deletions: 0
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'add original file back' '
+ : >one &&
+ git update-index --add one &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ cat >expect <<EOF &&
+$BASE
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
+replacements:
+deletions: 0
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'add new file' '
+ : >two &&
+ git update-index --add two &&
+ git ls-files --stage >actual &&
+ cat >expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'unify index, two files remain' '
+ git update-index --no-split-index &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ cat >expect <<EOF &&
+not a split index
+EOF
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t2001-checkout-cache-clash.sh b/t/t2001-checkout-cache-clash.sh
index 98aa73e..1fc8e63 100755
--- a/t/t2001-checkout-cache-clash.sh
+++ b/t/t2001-checkout-cache-clash.sh
@@ -59,10 +59,9 @@ test_expect_success \
'git read-tree -m $tree1 && git checkout-index -f -a'
test_debug 'show_files $tree1'
-test_expect_success SYMLINKS \
- 'git update-index --add a symlink.' \
- 'ln -s path0 path1 &&
- git update-index --add path1'
+test_expect_success \
+ 'add a symlink' \
+ 'test_ln_s_add path0 path1'
test_expect_success \
'writing tree out with git write-tree' \
'tree3=$(git write-tree)'
diff --git a/t/t2003-checkout-cache-mkdir.sh b/t/t2003-checkout-cache-mkdir.sh
index 02a4fc5..ff163cf 100755
--- a/t/t2003-checkout-cache-mkdir.sh
+++ b/t/t2003-checkout-cache-mkdir.sh
@@ -12,85 +12,108 @@ the GIT controlled paths.
. ./test-lib.sh
-test_expect_success \
- 'setup' \
- 'mkdir path1 &&
- echo frotz >path0 &&
- echo rezrov >path1/file1 &&
- git update-index --add path0 path1/file1'
+test_expect_success 'setup' '
+ mkdir path1 &&
+ echo frotz >path0 &&
+ echo rezrov >path1/file1 &&
+ git update-index --add path0 path1/file1
+'
+
+test_expect_success SYMLINKS 'have symlink in place where dir is expected.' '
+ rm -fr path0 path1 &&
+ mkdir path2 &&
+ ln -s path2 path1 &&
+ git checkout-index -f -a &&
+ test ! -h path1 && test -d path1 &&
+ test -f path1/file1 && test ! -f path2/file1
+'
-test_expect_success SYMLINKS \
- 'have symlink in place where dir is expected.' \
- 'rm -fr path0 path1 &&
- mkdir path2 &&
- ln -s path2 path1 &&
- git checkout-index -f -a &&
- test ! -h path1 && test -d path1 &&
- test -f path1/file1 && test ! -f path2/file1'
+test_expect_success 'use --prefix=path2/' '
+ rm -fr path0 path1 path2 &&
+ mkdir path2 &&
+ git checkout-index --prefix=path2/ -f -a &&
+ test -f path2/path0 &&
+ test -f path2/path1/file1 &&
+ test ! -f path0 &&
+ test ! -f path1/file1
+'
+
+test_expect_success 'use --prefix=tmp-' '
+ rm -fr path0 path1 path2 tmp* &&
+ git checkout-index --prefix=tmp- -f -a &&
+ test -f tmp-path0 &&
+ test -f tmp-path1/file1 &&
+ test ! -f path0 &&
+ test ! -f path1/file1
+'
-test_expect_success \
- 'use --prefix=path2/' \
- 'rm -fr path0 path1 path2 &&
- mkdir path2 &&
- git checkout-index --prefix=path2/ -f -a &&
- test -f path2/path0 &&
- test -f path2/path1/file1 &&
- test ! -f path0 &&
- test ! -f path1/file1'
+test_expect_success 'use --prefix=tmp- but with a conflicting file and dir' '
+ rm -fr path0 path1 path2 tmp* &&
+ echo nitfol >tmp-path1 &&
+ mkdir tmp-path0 &&
+ git checkout-index --prefix=tmp- -f -a &&
+ test -f tmp-path0 &&
+ test -f tmp-path1/file1 &&
+ test ! -f path0 &&
+ test ! -f path1/file1
+'
-test_expect_success \
- 'use --prefix=tmp-' \
- 'rm -fr path0 path1 path2 tmp* &&
- git checkout-index --prefix=tmp- -f -a &&
- test -f tmp-path0 &&
- test -f tmp-path1/file1 &&
- test ! -f path0 &&
- test ! -f path1/file1'
+test_expect_success SYMLINKS 'use --prefix=tmp/orary/ where tmp is a symlink' '
+ rm -fr path0 path1 path2 tmp* &&
+ mkdir tmp1 tmp1/orary &&
+ ln -s tmp1 tmp &&
+ git checkout-index --prefix=tmp/orary/ -f -a &&
+ test -d tmp1/orary &&
+ test -f tmp1/orary/path0 &&
+ test -f tmp1/orary/path1/file1 &&
+ test -h tmp
+'
-test_expect_success \
- 'use --prefix=tmp- but with a conflicting file and dir' \
- 'rm -fr path0 path1 path2 tmp* &&
- echo nitfol >tmp-path1 &&
- mkdir tmp-path0 &&
- git checkout-index --prefix=tmp- -f -a &&
- test -f tmp-path0 &&
- test -f tmp-path1/file1 &&
- test ! -f path0 &&
- test ! -f path1/file1'
+test_expect_success SYMLINKS 'use --prefix=tmp/orary- where tmp is a symlink' '
+ rm -fr path0 path1 path2 tmp* &&
+ mkdir tmp1 &&
+ ln -s tmp1 tmp &&
+ git checkout-index --prefix=tmp/orary- -f -a &&
+ test -f tmp1/orary-path0 &&
+ test -f tmp1/orary-path1/file1 &&
+ test -h tmp
+'
-# Linus fix #1
-test_expect_success SYMLINKS \
- 'use --prefix=tmp/orary/ where tmp is a symlink' \
- 'rm -fr path0 path1 path2 tmp* &&
- mkdir tmp1 tmp1/orary &&
- ln -s tmp1 tmp &&
- git checkout-index --prefix=tmp/orary/ -f -a &&
- test -d tmp1/orary &&
- test -f tmp1/orary/path0 &&
- test -f tmp1/orary/path1/file1 &&
- test -h tmp'
+test_expect_success SYMLINKS 'use --prefix=tmp- where tmp-path1 is a symlink' '
+ rm -fr path0 path1 path2 tmp* &&
+ mkdir tmp1 &&
+ ln -s tmp1 tmp-path1 &&
+ git checkout-index --prefix=tmp- -f -a &&
+ test -f tmp-path0 &&
+ test ! -h tmp-path1 &&
+ test -d tmp-path1 &&
+ test -f tmp-path1/file1
+'
-# Linus fix #2
-test_expect_success SYMLINKS \
- 'use --prefix=tmp/orary- where tmp is a symlink' \
- 'rm -fr path0 path1 path2 tmp* &&
- mkdir tmp1 &&
- ln -s tmp1 tmp &&
- git checkout-index --prefix=tmp/orary- -f -a &&
- test -f tmp1/orary-path0 &&
- test -f tmp1/orary-path1/file1 &&
- test -h tmp'
+test_expect_success 'apply filter from working tree .gitattributes with --prefix' '
+ rm -fr path0 path1 path2 tmp* &&
+ mkdir path1 &&
+ mkdir tmp &&
+ git config filter.replace-all.smudge "sed -e s/./,/g" &&
+ git config filter.replace-all.clean cat &&
+ git config filter.replace-all.required true &&
+ echo "file1 filter=replace-all" >path1/.gitattributes &&
+ git checkout-index --prefix=tmp/ -f -a &&
+ echo frotz >expected &&
+ test_cmp expected tmp/path0 &&
+ echo ,,,,,, >expected &&
+ test_cmp expected tmp/path1/file1
+'
-# Linus fix #3
-test_expect_success SYMLINKS \
- 'use --prefix=tmp- where tmp-path1 is a symlink' \
- 'rm -fr path0 path1 path2 tmp* &&
- mkdir tmp1 &&
- ln -s tmp1 tmp-path1 &&
- git checkout-index --prefix=tmp- -f -a &&
- test -f tmp-path0 &&
- test ! -h tmp-path1 &&
- test -d tmp-path1 &&
- test -f tmp-path1/file1'
+test_expect_success 'apply CRLF filter from working tree .gitattributes with --prefix' '
+ rm -fr path0 path1 path2 tmp* &&
+ mkdir path1 &&
+ mkdir tmp &&
+ echo "file1 eol=crlf" >path1/.gitattributes &&
+ git checkout-index --prefix=tmp/ -f -a &&
+ echo rezrovQ >expected &&
+ tr \\015 Q <tmp/path1/file1 >actual &&
+ test_cmp expected actual
+'
test_done
diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh
index 36cca14..a12afe9 100755
--- a/t/t2004-checkout-cache-temp.sh
+++ b/t/t2004-checkout-cache-temp.sh
@@ -10,203 +10,212 @@ 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_expect_success 'setup' '
+ 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_* actual .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_* actual .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_* actual .git/index &&
+ git read-tree $t1 &&
+ git checkout-index --temp -- path1 >actual &&
+ test_line_count = 1 actual &&
+ test $(cut "-d " -f2 actual) = path1 &&
+ p=$(cut "-d " -f1 actual) &&
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 $(cat $p) = tree1path1
+'
+
+test_expect_success 'checkout all stage 0 to temporary files' '
+ rm -f path* .merge_* actual .git/index &&
+ git read-tree $t1 &&
+ git checkout-index -a --temp >actual &&
+ test_line_count = 5 actual &&
+ for f in path0 path1 path3 path4 asubdir/path5
+ do
+ test $(grep $f actual | cut "-d " -f2) = $f &&
+ p=$(grep $f actual | cut "-d " -f1) &&
+ test -f $p &&
+ test $(cat $p) = tree1$f
+ done
+'
+
+test_expect_success 'setup 3-way merge' '
+ rm -f path* .merge_* actual .git/index &&
+ git read-tree -m $t1 $t2 $t3
+'
+
+test_expect_success 'checkout one stage 2 to temporary file' '
+ rm -f path* .merge_* actual &&
+ git checkout-index --stage=2 --temp -- path1 >actual &&
+ test_line_count = 1 actual &&
+ test $(cut "-d " -f2 actual) = path1 &&
+ p=$(cut "-d " -f1 actual) &&
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 $(cat $p) = tree2path1
+'
+
+test_expect_success 'checkout all stage 2 to temporary files' '
+ rm -f path* .merge_* actual &&
+ git checkout-index --all --stage=2 --temp >actual &&
+ test_line_count = 3 actual &&
+ for f in path1 path2 path4
+ do
+ test $(grep $f actual | cut "-d " -f2) = $f &&
+ p=$(grep $f actual | 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_* actual &&
+ git checkout-index --stage=all --temp -- path0 >actual &&
+ test_line_count = 0 actual
+'
+
+test_expect_success 'checkout all stages/one file to temporary files' '
+ rm -f path* .merge_* actual &&
+ git checkout-index --stage=all --temp -- path1 >actual &&
+ test_line_count = 1 actual &&
+ test $(cut "-d " -f2 actual) = path1 &&
+ cut "-d " -f1 actual | (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_* actual &&
+ git checkout-index --stage=all --temp -- path2 >actual &&
+ test_line_count = 1 actual &&
+ test $(cut "-d " -f2 actual) = path2 &&
+ cut "-d " -f1 actual | (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_* actual &&
+ git checkout-index -a --stage=all --temp >actual &&
+ test_line_count = 5 actual
+'
+
+test_expect_success '-- path0: no entry' '
+ test x$(grep path0 actual | cut "-d " -f2) = x
+'
+
+test_expect_success '-- path1: all 3 stages' '
+ test $(grep path1 actual | cut "-d " -f2) = path1 &&
+ grep path1 actual | 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 actual | cut "-d " -f2) = path2 &&
+ grep path2 actual | 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 actual | cut "-d " -f2) = path3 &&
+ grep path3 actual | 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 actual | cut "-d " -f2) = path4 &&
+ grep path4 actual | 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 actual | cut "-d " -f2) = asubdir/path5 &&
+ grep asubdir/path5 actual | 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 >actual &&
+ test_line_count = 1 actual &&
+ test $(grep path5 actual | cut "-d " -f2) = path5 &&
+ grep path5 actual | cut "-d " -f1 | (read s1 s2 s3 &&
+ test -f ../$s1 &&
+ test $s2 = . &&
+ test $s3 = . &&
+ test $(cat ../$s1) = tree1asubdir/path5)
+ )
+'
+
+test_expect_success 'checkout --temp symlink' '
+ rm -f path* .merge_* actual .git/index &&
+ test_ln_s_add path7 path6 &&
+ git checkout-index --temp -a >actual &&
+ test_line_count = 1 actual &&
+ test $(cut "-d " -f2 actual) = path6 &&
+ p=$(cut "-d " -f1 actual) &&
+ test -f $p &&
+ test $(cat $p) = path7
+'
+
+test_expect_success 'emit well-formed relative path' '
+ rm -f path* .merge_* actual .git/index &&
+ >path0123456789 &&
+ git update-index --add path0123456789 &&
+ (
+ cd asubdir &&
+ git checkout-index --temp -- ../path0123456789 >actual &&
+ test_line_count = 1 actual &&
+ test $(cut "-d " -f2 actual) = ../path0123456789
+ )
+'
test_done
diff --git a/t/t2006-checkout-index-basic.sh b/t/t2006-checkout-index-basic.sh
new file mode 100755
index 0000000..57cbdfe
--- /dev/null
+++ b/t/t2006-checkout-index-basic.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='basic checkout-index tests
+'
+
+. ./test-lib.sh
+
+test_expect_success 'checkout-index --gobbledegook' '
+ test_expect_code 129 git checkout-index --gobbledegook 2>err &&
+ test_i18ngrep "[Uu]sage" err
+'
+
+test_expect_success 'checkout-index -h in broken repository' '
+ mkdir broken &&
+ (
+ cd broken &&
+ git init &&
+ >.git/index &&
+ test_expect_code 129 git checkout-index -h >usage 2>&1
+ ) &&
+ test_i18ngrep "[Uu]sage" broken/usage
+'
+
+test_done
diff --git a/t/t2007-checkout-symlink.sh b/t/t2007-checkout-symlink.sh
index 05cc8fd..fc9aad5 100755
--- a/t/t2007-checkout-symlink.sh
+++ b/t/t2007-checkout-symlink.sh
@@ -6,12 +6,6 @@ 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 &&
@@ -23,7 +17,7 @@ test_expect_success setup '
git branch side &&
echo goodbye >nitfol &&
- git add nitfol
+ git add nitfol &&
test_tick &&
git commit -m "master adds file nitfol" &&
@@ -31,8 +25,8 @@ test_expect_success setup '
git rm --cached frotz/filfre &&
mv frotz xyzzy &&
- ln -s xyzzy frotz &&
- git add xyzzy/filfre frotz &&
+ test_ln_s_add xyzzy frotz &&
+ git add xyzzy/filfre &&
test_tick &&
git commit -m "side moves frotz/ to xyzzy/ and adds frotz->xyzzy/"
diff --git a/t/t2008-checkout-subdir.sh b/t/t2008-checkout-subdir.sh
index 3e098ab..eadb943 100755
--- a/t/t2008-checkout-subdir.sh
+++ b/t/t2008-checkout-subdir.sh
@@ -58,13 +58,13 @@ test_expect_success 'checkout with simple prefix' '
'
-# This is not expected to work as ls-files was not designed
-# to deal with such. Enable it when ls-files is updated.
-: test_expect_success 'checkout with complex relative path' '
-
- rm file1 &&
- git checkout HEAD -- ../dir1/../dir1/file1 && test -f ./file1
-
+test_expect_success 'checkout with complex relative path' '
+ (
+ cd dir1 &&
+ rm file1 &&
+ git checkout HEAD -- ../dir1/../dir1/file1 &&
+ test "hello" = "$(cat file1)"
+ )
'
test_expect_success 'relative path outside tree should fail' \
diff --git a/t/t2010-checkout-ambiguous.sh b/t/t2010-checkout-ambiguous.sh
index 7cc0a35..87bdf9c 100755
--- a/t/t2010-checkout-ambiguous.sh
+++ b/t/t2010-checkout-ambiguous.sh
@@ -47,4 +47,10 @@ test_expect_success 'disambiguate checking out from a tree-ish' '
git diff --exit-code --quiet
'
+test_expect_success 'accurate error message with more than one ref' '
+ test_must_fail git checkout HEAD master -- 2>actual &&
+ grep 2 actual &&
+ test_i18ngrep "one reference expected, 2 given" actual
+'
+
test_done
diff --git a/t/t2011-checkout-invalid-head.sh b/t/t2011-checkout-invalid-head.sh
index 15ebdc2..c5501b0 100755
--- a/t/t2011-checkout-invalid-head.sh
+++ b/t/t2011-checkout-invalid-head.sh
@@ -15,8 +15,47 @@ test_expect_success 'checkout should not start branch from a tree' '
'
test_expect_success 'checkout master from invalid HEAD' '
- echo 0000000000000000000000000000000000000000 >.git/HEAD &&
+ echo $_z40 >.git/HEAD &&
git checkout master --
'
+test_expect_success 'checkout notices failure to lock HEAD' '
+ test_when_finished "rm -f .git/HEAD.lock" &&
+ >.git/HEAD.lock &&
+ test_must_fail git checkout -b other
+'
+
+test_expect_success 'create ref directory/file conflict scenario' '
+ git update-ref refs/heads/outer/inner master &&
+
+ # do not rely on symbolic-ref to get a known state,
+ # as it may use the same code we are testing
+ reset_to_df () {
+ echo "ref: refs/heads/outer" >.git/HEAD
+ }
+'
+
+test_expect_success 'checkout away from d/f HEAD (unpacked, to branch)' '
+ reset_to_df &&
+ git checkout master
+'
+
+test_expect_success 'checkout away from d/f HEAD (unpacked, to detached)' '
+ reset_to_df &&
+ git checkout --detach master
+'
+
+test_expect_success 'pack refs' '
+ git pack-refs --all --prune
+'
+
+test_expect_success 'checkout away from d/f HEAD (packed, to branch)' '
+ reset_to_df &&
+ git checkout master
+'
+
+test_expect_success 'checkout away from d/f HEAD (packed, to detached)' '
+ reset_to_df &&
+ git checkout --detach master
+'
test_done
diff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh
index b44de9d..e7ba8c5 100755
--- a/t/t2012-checkout-last.sh
+++ b/t/t2012-checkout-last.sh
@@ -116,4 +116,38 @@ test_expect_success 'master...' '
test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
'
+test_expect_success '"checkout -" works after a rebase A' '
+ git checkout master &&
+ git checkout other &&
+ git rebase master &&
+ git checkout - &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" works after a rebase A B' '
+ git branch moodle master~1 &&
+ git checkout master &&
+ git checkout other &&
+ git rebase master moodle &&
+ git checkout - &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" works after a rebase -i A' '
+ git checkout master &&
+ git checkout other &&
+ git rebase -i master &&
+ git checkout - &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" works after a rebase -i A B' '
+ git branch foodle master~1 &&
+ git checkout master &&
+ git checkout other &&
+ git rebase master foodle &&
+ git checkout - &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
test_done
diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh
index fda3f0a..6847f75 100755
--- a/t/t2013-checkout-submodule.sh
+++ b/t/t2013-checkout-submodule.sh
@@ -3,6 +3,7 @@
test_description='checkout can handle submodules'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
test_expect_success 'setup' '
mkdir submodule &&
@@ -23,7 +24,7 @@ test_expect_success '"reset <submodule>" updates the index' '
git update-index --refresh &&
git diff-files --quiet &&
git diff-index --quiet --cached HEAD &&
- test_must_fail git reset HEAD^ submodule &&
+ git reset HEAD^ submodule &&
test_must_fail git diff-files --quiet &&
git reset submodule &&
git diff-files --quiet
@@ -39,4 +40,31 @@ test_expect_success '"checkout <submodule>" updates the index only' '
git diff-files --quiet
'
+test_expect_success '"checkout <submodule>" honors diff.ignoreSubmodules' '
+ git config diff.ignoreSubmodules dirty &&
+ echo x> submodule/untracked &&
+ git checkout HEAD >actual 2>&1 &&
+ ! test -s actual
+'
+
+test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .gitmodules' '
+ git config diff.ignoreSubmodules none &&
+ git config -f .gitmodules submodule.submodule.path submodule &&
+ git config -f .gitmodules submodule.submodule.ignore untracked &&
+ git checkout HEAD >actual 2>&1 &&
+ ! test -s actual
+'
+
+test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .git/config' '
+ git config -f .gitmodules submodule.submodule.ignore none &&
+ git config submodule.submodule.path submodule &&
+ git config submodule.submodule.ignore all &&
+ git checkout HEAD >actual 2>&1 &&
+ ! test -s actual
+'
+
+test_submodule_switch "git checkout"
+
+test_submodule_forced_switch "git checkout -f"
+
test_done
diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh
index c551d39..37bdced 100755
--- a/t/t2015-checkout-unborn.sh
+++ b/t/t2015-checkout-unborn.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-test_description='checkout from unborn branch protects contents'
+test_description='checkout from unborn branch'
. ./test-lib.sh
test_expect_success 'setup' '
@@ -37,4 +37,24 @@ test_expect_success 'checkout from unborn merges identical index contents' '
git checkout -b new origin
'
+test_expect_success 'checking out another branch from unborn state' '
+ git checkout --orphan newroot &&
+ git checkout -b anothername &&
+ test_must_fail git show-ref --verify refs/heads/newroot &&
+ git symbolic-ref HEAD >actual &&
+ echo refs/heads/anothername >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'checking out in a newly created repo' '
+ test_create_repo empty &&
+ (
+ cd empty &&
+ git symbolic-ref HEAD >expect &&
+ test_must_fail git checkout &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh
index 2144184..9cd0ac4 100755
--- a/t/t2016-checkout-patch.sh
+++ b/t/t2016-checkout-patch.sh
@@ -4,7 +4,7 @@ test_description='git checkout --patch'
. ./lib-patch-mode.sh
-test_expect_success 'setup' '
+test_expect_success PERL 'setup' '
mkdir dir &&
echo parent > dir/foo &&
echo dummy > bar &&
@@ -18,55 +18,55 @@ test_expect_success 'setup' '
# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
-test_expect_success 'saying "n" does nothing' '
+test_expect_success PERL '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' '
+test_expect_success PERL '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
+test_expect_success PERL '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' '
+test_expect_success PERL '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' '
+test_expect_success PERL '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
+test_expect_success PERL '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^' '
+test_expect_success PERL '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' '
+test_expect_success PERL 'git checkout -p handles deletion' '
set_state dir/foo work index &&
rm dir/foo &&
(echo n; echo y) | git checkout -p &&
@@ -79,28 +79,28 @@ test_expect_success 'git checkout -p handles deletion' '
# 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' '
+test_expect_success PERL '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' '
+test_expect_success PERL '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' '
+test_expect_success PERL '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' '
+test_expect_success PERL '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) &&
@@ -108,7 +108,7 @@ test_expect_success 'path limiting works: foo inside dir' '
verify_state dir/foo head head
'
-test_expect_success 'none of this moved HEAD' '
+test_expect_success PERL 'none of this moved HEAD' '
verify_saved_head
'
diff --git a/t/t2017-checkout-orphan.sh b/t/t2017-checkout-orphan.sh
index 2d2f63f..655f278 100755
--- a/t/t2017-checkout-orphan.sh
+++ b/t/t2017-checkout-orphan.sh
@@ -14,7 +14,7 @@ TEST_FILE=foo
test_expect_success 'Setup' '
echo "Initial" >"$TEST_FILE" &&
git add "$TEST_FILE" &&
- git commit -m "First Commit"
+ git commit -m "First Commit" &&
test_tick &&
echo "State 1" >>"$TEST_FILE" &&
git add "$TEST_FILE" &&
@@ -116,4 +116,10 @@ test_expect_success '--orphan refuses to switch if a merge is needed' '
git reset --hard
'
+test_expect_success 'cannot --detach on an unborn branch' '
+ git checkout master &&
+ git checkout --orphan new &&
+ test_must_fail git checkout --detach
+'
+
test_done
diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh
new file mode 100755
index 0000000..2741262
--- /dev/null
+++ b/t/t2018-checkout-branch.sh
@@ -0,0 +1,201 @@
+#!/bin/sh
+
+test_description='checkout '
+
+. ./test-lib.sh
+
+# Arguments: <branch> <sha> [<checkout options>]
+#
+# Runs "git checkout" to switch to <branch>, testing that
+#
+# 1) we are on the specified branch, <branch>;
+# 2) HEAD is <sha>; if <sha> is not specified, the old HEAD is used.
+#
+# If <checkout options> is not specified, "git checkout" is run with -b.
+do_checkout() {
+ exp_branch=$1 &&
+ exp_ref="refs/heads/$exp_branch" &&
+
+ # if <sha> is not specified, use HEAD.
+ exp_sha=${2:-$(git rev-parse --verify HEAD)} &&
+
+ # default options for git checkout: -b
+ if [ -z "$3" ]; then
+ opts="-b"
+ else
+ opts="$3"
+ fi
+
+ git checkout $opts $exp_branch $exp_sha &&
+
+ test $exp_ref = $(git rev-parse --symbolic-full-name HEAD) &&
+ test $exp_sha = $(git rev-parse --verify HEAD)
+}
+
+test_dirty_unmergeable() {
+ ! git diff --exit-code >/dev/null
+}
+
+setup_dirty_unmergeable() {
+ echo >>file1 change2
+}
+
+test_dirty_mergeable() {
+ ! git diff --cached --exit-code >/dev/null
+}
+
+setup_dirty_mergeable() {
+ echo >file2 file2 &&
+ git add file2
+}
+
+test_expect_success 'setup' '
+ test_commit initial file1 &&
+ HEAD1=$(git rev-parse --verify HEAD) &&
+
+ test_commit change1 file1 &&
+ HEAD2=$(git rev-parse --verify HEAD) &&
+
+ git branch -m branch1
+'
+
+test_expect_success 'checkout -b to a new branch, set to HEAD' '
+ do_checkout branch2
+'
+
+test_expect_success 'checkout -b to a new branch, set to an explicit ref' '
+ git checkout branch1 &&
+ git branch -D branch2 &&
+
+ do_checkout branch2 $HEAD1
+'
+
+test_expect_success 'checkout -b to a new branch with unmergeable changes fails' '
+ git checkout branch1 &&
+
+ # clean up from previous test
+ git branch -D branch2 &&
+
+ setup_dirty_unmergeable &&
+ test_must_fail do_checkout branch2 $HEAD1 &&
+ test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -f -b to a new branch with unmergeable changes discards changes' '
+ # still dirty and on branch1
+ do_checkout branch2 $HEAD1 "-f -b" &&
+ test_must_fail test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -b to a new branch preserves mergeable changes' '
+ git checkout branch1 &&
+
+ # clean up from previous test
+ git branch -D branch2 &&
+
+ setup_dirty_mergeable &&
+ do_checkout branch2 $HEAD1 &&
+ test_dirty_mergeable
+'
+
+test_expect_success 'checkout -f -b to a new branch with mergeable changes discards changes' '
+ # clean up from previous test
+ git reset --hard &&
+
+ git checkout branch1 &&
+
+ # clean up from previous test
+ git branch -D branch2 &&
+
+ setup_dirty_mergeable &&
+ do_checkout branch2 $HEAD1 "-f -b" &&
+ test_must_fail test_dirty_mergeable
+'
+
+test_expect_success 'checkout -b to an existing branch fails' '
+ git reset --hard HEAD &&
+
+ test_must_fail do_checkout branch2 $HEAD2
+'
+
+test_expect_success 'checkout -b to @{-1} fails with the right branch name' '
+ git reset --hard HEAD &&
+ git checkout branch1 &&
+ git checkout branch2 &&
+ echo >expect "fatal: A branch named '\''branch1'\'' already exists." &&
+ test_must_fail git checkout -b @{-1} 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'checkout -B to an existing branch resets branch to HEAD' '
+ git checkout branch1 &&
+
+ do_checkout branch2 "" -B
+'
+
+test_expect_success 'checkout -B to an existing branch from detached HEAD resets branch to HEAD' '
+ git checkout $(git rev-parse --verify HEAD) &&
+
+ do_checkout branch2 "" -B
+'
+
+test_expect_success 'checkout -B to an existing branch with an explicit ref resets branch to that ref' '
+ git checkout branch1 &&
+
+ do_checkout branch2 $HEAD1 -B
+'
+
+test_expect_success 'checkout -B to an existing branch with unmergeable changes fails' '
+ git checkout branch1 &&
+
+ setup_dirty_unmergeable &&
+ test_must_fail do_checkout branch2 $HEAD1 -B &&
+ test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -f -B to an existing branch with unmergeable changes discards changes' '
+ # still dirty and on branch1
+ do_checkout branch2 $HEAD1 "-f -B" &&
+ test_must_fail test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -B to an existing branch preserves mergeable changes' '
+ git checkout branch1 &&
+
+ setup_dirty_mergeable &&
+ do_checkout branch2 $HEAD1 -B &&
+ test_dirty_mergeable
+'
+
+test_expect_success 'checkout -f -B to an existing branch with mergeable changes discards changes' '
+ # clean up from previous test
+ git reset --hard &&
+
+ git checkout branch1 &&
+
+ setup_dirty_mergeable &&
+ do_checkout branch2 $HEAD1 "-f -B" &&
+ test_must_fail test_dirty_mergeable
+'
+
+test_expect_success 'checkout -b <describe>' '
+ git tag -f -m "First commit" initial initial &&
+ git checkout -f change1 &&
+ name=$(git describe) &&
+ git checkout -b $name &&
+ git diff --exit-code change1 &&
+ echo "refs/heads/$name" >expect &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'checkout -B to the current branch works' '
+ git checkout branch1 &&
+ git checkout -B branch1-scratch &&
+
+ setup_dirty_mergeable &&
+ git checkout -B branch1-scratch initial &&
+ test_dirty_mergeable
+'
+
+test_done
diff --git a/t/t2019-checkout-ambiguous-ref.sh b/t/t2019-checkout-ambiguous-ref.sh
new file mode 100755
index 0000000..b99d519
--- /dev/null
+++ b/t/t2019-checkout-ambiguous-ref.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+test_description='checkout handling of ambiguous (branch/tag) refs'
+. ./test-lib.sh
+
+test_expect_success 'setup ambiguous refs' '
+ test_commit branch file &&
+ git branch ambiguity &&
+ git branch vagueness &&
+ test_commit tag file &&
+ git tag ambiguity &&
+ git tag vagueness HEAD:file &&
+ test_commit other file
+'
+
+test_expect_success 'checkout ambiguous ref succeeds' '
+ git checkout ambiguity >stdout 2>stderr
+'
+
+test_expect_success 'checkout produces ambiguity warning' '
+ grep "warning.*ambiguous" stderr
+'
+
+test_expect_success 'checkout chooses branch over tag' '
+ echo refs/heads/ambiguity >expect &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual &&
+ echo branch >expect &&
+ test_cmp expect file
+'
+
+test_expect_success 'checkout reports switch to branch' '
+ test_i18ngrep "Switched to branch" stderr &&
+ test_i18ngrep ! "^HEAD is now at" stderr
+'
+
+test_expect_success 'checkout vague ref succeeds' '
+ git checkout vagueness >stdout 2>stderr &&
+ test_set_prereq VAGUENESS_SUCCESS
+'
+
+test_expect_success VAGUENESS_SUCCESS 'checkout produces ambiguity warning' '
+ grep "warning.*ambiguous" stderr
+'
+
+test_expect_success VAGUENESS_SUCCESS 'checkout chooses branch over tag' '
+ echo refs/heads/vagueness >expect &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual &&
+ echo branch >expect &&
+ test_cmp expect file
+'
+
+test_expect_success VAGUENESS_SUCCESS 'checkout reports switch to branch' '
+ test_i18ngrep "Switched to branch" stderr &&
+ test_i18ngrep ! "^HEAD is now at" stderr
+'
+
+test_done
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
new file mode 100755
index 0000000..5d68729
--- /dev/null
+++ b/t/t2020-checkout-detach.sh
@@ -0,0 +1,166 @@
+#!/bin/sh
+
+test_description='checkout into detached HEAD state'
+. ./test-lib.sh
+
+check_detached () {
+ test_must_fail git symbolic-ref -q HEAD >/dev/null
+}
+
+check_not_detached () {
+ git symbolic-ref -q HEAD >/dev/null
+}
+
+PREV_HEAD_DESC='Previous HEAD position was'
+check_orphan_warning() {
+ test_i18ngrep "you are leaving $2 behind" "$1" &&
+ test_i18ngrep ! "$PREV_HEAD_DESC" "$1"
+}
+check_no_orphan_warning() {
+ test_i18ngrep ! "you are leaving .* commit.*behind" "$1" &&
+ test_i18ngrep "$PREV_HEAD_DESC" "$1"
+}
+
+reset () {
+ git checkout master &&
+ check_not_detached
+}
+
+test_expect_success 'setup' '
+ test_commit one &&
+ test_commit two &&
+ test_commit three && git tag -d three &&
+ test_commit four && git tag -d four &&
+ git branch branch &&
+ git tag tag
+'
+
+test_expect_success 'checkout branch does not detach' '
+ reset &&
+ git checkout branch &&
+ check_not_detached
+'
+
+test_expect_success 'checkout tag detaches' '
+ reset &&
+ git checkout tag &&
+ check_detached
+'
+
+test_expect_success 'checkout branch by full name detaches' '
+ reset &&
+ git checkout refs/heads/branch &&
+ check_detached
+'
+
+test_expect_success 'checkout non-ref detaches' '
+ reset &&
+ git checkout branch^ &&
+ check_detached
+'
+
+test_expect_success 'checkout ref^0 detaches' '
+ reset &&
+ git checkout branch^0 &&
+ check_detached
+'
+
+test_expect_success 'checkout --detach detaches' '
+ reset &&
+ git checkout --detach branch &&
+ check_detached
+'
+
+test_expect_success 'checkout --detach without branch name' '
+ reset &&
+ git checkout --detach &&
+ check_detached
+'
+
+test_expect_success 'checkout --detach errors out for non-commit' '
+ reset &&
+ test_must_fail git checkout --detach one^{tree} &&
+ check_not_detached
+'
+
+test_expect_success 'checkout --detach errors out for extra argument' '
+ reset &&
+ git checkout master &&
+ test_must_fail git checkout --detach tag one.t &&
+ check_not_detached
+'
+
+test_expect_success 'checkout --detached and -b are incompatible' '
+ reset &&
+ test_must_fail git checkout --detach -b newbranch tag &&
+ check_not_detached
+'
+
+test_expect_success 'checkout --detach moves HEAD' '
+ reset &&
+ git checkout one &&
+ git checkout --detach two &&
+ git diff --exit-code HEAD &&
+ git diff --exit-code two
+'
+
+test_expect_success 'checkout warns on orphan commits' '
+ reset &&
+ git checkout --detach two &&
+ echo content >orphan &&
+ git add orphan &&
+ git commit -a -m orphan1 &&
+ echo new content >orphan &&
+ git commit -a -m orphan2 &&
+ orphan2=$(git rev-parse HEAD) &&
+ git checkout master 2>stderr
+'
+
+test_expect_success 'checkout warns on orphan commits: output' '
+ check_orphan_warning stderr "2 commits"
+'
+
+test_expect_success 'checkout warns orphaning 1 of 2 commits' '
+ git checkout "$orphan2" &&
+ git checkout HEAD^ 2>stderr
+'
+
+test_expect_success 'checkout warns orphaning 1 of 2 commits: output' '
+ check_orphan_warning stderr "1 commit"
+'
+
+test_expect_success 'checkout does not warn leaving ref tip' '
+ reset &&
+ git checkout --detach two &&
+ git checkout master 2>stderr
+'
+
+test_expect_success 'checkout does not warn leaving ref tip' '
+ check_no_orphan_warning stderr
+'
+
+test_expect_success 'checkout does not warn leaving reachable commit' '
+ reset &&
+ git checkout --detach HEAD^ &&
+ git checkout master 2>stderr
+'
+
+test_expect_success 'checkout does not warn leaving reachable commit' '
+ check_no_orphan_warning stderr
+'
+
+cat >expect <<'EOF'
+Your branch is behind 'master' by 1 commit, and can be fast-forwarded.
+ (use "git pull" to update your local branch)
+EOF
+test_expect_success 'tracking count is accurate after orphan check' '
+ reset &&
+ git branch child master^ &&
+ git config branch.child.remote . &&
+ git config branch.child.merge refs/heads/master &&
+ git checkout child^ &&
+ git checkout child >stdout &&
+ test_i18ncmp expect stdout
+'
+
+test_done
diff --git a/t/t2021-checkout-overwrite.sh b/t/t2021-checkout-overwrite.sh
new file mode 100755
index 0000000..c2ada7d
--- /dev/null
+++ b/t/t2021-checkout-overwrite.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+test_description='checkout must not overwrite an untracked objects'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+ mkdir -p a/b/c &&
+ >a/b/c/d &&
+ git add -A &&
+ git commit -m base &&
+ git tag start
+'
+
+test_expect_success 'create a commit where dir a/b changed to file' '
+
+ git checkout -b file &&
+ rm -rf a/b &&
+ >a/b &&
+ git add -A &&
+ git commit -m "dir to file"
+'
+
+test_expect_success 'checkout commit with dir must not remove untracked a/b' '
+
+ git rm --cached a/b &&
+ git commit -m "un-track the file" &&
+ test_must_fail git checkout start &&
+ test -f a/b
+'
+
+test_expect_success 'create a commit where dir a/b changed to symlink' '
+
+ rm -rf a/b && # cleanup if previous test failed
+ git checkout -f -b symlink start &&
+ rm -rf a/b &&
+ git add -A &&
+ test_ln_s_add foo a/b &&
+ git commit -m "dir to symlink"
+'
+
+test_expect_success 'checkout commit with dir must not remove untracked a/b' '
+
+ git rm --cached a/b &&
+ git commit -m "un-track the symlink" &&
+ test_must_fail git checkout start
+'
+
+test_expect_success SYMLINKS 'the symlink remained' '
+
+ test -h a/b
+'
+
+test_done
diff --git a/t/t2022-checkout-paths.sh b/t/t2022-checkout-paths.sh
new file mode 100755
index 0000000..f46d049
--- /dev/null
+++ b/t/t2022-checkout-paths.sh
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+test_description='checkout $tree -- $paths'
+. ./test-lib.sh
+
+test_expect_success setup '
+ mkdir dir &&
+ >dir/master &&
+ echo common >dir/common &&
+ git add dir/master dir/common &&
+ test_tick && git commit -m "master has dir/master" &&
+ git checkout -b next &&
+ git mv dir/master dir/next0 &&
+ echo next >dir/next1 &&
+ git add dir &&
+ test_tick && git commit -m "next has dir/next but not dir/master"
+'
+
+test_expect_success 'checking out paths out of a tree does not clobber unrelated paths' '
+ git checkout next &&
+ git reset --hard &&
+ rm dir/next0 &&
+ cat dir/common >expect.common &&
+ echo modified >expect.next1 &&
+ cat expect.next1 >dir/next1 &&
+ echo untracked >expect.next2 &&
+ cat expect.next2 >dir/next2 &&
+
+ git checkout master dir &&
+
+ test_cmp expect.common dir/common &&
+ test_path_is_file dir/master &&
+ git diff --exit-code master dir/master &&
+
+ test_path_is_missing dir/next0 &&
+ test_cmp expect.next1 dir/next1 &&
+ test_path_is_file dir/next2 &&
+ test_must_fail git ls-files --error-unmatch dir/next2 &&
+ test_cmp expect.next2 dir/next2
+'
+
+test_expect_success 'do not touch unmerged entries matching $path but not in $tree' '
+ git checkout next &&
+ git reset --hard &&
+
+ cat dir/common >expect.common &&
+ EMPTY_SHA1=$(git hash-object -w --stdin </dev/null) &&
+ git rm dir/next0 &&
+ cat >expect.next0 <<-EOF &&
+ 100644 $EMPTY_SHA1 1 dir/next0
+ 100644 $EMPTY_SHA1 2 dir/next0
+ EOF
+ git update-index --index-info <expect.next0 &&
+
+ git checkout master dir &&
+
+ test_cmp expect.common dir/common &&
+ test_path_is_file dir/master &&
+ git diff --exit-code master dir/master &&
+ git ls-files -s dir/next0 >actual.next0 &&
+ test_cmp expect.next0 actual.next0
+'
+
+test_expect_success 'do not touch files that are already up-to-date' '
+ git reset --hard &&
+ echo one >file1 &&
+ echo two >file2 &&
+ git add file1 file2 &&
+ git commit -m base &&
+ echo modified >file1 &&
+ test-chmtime =1000000000 file2 &&
+ git update-index -q --refresh &&
+ git checkout HEAD -- file1 file2 &&
+ echo one >expect &&
+ test_cmp expect file1 &&
+ echo "1000000000 file2" >expect &&
+ test-chmtime -v +0 file2 >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t2023-checkout-m.sh b/t/t2023-checkout-m.sh
new file mode 100755
index 0000000..7e18985
--- /dev/null
+++ b/t/t2023-checkout-m.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='checkout -m -- <conflicted path>
+
+Ensures that checkout -m on a resolved file restores the conflicted file'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_tick &&
+ test_commit both.txt both.txt initial &&
+ git branch topic &&
+ test_commit modified_in_master both.txt in_master &&
+ test_commit added_in_master each.txt in_master &&
+ git checkout topic &&
+ test_commit modified_in_topic both.txt in_topic &&
+ test_commit added_in_topic each.txt in_topic
+'
+
+test_expect_success 'git merge master' '
+ test_must_fail git merge master
+'
+
+clean_branchnames () {
+ # Remove branch names after conflict lines
+ sed 's/^\([<>]\{5,\}\) .*$/\1/'
+}
+
+test_expect_success '-m restores 2-way conflicted+resolved file' '
+ cp each.txt each.txt.conflicted &&
+ echo resolved >each.txt &&
+ git add each.txt &&
+ git checkout -m -- each.txt &&
+ clean_branchnames <each.txt >each.txt.cleaned &&
+ clean_branchnames <each.txt.conflicted >each.txt.conflicted.cleaned &&
+ test_cmp each.txt.conflicted.cleaned each.txt.cleaned
+'
+
+test_expect_success '-m restores 3-way conflicted+resolved file' '
+ cp both.txt both.txt.conflicted &&
+ echo resolved >both.txt &&
+ git add both.txt &&
+ git checkout -m -- both.txt &&
+ clean_branchnames <both.txt >both.txt.cleaned &&
+ clean_branchnames <both.txt.conflicted >both.txt.conflicted.cleaned &&
+ test_cmp both.txt.conflicted.cleaned both.txt.cleaned
+'
+
+test_done
diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh
new file mode 100755
index 0000000..468a000
--- /dev/null
+++ b/t/t2024-checkout-dwim.sh
@@ -0,0 +1,206 @@
+#!/bin/sh
+
+test_description='checkout <branch>
+
+Ensures that checkout on an unborn branch does what the user expects'
+
+. ./test-lib.sh
+
+# Is the current branch "refs/heads/$1"?
+test_branch () {
+ printf "%s\n" "refs/heads/$1" >expect.HEAD &&
+ git symbolic-ref HEAD >actual.HEAD &&
+ test_cmp expect.HEAD actual.HEAD
+}
+
+# Is branch "refs/heads/$1" set to pull from "$2/$3"?
+test_branch_upstream () {
+ printf "%s\n" "$2" "refs/heads/$3" >expect.upstream &&
+ {
+ git config "branch.$1.remote" &&
+ git config "branch.$1.merge"
+ } >actual.upstream &&
+ test_cmp expect.upstream actual.upstream
+}
+
+test_expect_success 'setup' '
+ test_commit my_master &&
+ git init repo_a &&
+ (
+ cd repo_a &&
+ test_commit a_master &&
+ git checkout -b foo &&
+ test_commit a_foo &&
+ git checkout -b bar &&
+ test_commit a_bar
+ ) &&
+ git init repo_b &&
+ (
+ cd repo_b &&
+ test_commit b_master &&
+ git checkout -b foo &&
+ test_commit b_foo &&
+ git checkout -b baz &&
+ test_commit b_baz
+ ) &&
+ git remote add repo_a repo_a &&
+ git remote add repo_b repo_b &&
+ git config remote.repo_b.fetch \
+ "+refs/heads/*:refs/remotes/other_b/*" &&
+ git fetch --all
+'
+
+test_expect_success 'checkout of non-existing branch fails' '
+ git checkout -B master &&
+ test_might_fail git branch -D xyzzy &&
+
+ test_must_fail git checkout xyzzy &&
+ test_must_fail git rev-parse --verify refs/heads/xyzzy &&
+ test_branch master
+'
+
+test_expect_success 'checkout of branch from multiple remotes fails #1' '
+ git checkout -B master &&
+ test_might_fail git branch -D foo &&
+
+ test_must_fail git checkout foo &&
+ test_must_fail git rev-parse --verify refs/heads/foo &&
+ test_branch master
+'
+
+test_expect_success 'checkout of branch from a single remote succeeds #1' '
+ git checkout -B master &&
+ test_might_fail git branch -D bar &&
+
+ git checkout bar &&
+ test_branch bar &&
+ test_cmp_rev remotes/repo_a/bar HEAD &&
+ test_branch_upstream bar repo_a bar
+'
+
+test_expect_success 'checkout of branch from a single remote succeeds #2' '
+ git checkout -B master &&
+ test_might_fail git branch -D baz &&
+
+ git checkout baz &&
+ test_branch baz &&
+ test_cmp_rev remotes/other_b/baz HEAD &&
+ test_branch_upstream baz repo_b baz
+'
+
+test_expect_success '--no-guess suppresses branch auto-vivification' '
+ git checkout -B master &&
+ test_might_fail git branch -D bar &&
+
+ test_must_fail git checkout --no-guess bar &&
+ test_must_fail git rev-parse --verify refs/heads/bar &&
+ test_branch master
+'
+
+test_expect_success 'setup more remotes with unconventional refspecs' '
+ git checkout -B master &&
+ git init repo_c &&
+ (
+ cd repo_c &&
+ test_commit c_master &&
+ git checkout -b bar &&
+ test_commit c_bar &&
+ git checkout -b spam &&
+ test_commit c_spam
+ ) &&
+ git init repo_d &&
+ (
+ cd repo_d &&
+ test_commit d_master &&
+ git checkout -b baz &&
+ test_commit d_baz &&
+ git checkout -b eggs &&
+ test_commit d_eggs
+ ) &&
+ git remote add repo_c repo_c &&
+ git config remote.repo_c.fetch \
+ "+refs/heads/*:refs/remotes/extra_dir/repo_c/extra_dir/*" &&
+ git remote add repo_d repo_d &&
+ git config remote.repo_d.fetch \
+ "+refs/heads/*:refs/repo_d/*" &&
+ git fetch --all
+'
+
+test_expect_success 'checkout of branch from multiple remotes fails #2' '
+ git checkout -B master &&
+ test_might_fail git branch -D bar &&
+
+ test_must_fail git checkout bar &&
+ test_must_fail git rev-parse --verify refs/heads/bar &&
+ test_branch master
+'
+
+test_expect_success 'checkout of branch from multiple remotes fails #3' '
+ git checkout -B master &&
+ test_might_fail git branch -D baz &&
+
+ test_must_fail git checkout baz &&
+ test_must_fail git rev-parse --verify refs/heads/baz &&
+ test_branch master
+'
+
+test_expect_success 'checkout of branch from a single remote succeeds #3' '
+ git checkout -B master &&
+ test_might_fail git branch -D spam &&
+
+ git checkout spam &&
+ test_branch spam &&
+ test_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&
+ test_branch_upstream spam repo_c spam
+'
+
+test_expect_success 'checkout of branch from a single remote succeeds #4' '
+ git checkout -B master &&
+ test_might_fail git branch -D eggs &&
+
+ git checkout eggs &&
+ test_branch eggs &&
+ test_cmp_rev refs/repo_d/eggs HEAD &&
+ test_branch_upstream eggs repo_d eggs
+'
+
+test_expect_success 'checkout of branch with a file having the same name fails' '
+ git checkout -B master &&
+ test_might_fail git branch -D spam &&
+
+ >spam &&
+ test_must_fail git checkout spam &&
+ test_must_fail git rev-parse --verify refs/heads/spam &&
+ test_branch master
+'
+
+test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' '
+ git checkout -B master &&
+ test_might_fail git branch -D spam &&
+
+ >spam &&
+ git checkout spam -- &&
+ test_branch spam &&
+ test_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&
+ test_branch_upstream spam repo_c spam
+'
+
+test_expect_success 'loosely defined local base branch is reported correctly' '
+
+ git checkout master &&
+ git branch strict &&
+ git branch loose &&
+ git commit --allow-empty -m "a bit more" &&
+
+ test_config branch.strict.remote . &&
+ test_config branch.loose.remote . &&
+ test_config branch.strict.merge refs/heads/master &&
+ test_config branch.loose.merge master &&
+
+ git checkout strict | sed -e "s/strict/BRANCHNAME/g" >expect &&
+ git checkout loose | sed -e "s/loose/BRANCHNAME/g" >actual &&
+
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
new file mode 100755
index 0000000..3a22fc5
--- /dev/null
+++ b/t/t2025-worktree-add.sh
@@ -0,0 +1,287 @@
+#!/bin/sh
+
+test_description='test git worktree add'
+
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+test_expect_success 'setup' '
+ test_commit init
+'
+
+test_expect_success '"add" an existing worktree' '
+ mkdir -p existing/subtree &&
+ test_must_fail git worktree add --detach existing master
+'
+
+test_expect_success '"add" an existing empty worktree' '
+ mkdir existing_empty &&
+ git worktree add --detach existing_empty master
+'
+
+test_expect_success '"add" refuses to checkout locked branch' '
+ test_must_fail git worktree add zere master &&
+ ! test -d zere &&
+ ! test -d .git/worktrees/zere
+'
+
+test_expect_success 'checking out paths not complaining about linked checkouts' '
+ (
+ cd existing_empty &&
+ echo dirty >>init.t &&
+ git checkout master -- init.t
+ )
+'
+
+test_expect_success '"add" worktree' '
+ git rev-parse HEAD >expect &&
+ git worktree add --detach here master &&
+ (
+ cd here &&
+ test_cmp ../init.t init.t &&
+ test_must_fail git symbolic-ref HEAD &&
+ git rev-parse HEAD >actual &&
+ test_cmp ../expect actual &&
+ git fsck
+ )
+'
+
+test_expect_success '"add" worktree from a subdir' '
+ (
+ mkdir sub &&
+ cd sub &&
+ git worktree add --detach here master &&
+ cd here &&
+ test_cmp ../../init.t init.t
+ )
+'
+
+test_expect_success '"add" from a linked checkout' '
+ (
+ cd here &&
+ git worktree add --detach nested-here master &&
+ cd nested-here &&
+ git fsck
+ )
+'
+
+test_expect_success '"add" worktree creating new branch' '
+ git worktree add -b newmaster there master &&
+ (
+ cd there &&
+ test_cmp ../init.t init.t &&
+ git symbolic-ref HEAD >actual &&
+ echo refs/heads/newmaster >expect &&
+ test_cmp expect actual &&
+ git fsck
+ )
+'
+
+test_expect_success 'die the same branch is already checked out' '
+ (
+ cd here &&
+ test_must_fail git checkout newmaster
+ )
+'
+
+test_expect_success SYMLINKS 'die the same branch is already checked out (symlink)' '
+ head=$(git -C there rev-parse --git-path HEAD) &&
+ ref=$(git -C there symbolic-ref HEAD) &&
+ rm "$head" &&
+ ln -s "$ref" "$head" &&
+ test_must_fail git -C here checkout newmaster
+'
+
+test_expect_success 'not die the same branch is already checked out' '
+ (
+ cd here &&
+ git worktree add --force anothernewmaster newmaster
+ )
+'
+
+test_expect_success 'not die on re-checking out current branch' '
+ (
+ cd there &&
+ git checkout newmaster
+ )
+'
+
+test_expect_success '"add" from a bare repo' '
+ (
+ git clone --bare . bare &&
+ cd bare &&
+ git worktree add -b bare-master ../there2 master
+ )
+'
+
+test_expect_success 'checkout from a bare repo without "add"' '
+ (
+ cd bare &&
+ test_must_fail git checkout master
+ )
+'
+
+test_expect_success 'checkout with grafts' '
+ test_when_finished rm .git/info/grafts &&
+ test_commit abc &&
+ SHA1=$(git rev-parse HEAD) &&
+ test_commit def &&
+ test_commit xyz &&
+ echo "$(git rev-parse HEAD) $SHA1" >.git/info/grafts &&
+ cat >expected <<-\EOF &&
+ xyz
+ abc
+ EOF
+ git log --format=%s -2 >actual &&
+ test_cmp expected actual &&
+ git worktree add --detach grafted master &&
+ git --git-dir=grafted/.git log --format=%s -2 >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '"add" from relative HEAD' '
+ test_commit a &&
+ test_commit b &&
+ test_commit c &&
+ git rev-parse HEAD~1 >expected &&
+ git worktree add relhead HEAD~1 &&
+ git -C relhead rev-parse HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '"add -b" with <branch> omitted' '
+ git worktree add -b burble flornk &&
+ test_cmp_rev HEAD burble
+'
+
+test_expect_success '"add --detach" with <branch> omitted' '
+ git worktree add --detach fishhook &&
+ git rev-parse HEAD >expected &&
+ git -C fishhook rev-parse HEAD >actual &&
+ test_cmp expected actual &&
+ test_must_fail git -C fishhook symbolic-ref HEAD
+'
+
+test_expect_success '"add" with <branch> omitted' '
+ git worktree add wiffle/bat &&
+ test_cmp_rev HEAD bat
+'
+
+test_expect_success '"add" auto-vivify does not clobber existing branch' '
+ test_commit c1 &&
+ test_commit c2 &&
+ git branch precious HEAD~1 &&
+ test_must_fail git worktree add precious &&
+ test_cmp_rev HEAD~1 precious &&
+ test_path_is_missing precious
+'
+
+test_expect_success '"add" no auto-vivify with --detach and <branch> omitted' '
+ git worktree add --detach mish/mash &&
+ test_must_fail git rev-parse mash -- &&
+ test_must_fail git -C mish/mash symbolic-ref HEAD
+'
+
+test_expect_success '"add" -b/-B mutually exclusive' '
+ test_must_fail git worktree add -b poodle -B poodle bamboo master
+'
+
+test_expect_success '"add" -b/--detach mutually exclusive' '
+ test_must_fail git worktree add -b poodle --detach bamboo master
+'
+
+test_expect_success '"add" -B/--detach mutually exclusive' '
+ test_must_fail git worktree add -B poodle --detach bamboo master
+'
+
+test_expect_success '"add -B" fails if the branch is checked out' '
+ git rev-parse newmaster >before &&
+ test_must_fail git worktree add -B newmaster bamboo master &&
+ git rev-parse newmaster >after &&
+ test_cmp before after
+'
+
+test_expect_success 'add -B' '
+ git worktree add -B poodle bamboo2 master^ &&
+ git -C bamboo2 symbolic-ref HEAD >actual &&
+ echo refs/heads/poodle >expected &&
+ test_cmp expected actual &&
+ test_cmp_rev master^ poodle
+'
+
+test_expect_success 'local clone from linked checkout' '
+ git clone --local here here-clone &&
+ ( cd here-clone && git fsck )
+'
+
+test_expect_success '"add" worktree with --no-checkout' '
+ git worktree add --no-checkout -b swamp swamp &&
+ ! test -e swamp/init.t &&
+ git -C swamp reset --hard &&
+ test_cmp init.t swamp/init.t
+'
+
+test_expect_success '"add" worktree with --checkout' '
+ git worktree add --checkout -b swmap2 swamp2 &&
+ test_cmp init.t swamp2/init.t
+'
+
+test_expect_success 'put a worktree under rebase' '
+ git worktree add under-rebase &&
+ (
+ cd under-rebase &&
+ set_fake_editor &&
+ FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+ git worktree list | grep "under-rebase.*detached HEAD"
+ )
+'
+
+test_expect_success 'add a worktree, checking out a rebased branch' '
+ test_must_fail git worktree add new-rebase under-rebase &&
+ ! test -d new-rebase
+'
+
+test_expect_success 'checking out a rebased branch from another worktree' '
+ git worktree add new-place &&
+ test_must_fail git -C new-place checkout under-rebase
+'
+
+test_expect_success 'not allow to delete a branch under rebase' '
+ (
+ cd under-rebase &&
+ test_must_fail git branch -D under-rebase
+ )
+'
+
+test_expect_success 'rename a branch under rebase not allowed' '
+ test_must_fail git branch -M under-rebase rebase-with-new-name
+'
+
+test_expect_success 'check out from current worktree branch ok' '
+ (
+ cd under-rebase &&
+ git checkout under-rebase &&
+ git checkout - &&
+ git rebase --abort
+ )
+'
+
+test_expect_success 'checkout a branch under bisect' '
+ git worktree add under-bisect &&
+ (
+ cd under-bisect &&
+ git bisect start &&
+ git bisect bad &&
+ git bisect good HEAD~2 &&
+ git worktree list | grep "under-bisect.*detached HEAD" &&
+ test_must_fail git worktree add new-bisect under-bisect &&
+ ! test -d new-bisect
+ )
+'
+
+test_expect_success 'rename a branch under bisect not allowed' '
+ test_must_fail git branch -M under-bisect bisect-with-new-name
+'
+
+test_done
diff --git a/t/t2026-worktree-prune.sh b/t/t2026-worktree-prune.sh
new file mode 100755
index 0000000..a0f1e3b
--- /dev/null
+++ b/t/t2026-worktree-prune.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+test_description='prune $GIT_DIR/worktrees'
+
+. ./test-lib.sh
+
+test_expect_success initialize '
+ git commit --allow-empty -m init
+'
+
+test_expect_success 'worktree prune on normal repo' '
+ git worktree prune &&
+ test_must_fail git worktree prune abc
+'
+
+test_expect_success 'prune files inside $GIT_DIR/worktrees' '
+ mkdir .git/worktrees &&
+ : >.git/worktrees/abc &&
+ git worktree prune --verbose >actual &&
+ cat >expect <<EOF &&
+Removing worktrees/abc: not a valid directory
+EOF
+ test_i18ncmp expect actual &&
+ ! test -f .git/worktrees/abc &&
+ ! test -d .git/worktrees
+'
+
+test_expect_success 'prune directories without gitdir' '
+ mkdir -p .git/worktrees/def/abc &&
+ : >.git/worktrees/def/def &&
+ cat >expect <<EOF &&
+Removing worktrees/def: gitdir file does not exist
+EOF
+ git worktree prune --verbose >actual &&
+ test_i18ncmp expect actual &&
+ ! test -d .git/worktrees/def &&
+ ! test -d .git/worktrees
+'
+
+test_expect_success SANITY 'prune directories with unreadable gitdir' '
+ mkdir -p .git/worktrees/def/abc &&
+ : >.git/worktrees/def/def &&
+ : >.git/worktrees/def/gitdir &&
+ chmod u-r .git/worktrees/def/gitdir &&
+ git worktree prune --verbose >actual &&
+ test_i18ngrep "Removing worktrees/def: unable to read gitdir file" actual &&
+ ! test -d .git/worktrees/def &&
+ ! test -d .git/worktrees
+'
+
+test_expect_success 'prune directories with invalid gitdir' '
+ mkdir -p .git/worktrees/def/abc &&
+ : >.git/worktrees/def/def &&
+ : >.git/worktrees/def/gitdir &&
+ git worktree prune --verbose >actual &&
+ test_i18ngrep "Removing worktrees/def: invalid gitdir file" actual &&
+ ! test -d .git/worktrees/def &&
+ ! test -d .git/worktrees
+'
+
+test_expect_success 'prune directories with gitdir pointing to nowhere' '
+ mkdir -p .git/worktrees/def/abc &&
+ : >.git/worktrees/def/def &&
+ echo "$(pwd)"/nowhere >.git/worktrees/def/gitdir &&
+ git worktree prune --verbose >actual &&
+ test_i18ngrep "Removing worktrees/def: gitdir file points to non-existent location" actual &&
+ ! test -d .git/worktrees/def &&
+ ! test -d .git/worktrees
+'
+
+test_expect_success 'not prune locked checkout' '
+ test_when_finished rm -r .git/worktrees &&
+ mkdir -p .git/worktrees/ghi &&
+ : >.git/worktrees/ghi/locked &&
+ git worktree prune &&
+ test -d .git/worktrees/ghi
+'
+
+test_expect_success 'not prune recent checkouts' '
+ test_when_finished rm -r .git/worktrees &&
+ mkdir zz &&
+ mkdir -p .git/worktrees/jlm &&
+ echo "$(pwd)"/zz >.git/worktrees/jlm/gitdir &&
+ rmdir zz &&
+ git worktree prune --verbose --expire=2.days.ago &&
+ test -d .git/worktrees/jlm
+'
+
+test_expect_success 'not prune proper checkouts' '
+ test_when_finished rm -r .git/worktrees &&
+ git worktree add --detach "$PWD/nop" master &&
+ git worktree prune &&
+ test -d .git/worktrees/nop
+'
+
+test_done
diff --git a/t/t2027-worktree-list.sh b/t/t2027-worktree-list.sh
new file mode 100755
index 0000000..1b1b65a
--- /dev/null
+++ b/t/t2027-worktree-list.sh
@@ -0,0 +1,99 @@
+#!/bin/sh
+
+test_description='test git worktree list'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit init
+'
+
+test_expect_success 'rev-parse --git-common-dir on main worktree' '
+ git rev-parse --git-common-dir >actual &&
+ echo .git >expected &&
+ test_cmp expected actual &&
+ mkdir sub &&
+ git -C sub rev-parse --git-common-dir >actual2 &&
+ echo sub/.git >expected2 &&
+ test_cmp expected2 actual2
+'
+
+test_expect_success '"list" all worktrees from main' '
+ echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
+ test_when_finished "rm -rf here && git worktree prune" &&
+ git worktree add --detach here master &&
+ echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect &&
+ git worktree list | sed "s/ */ /g" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '"list" all worktrees from linked' '
+ echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
+ test_when_finished "rm -rf here && git worktree prune" &&
+ git worktree add --detach here master &&
+ echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect &&
+ git -C here worktree list | sed "s/ */ /g" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '"list" all worktrees --porcelain' '
+ echo "worktree $(git rev-parse --show-toplevel)" >expect &&
+ echo "HEAD $(git rev-parse HEAD)" >>expect &&
+ echo "branch $(git symbolic-ref HEAD)" >>expect &&
+ echo >>expect &&
+ test_when_finished "rm -rf here && git worktree prune" &&
+ git worktree add --detach here master &&
+ echo "worktree $(git -C here rev-parse --show-toplevel)" >>expect &&
+ echo "HEAD $(git rev-parse HEAD)" >>expect &&
+ echo "detached" >>expect &&
+ echo >>expect &&
+ git worktree list --porcelain >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'bare repo setup' '
+ git init --bare bare1 &&
+ echo "data" >file1 &&
+ git add file1 &&
+ git commit -m"File1: add data" &&
+ git push bare1 master &&
+ git reset --hard HEAD^
+'
+
+test_expect_success '"list" all worktrees from bare main' '
+ test_when_finished "rm -rf there && git -C bare1 worktree prune" &&
+ git -C bare1 worktree add --detach ../there master &&
+ echo "$(pwd)/bare1 (bare)" >expect &&
+ echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect &&
+ git -C bare1 worktree list | sed "s/ */ /g" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '"list" all worktrees --porcelain from bare main' '
+ test_when_finished "rm -rf there && git -C bare1 worktree prune" &&
+ git -C bare1 worktree add --detach ../there master &&
+ echo "worktree $(pwd)/bare1" >expect &&
+ echo "bare" >>expect &&
+ echo >>expect &&
+ echo "worktree $(git -C there rev-parse --show-toplevel)" >>expect &&
+ echo "HEAD $(git -C there rev-parse HEAD)" >>expect &&
+ echo "detached" >>expect &&
+ echo >>expect &&
+ git -C bare1 worktree list --porcelain >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '"list" all worktrees from linked with a bare main' '
+ test_when_finished "rm -rf there && git -C bare1 worktree prune" &&
+ git -C bare1 worktree add --detach ../there master &&
+ echo "$(pwd)/bare1 (bare)" >expect &&
+ echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect &&
+ git -C there worktree list | sed "s/ */ /g" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'bare repo cleanup' '
+ rm -rf bare1
+'
+
+test_done
diff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh
index cb7effe..309199b 100755
--- a/t/t2030-unresolve-info.sh
+++ b/t/t2030-unresolve-info.sh
@@ -44,14 +44,21 @@ prime_resolve_undo () {
test_expect_success setup '
mkdir fi &&
+ printf "a\0a" >binary &&
+ git add binary &&
test_commit initial fi/le first &&
git branch side &&
git branch another &&
+ printf "a\0b" >binary &&
+ git add binary &&
test_commit second fi/le second &&
git checkout side &&
test_commit third fi/le third &&
+ git branch add-add &&
git checkout another &&
test_commit fourth fi/le fourth &&
+ git checkout add-add &&
+ test_commit fifth add-differently &&
git checkout master
'
@@ -113,7 +120,7 @@ test_expect_success 'unmerge with plumbing' '
prime_resolve_undo &&
git update-index --unresolve fi/le &&
git ls-files -u >actual &&
- test $(wc -l <actual) = 3
+ test_line_count = 3 actual
'
test_expect_success 'rerere and rerere forget' '
@@ -167,4 +174,22 @@ test_expect_success 'rerere and rerere forget (subdirectory)' '
test_cmp expect actual
'
+test_expect_success 'rerere forget (binary)' '
+ git checkout -f side &&
+ printf "a\0c" >binary &&
+ git commit -a -m binary &&
+ test_must_fail git merge second &&
+ git rerere forget binary
+'
+
+test_expect_success 'rerere forget (add-add conflict)' '
+ git checkout -f master &&
+ echo master >add-differently &&
+ git add add-differently &&
+ git commit -m "add differently" &&
+ test_must_fail git merge fifth &&
+ git rerere forget add-differently 2>actual &&
+ test_i18ngrep "no remembered" actual
+'
+
test_done
diff --git a/t/t2050-git-dir-relative.sh b/t/t2050-git-dir-relative.sh
index b7131d8..21f4659 100755
--- a/t/t2050-git-dir-relative.sh
+++ b/t/t2050-git-dir-relative.sh
@@ -26,7 +26,7 @@ chmod +x .git/hooks/post-commit'
test_expect_success 'post-commit hook used ordinarily' '
echo initial >top &&
-git add top
+git add top &&
git commit -m initial &&
test -r "${COMMIT_FILE}"
'
@@ -45,7 +45,7 @@ test -r "${COMMIT_FILE}"
rm -rf "${COMMIT_FILE}"
test_expect_success 'post-commit-hook from sub dir' '
-echo changed again >top
+echo changed again >top &&
cd subdir &&
git --git-dir .git --work-tree .. add ../top &&
git --git-dir .git --work-tree .. commit -m subcommit &&
diff --git a/t/t2101-update-index-reupdate.sh b/t/t2101-update-index-reupdate.sh
index 648184f..c8bce8c 100755
--- a/t/t2101-update-index-reupdate.sh
+++ b/t/t2101-update-index-reupdate.sh
@@ -51,7 +51,7 @@ test_expect_success 'update-index again' \
echo hello world >dir1/file3 &&
echo goodbye people >file2 &&
git update-index --add file2 dir1/file3 &&
- echo hello everybody >file2
+ echo hello everybody >file2 &&
echo happy >dir1/file3 &&
git update-index --again &&
git ls-files -s >current &&
@@ -63,10 +63,10 @@ cat > expected <<\EOF
EOF
test_expect_success 'update-index --update from subdir' \
'echo not so happy >file2 &&
- cd dir1 &&
+ (cd dir1 &&
cat ../file2 >file3 &&
- git update-index --again &&
- cd .. &&
+ git update-index --again
+ ) &&
git ls-files -s >current &&
cmp current expected'
diff --git a/t/t2102-update-index-symlinks.sh b/t/t2102-update-index-symlinks.sh
index 4d0d0a3..22f2c73 100755
--- a/t/t2102-update-index-symlinks.sh
+++ b/t/t2102-update-index-symlinks.sh
@@ -23,7 +23,7 @@ git update-index symlink'
test_expect_success \
'the index entry must still be a symbolic link' '
-case "`git ls-files --stage --cached symlink`" in
+case "$(git ls-files --stage --cached symlink)" in
120000" "*symlink) echo pass;;
*) echo fail; git ls-files --stage --cached symlink; (exit 1);;
esac'
diff --git a/t/t2104-update-index-skip-worktree.sh b/t/t2104-update-index-skip-worktree.sh
index 1d0879b..cc830da 100755
--- a/t/t2104-update-index-skip-worktree.sh
+++ b/t/t2104-update-index-skip-worktree.sh
@@ -7,6 +7,10 @@ test_description='skip-worktree bit test'
. ./test-lib.sh
+sane_unset GIT_TEST_SPLIT_INDEX
+
+test_set_index_version 3
+
cat >expect.full <<EOF
H 1
H 2
diff --git a/t/t2105-update-index-gitfile.sh b/t/t2105-update-index-gitfile.sh
index 641607d..a7f3d47 100755
--- a/t/t2105-update-index-gitfile.sh
+++ b/t/t2105-update-index-gitfile.sh
@@ -13,7 +13,7 @@ test_expect_success 'submodule with absolute .git file' '
(cd sub1 &&
git init &&
REAL="$(pwd)/.real" &&
- mv .git "$REAL"
+ mv .git "$REAL" &&
echo "gitdir: $REAL" >.git &&
test_commit first)
'
diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh
new file mode 100755
index 0000000..dfe02f4
--- /dev/null
+++ b/t/t2107-update-index-basic.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+test_description='basic update-index tests
+
+Tests for command-line parsing and basic operation.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'update-index --nonsense fails' '
+ test_must_fail git update-index --nonsense 2>msg &&
+ cat msg &&
+ test -s msg
+'
+
+test_expect_success 'update-index --nonsense dumps usage' '
+ test_expect_code 129 git update-index --nonsense 2>err &&
+ test_i18ngrep "[Uu]sage: git update-index" err
+'
+
+test_expect_success 'update-index -h with corrupt index' '
+ mkdir broken &&
+ (
+ cd broken &&
+ git init &&
+ >.git/index &&
+ test_expect_code 129 git update-index -h >usage 2>&1
+ ) &&
+ test_i18ngrep "[Uu]sage: git update-index" broken/usage
+'
+
+test_expect_success '--cacheinfo complains of missing arguments' '
+ test_must_fail git update-index --cacheinfo
+'
+
+test_expect_success '--cacheinfo does not accept blob null sha1' '
+ echo content >file &&
+ git add file &&
+ git rev-parse :file >expect &&
+ test_must_fail git update-index --cacheinfo 100644 $_z40 file &&
+ git rev-parse :file >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--cacheinfo does not accept gitlink null sha1' '
+ git init submodule &&
+ (cd submodule && test_commit foo) &&
+ git add submodule &&
+ git rev-parse :submodule >expect &&
+ test_must_fail git update-index --cacheinfo 160000 $_z40 submodule &&
+ git rev-parse :submodule >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--cacheinfo mode,sha1,path (new syntax)' '
+ echo content >file &&
+ git hash-object -w --stdin <file >expect &&
+
+ git update-index --add --cacheinfo 100644 "$(cat expect)" file &&
+ git rev-parse :file >actual &&
+ test_cmp expect actual &&
+
+ git update-index --add --cacheinfo "100644,$(cat expect),elif" &&
+ git rev-parse :elif >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '.lock files cleaned up' '
+ mkdir cleanup &&
+ (
+ cd cleanup &&
+ mkdir worktree &&
+ git init repo &&
+ cd repo &&
+ git config core.worktree ../../worktree &&
+ # --refresh triggers late setup_work_tree,
+ # active_cache_changed is zero, rollback_lock_file fails
+ git update-index --refresh &&
+ ! test -f .git/index.lock
+ )
+'
+
+test_done
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 2ad2819..314c73c 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -25,7 +25,7 @@ test_expect_success setup '
echo initial >dir1/sub2 &&
echo initial >dir2/sub3 &&
git add check dir1 dir2 top foo &&
- test_tick
+ test_tick &&
git commit -m initial &&
echo changed >check &&
@@ -80,11 +80,23 @@ test_expect_success 'change gets noticed' '
'
-test_expect_success SYMLINKS 'replace a file with a symlink' '
+test_expect_success 'non-qualified update in subdir updates from the root' '
+ (
+ cd dir1 &&
+ echo even more >>sub2 &&
+ git --literal-pathspecs add -u &&
+ echo even more >>sub2 &&
+ git add -u
+ ) &&
+ : >expect &&
+ git diff-files --name-only >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'replace a file with a symlink' '
rm foo &&
- ln -s top foo &&
- git add -u -- foo
+ test_ln_s_add top foo
'
@@ -124,7 +136,7 @@ test_expect_success 'add -n -u should not add but just report' '
after=$(git ls-files -s check top) &&
test "$before" = "$after" &&
- test_cmp expect actual
+ test_i18ncmp expect actual
'
@@ -149,31 +161,21 @@ test_expect_success 'add -u resolves unmerged paths' '
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 &&
+ # Fail to explicitly resolve removed paths with "git add"
+ test_must_fail git add --no-all path4 &&
+ test_must_fail git add --no-all path6 &&
- git ls-files -s "path?" >actual &&
+ # "add -u" should notice removals no matter what stages
+ # the index entries are in.
+ git add -u &&
+ git ls-files -s path1 path2 path3 path4 path5 path6 >actual &&
{
echo "100644 $three 0 path1"
echo "100644 $two 0 path3"
echo "100644 $two 0 path5"
- } >expect
-
+ } >expect &&
+ test_cmp expect actual
'
test_expect_success '"add -u non-existent" should fail' '
diff --git a/t/t2201-add-update-typechange.sh b/t/t2201-add-update-typechange.sh
index 2e8f702..954fc51 100755
--- a/t/t2201-add-update-typechange.sh
+++ b/t/t2201-add-update-typechange.sh
@@ -4,8 +4,6 @@ test_description='more git add -u'
. ./test-lib.sh
-_z40=0000000000000000000000000000000000000000
-
test_expect_success setup '
>xyzzy &&
_empty=$(git hash-object --stdin <xyzzy) &&
diff --git a/t/t2202-add-addremove.sh b/t/t2202-add-addremove.sh
index 6a81510..6a5a316 100755
--- a/t/t2202-add-addremove.sh
+++ b/t/t2202-add-addremove.sh
@@ -14,6 +14,7 @@ test_expect_success setup '
echo expect
echo ignored
) >.gitignore &&
+ git --literal-pathspecs add --all &&
>will-remove &&
git add --all &&
test_tick &&
@@ -41,4 +42,14 @@ test_expect_success 'git add --all' '
test_cmp expect actual
'
+test_expect_success 'Just "git add" is a no-op' '
+ git reset --hard &&
+ echo >will-remove &&
+ >will-not-be-added &&
+ git add &&
+ git diff-index --name-status --cached HEAD >actual &&
+ >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh
index 58a3299..2a4a749 100755
--- a/t/t2203-add-intent.sh
+++ b/t/t2203-add-intent.sh
@@ -32,7 +32,7 @@ test_expect_success 'intent to add does not clobber existing paths' '
! grep "$empty" actual
'
-test_expect_success 'cannot commit with i-t-a entry' '
+test_expect_success 'i-t-a entry is simply ignored' '
test_tick &&
git commit -a -m initial &&
git reset --hard &&
@@ -41,12 +41,14 @@ test_expect_success 'cannot commit with i-t-a entry' '
echo frotz >nitfol &&
git add rezrov &&
git add -N nitfol &&
- test_must_fail git commit
+ git commit -m second &&
+ test $(git ls-tree HEAD -- nitfol | wc -l) = 0 &&
+ test $(git diff --name-only HEAD -- nitfol | wc -l) = 1
'
test_expect_success 'can commit with an unrelated i-t-a entry in index' '
git reset --hard &&
- echo xyzzy >rezrov &&
+ echo bozbar >rezrov &&
echo frotz >nitfol &&
git add rezrov &&
git add -N nitfol &&
@@ -60,5 +62,25 @@ test_expect_success 'can "commit -a" with an i-t-a entry' '
git commit -a -m all
'
+test_expect_success 'cache-tree invalidates i-t-a paths' '
+ git reset --hard &&
+ mkdir dir &&
+ : >dir/foo &&
+ git add dir/foo &&
+ git commit -m foo &&
+
+ : >dir/bar &&
+ git add -N dir/bar &&
+ git diff --cached --name-only >actual &&
+ echo dir/bar >expect &&
+ test_cmp expect actual &&
+
+ git write-tree >/dev/null &&
+
+ git diff --cached --name-only >actual &&
+ echo dir/bar >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t2204-add-ignored.sh b/t/t2204-add-ignored.sh
index 24afdab..8340ac2 100755
--- a/t/t2204-add-ignored.sh
+++ b/t/t2204-add-ignored.sh
@@ -31,18 +31,21 @@ do
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 -s out
+ '
+
+ test_expect_success "complaints for ignored $i output" '
+ test_i18ngrep -e "Use -f if" 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
+ ! test -s out
+ '
+ test_expect_success "complaints for ignored $i with unignored file output" '
+ test_i18ngrep -e "Use -f if" err
'
done
@@ -54,9 +57,14 @@ do
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
+ ! test -s out
+ )
+ '
+
+ test_expect_success "complaints for ignored $i in dir output" '
+ (
+ cd dir &&
+ test_i18ngrep -e "Use -f if" err
)
'
done
@@ -69,9 +77,14 @@ do
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
+ ! test -s out
+ )
+ '
+
+ test_expect_success "complaints for ignored $i in sub output" '
+ (
+ cd sub &&
+ test_i18ngrep -e "Use -f if" err
)
'
done
diff --git a/t/t2300-cd-to-toplevel.sh b/t/t2300-cd-to-toplevel.sh
index 9965bc5..c8de6d8 100755
--- a/t/t2300-cd-to-toplevel.sh
+++ b/t/t2300-cd-to-toplevel.sh
@@ -4,11 +4,20 @@ test_description='cd_to_toplevel'
. ./test-lib.sh
+EXEC_PATH="$(git --exec-path)"
+test_have_prereq !MINGW ||
+case "$EXEC_PATH" in
+[A-Za-z]:/*)
+ EXEC_PATH="/${EXEC_PATH%%:*}${EXEC_PATH#?:}"
+ ;;
+esac
+
test_cd_to_toplevel () {
test_expect_success $3 "$2" '
(
cd '"'$1'"' &&
- . "$(git --exec-path)"/git-sh-setup &&
+ PATH="$EXEC_PATH:$PATH" &&
+ . git-sh-setup &&
cd_to_toplevel &&
[ "$(pwd -P)" = "$TOPLEVEL" ]
)
diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
index 2eec011..c525656 100755
--- a/t/t3000-ls-files-others.sh
+++ b/t/t3000-ls-files-others.sh
@@ -65,4 +65,30 @@ test_expect_success '--no-empty-directory hides empty directory' '
test_cmp expected3 output
'
+test_expect_success 'ls-files --others handles non-submodule .git' '
+ mkdir not-a-submodule &&
+ echo foo >not-a-submodule/.git &&
+ git ls-files -o >output &&
+ test_cmp expected1 output
+'
+
+test_expect_success SYMLINKS 'ls-files --others with symlinked submodule' '
+ git init super &&
+ git init sub &&
+ (
+ cd sub &&
+ >a &&
+ git add a &&
+ git commit -m sub &&
+ git pack-refs --all
+ ) &&
+ (
+ cd super &&
+ "$SHELL_PATH" "$TEST_DIRECTORY/../contrib/workdir/git-new-workdir" ../sub sub
+ git ls-files --others --exclude-standard >../actual
+ ) &&
+ echo sub/ >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index 6d2f2b6..3fc484e 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -103,7 +103,7 @@ test_expect_success \
test_cmp expect output'
test_expect_success 'restore gitignore' '
- git checkout $allignores &&
+ git checkout --ignore-skip-worktree-bits $allignores &&
rm .git/index
'
@@ -115,7 +115,7 @@ EOF
git config core.excludesFile excludes-file
-git status | grep "^# " > output
+git -c status.displayCommentPrefix=true status | grep "^# " > output
cat > expect << EOF
# .gitignore
@@ -156,7 +156,7 @@ test_expect_success 'trailing slash in exclude allows directory match (2)' '
test_expect_success 'trailing slash in exclude forces directory match (1)' '
- >two
+ >two &&
git ls-files --others --exclude=two/ >output &&
grep "^two" output
@@ -175,6 +175,24 @@ test_expect_success 'negated exclude matches can override previous ones' '
grep "^a.1" output
'
+test_expect_success 'excluded directory overrides content patterns' '
+
+ git ls-files --others --exclude="one" --exclude="!one/a.1" >output &&
+ if grep "^one/a.1" output
+ then
+ false
+ fi
+'
+
+test_expect_success 'negated directory doesn'\''t affect content patterns' '
+
+ git ls-files --others --exclude="!one" --exclude="one/a.1" >output &&
+ if grep "^one/a.1" output
+ then
+ false
+ fi
+'
+
test_expect_success 'subdirectory ignore (setup)' '
mkdir -p top/l1/l2 &&
(
@@ -214,4 +232,77 @@ test_expect_success 'subdirectory ignore (l1)' '
test_cmp expect actual
'
+test_expect_success 'show/hide empty ignored directory (setup)' '
+ rm top/l1/l2/l1 &&
+ rm top/l1/.gitignore
+'
+
+test_expect_success 'show empty ignored directory with --directory' '
+ (
+ cd top &&
+ git ls-files -o -i --exclude l1 --directory
+ ) >actual &&
+ echo l1/ >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'hide empty ignored directory with --no-empty-directory' '
+ (
+ cd top &&
+ git ls-files -o -i --exclude l1 --directory --no-empty-directory
+ ) >actual &&
+ >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'show/hide empty ignored sub-directory (setup)' '
+ > top/l1/tracked &&
+ (
+ cd top &&
+ git add -f l1/tracked
+ )
+'
+
+test_expect_success 'show empty ignored sub-directory with --directory' '
+ (
+ cd top &&
+ git ls-files -o -i --exclude l1 --directory
+ ) >actual &&
+ echo l1/l2/ >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'hide empty ignored sub-directory with --no-empty-directory' '
+ (
+ cd top &&
+ git ls-files -o -i --exclude l1 --directory --no-empty-directory
+ ) >actual &&
+ >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pattern matches prefix completely' '
+ : >expect &&
+ git ls-files -i -o --exclude "/three/a.3[abc]" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'ls-files with "**" patterns' '
+ cat <<\EOF >expect &&
+a.1
+one/a.1
+one/two/a.1
+three/a.1
+EOF
+ git ls-files -o -i --exclude "**/a.1" >actual &&
+ test_cmp expect actual
+'
+
+
+test_expect_success 'ls-files with "**" patterns and no slashes' '
+ : >expect &&
+ git ls-files -o -i --exclude "one**a.1" >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3004-ls-files-basic.sh b/t/t3004-ls-files-basic.sh
new file mode 100755
index 0000000..9c7adbd
--- /dev/null
+++ b/t/t3004-ls-files-basic.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+test_description='basic ls-files tests
+
+This test runs git ls-files with various unusual or malformed
+command-line arguments.
+'
+
+. ./test-lib.sh
+
+>empty
+
+test_expect_success 'ls-files in empty repository' '
+ git ls-files >actual &&
+ test_cmp empty actual
+'
+
+test_expect_success 'ls-files with nonexistent path' '
+ git ls-files doesnotexist >actual &&
+ test_cmp empty actual
+'
+
+test_expect_success 'ls-files with nonsense option' '
+ test_expect_code 129 git ls-files --nonsense 2>actual &&
+ test_i18ngrep "[Uu]sage: git ls-files" actual
+'
+
+test_expect_success 'ls-files -h in corrupt repository' '
+ mkdir broken &&
+ (
+ cd broken &&
+ git init &&
+ >.git/index &&
+ test_expect_code 129 git ls-files -h >usage 2>&1
+ ) &&
+ test_i18ngrep "[Uu]sage: git ls-files " broken/usage
+'
+
+test_expect_success SYMLINKS 'ls-files with absolute paths to symlinks' '
+ mkdir subs &&
+ ln -s nosuch link &&
+ ln -s ../nosuch subs/link &&
+ git add link subs/link &&
+ git ls-files -s link subs/link >expect &&
+ git ls-files -s "$(pwd)/link" "$(pwd)/subs/link" >actual &&
+ test_cmp expect actual &&
+
+ (
+ cd subs &&
+ git ls-files -s link >../expect &&
+ git ls-files -s "$(pwd)/link" >../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t3005-ls-files-relative.sh b/t/t3005-ls-files-relative.sh
new file mode 100755
index 0000000..3778694
--- /dev/null
+++ b/t/t3005-ls-files-relative.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+test_description='ls-files tests with relative paths
+
+This test runs git ls-files with various relative path arguments.
+'
+
+. ./test-lib.sh
+
+new_line='
+'
+sq=\'
+
+test_expect_success 'prepare' '
+ : >never-mind-me &&
+ git add never-mind-me &&
+ mkdir top &&
+ (
+ cd top &&
+ mkdir sub &&
+ x="x xa xbc xdef xghij xklmno" &&
+ y=$(echo "$x" | tr x y) &&
+ touch $x &&
+ touch $y &&
+ cd sub &&
+ git add ../x*
+ )
+'
+
+test_expect_success 'ls-files with mixed levels' '
+ (
+ cd top/sub &&
+ cat >expect <<-EOF &&
+ ../../never-mind-me
+ ../x
+ EOF
+ git ls-files $(cat expect) >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'ls-files -c' '
+ (
+ cd top/sub &&
+ for f in ../y*
+ do
+ echo "error: pathspec $sq$f$sq did not match any file(s) known to git."
+ done >expect.err &&
+ echo "Did you forget to ${sq}git add${sq}?" >>expect.err &&
+ ls ../x* >expect.out &&
+ test_must_fail git ls-files -c --error-unmatch ../[xy]* >actual.out 2>actual.err &&
+ test_cmp expect.out actual.out &&
+ test_cmp expect.err actual.err
+ )
+'
+
+test_expect_success 'ls-files -o' '
+ (
+ cd top/sub &&
+ for f in ../x*
+ do
+ echo "error: pathspec $sq$f$sq did not match any file(s) known to git."
+ done >expect.err &&
+ echo "Did you forget to ${sq}git add${sq}?" >>expect.err &&
+ ls ../y* >expect.out &&
+ test_must_fail git ls-files -o --error-unmatch ../[xy]* >actual.out 2>actual.err &&
+ test_cmp expect.out actual.out &&
+ test_cmp expect.err actual.err
+ )
+'
+
+test_done
diff --git a/t/t3006-ls-files-long.sh b/t/t3006-ls-files-long.sh
new file mode 100755
index 0000000..202ad65
--- /dev/null
+++ b/t/t3006-ls-files-long.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+test_description='overly long paths'
+. ./test-lib.sh
+
+test_expect_success setup '
+ p=filefilefilefilefilefilefilefile &&
+ p=$p$p$p$p$p$p$p$p$p$p$p$p$p$p$p$p &&
+ p=$p$p$p$p$p$p$p$p$p$p$p$p$p$p$p$p &&
+
+ path_a=${p}_a &&
+ path_z=${p}_z &&
+
+ blob_a=$(echo frotz | git hash-object -w --stdin) &&
+ blob_z=$(echo nitfol | git hash-object -w --stdin) &&
+
+ pat="100644 %s 0\t%s\n"
+'
+
+test_expect_success 'overly-long path by itself is not a problem' '
+ printf "$pat" "$blob_a" "$path_a" |
+ git update-index --add --index-info &&
+ echo "$path_a" >expect &&
+ git ls-files >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'overly-long path does not replace another by mistake' '
+ printf "$pat" "$blob_a" "$path_a" "$blob_z" "$path_z" |
+ git update-index --add --index-info &&
+ (
+ echo "$path_a"
+ echo "$path_z"
+ ) >expect &&
+ git ls-files >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh
index 95671c2..580e158 100755
--- a/t/t3010-ls-files-killed-modified.sh
+++ b/t/t3010-ls-files-killed-modified.sh
@@ -11,6 +11,9 @@ This test prepares the following in the cache:
path1 - a symlink
path2/file2 - a file in a directory
path3/file3 - a file in a directory
+ pathx/ju - a file in a directory
+ submod1/ - a submodule
+ submod2/ - another submodule
and the following on the filesystem:
@@ -21,9 +24,12 @@ and the following on the filesystem:
path4 - a file
path5 - a symlink
path6/file6 - a file in a directory
+ pathx/ju/nk - a file in a directory to be killed
+ submod1/ - a submodule (modified from the cache)
+ submod2/ - a submodule (matches the cache)
-git ls-files -k should report that existing filesystem
-objects except path4, path5 and path6/file6 to be killed.
+git ls-files -k should report that existing filesystem objects
+path0/*, path1/*, path2 and path3 to be killed.
Also for modification test, the cache and working tree have:
@@ -33,75 +39,88 @@ Also for modification test, the cache and working tree have:
path10 - a non-empty file, cache dirtied.
We should report path0, path1, path2/file2, path3/file3, path7 and path8
-modified without reporting path9 and path10.
+modified without reporting path9 and path10. submod1 is also modified.
'
. ./test-lib.sh
-date >path0
-if test_have_prereq SYMLINKS
-then
- ln -s xyzzy path1
-else
- date > path1
-fi
-mkdir path2 path3
-date >path2/file2
-date >path3/file3
-: >path7
-date >path8
-: >path9
-date >path10
-test_expect_success \
- 'git update-index --add to add various paths.' \
- "git update-index --add -- path0 path1 path?/file? path7 path8 path9 path10"
-
-rm -fr path? ;# leave path10 alone
-date >path2
-if test_have_prereq SYMLINKS
-then
- ln -s frotz path3
- ln -s nitfol path5
-else
- date > path3
- date > path5
-fi
-mkdir path0 path1 path6
-date >path0/file0
-date >path1/file1
-date >path6/file6
-date >path7
-: >path8
-: >path9
-touch path10
-
-test_expect_success \
- 'git ls-files -k to show killed files.' \
- 'git ls-files -k >.output'
-cat >.expected <<EOF
-path0/file0
-path1/file1
-path2
-path3
-EOF
-
-test_expect_success \
- 'validate git ls-files -k output.' \
- 'test_cmp .expected .output'
-
-test_expect_success \
- 'git ls-files -m to show modified files.' \
- 'git ls-files -m >.output'
-cat >.expected <<EOF
-path0
-path1
-path2/file2
-path3/file3
-path7
-path8
-EOF
-
-test_expect_success \
- 'validate git ls-files -m output.' \
- 'test_cmp .expected .output'
+test_expect_success 'git update-index --add to add various paths.' '
+ date >path0 &&
+ test_ln_s_add xyzzy path1 &&
+ mkdir path2 path3 pathx &&
+ date >path2/file2 &&
+ date >path3/file3 &&
+ >pathx/ju &&
+ : >path7 &&
+ date >path8 &&
+ : >path9 &&
+ date >path10 &&
+ git update-index --add -- path0 path?/file? pathx/ju path7 path8 path9 path10 &&
+ git init submod1 &&
+ git -C submod1 commit --allow-empty -m "empty 1" &&
+ git init submod2 &&
+ git -C submod2 commit --allow-empty -m "empty 2" &&
+ git update-index --add submod[12] &&
+ (
+ cd submod1 &&
+ git commit --allow-empty -m "empty 1 (updated)"
+ ) &&
+ rm -fr path? # leave path10 alone
+'
+
+test_expect_success 'git ls-files -k to show killed files.' '
+ date >path2 &&
+ if test_have_prereq SYMLINKS
+ then
+ ln -s frotz path3 &&
+ ln -s nitfol path5
+ else
+ date >path3 &&
+ date >path5
+ fi &&
+ mkdir -p path0 path1 path6 pathx/ju &&
+ date >path0/file0 &&
+ date >path1/file1 &&
+ date >path6/file6 &&
+ date >path7 &&
+ : >path8 &&
+ : >path9 &&
+ touch path10 &&
+ >pathx/ju/nk &&
+ cat >.expected <<-\EOF
+ path0/file0
+ path1/file1
+ path2
+ path3
+ pathx/ju/nk
+ EOF
+'
+
+test_expect_success 'git ls-files -k output (w/o icase)' '
+ git ls-files -k >.output &&
+ test_cmp .expected .output
+'
+
+test_expect_success 'git ls-files -k output (w/ icase)' '
+ git -c core.ignorecase=true ls-files -k >.output &&
+ test_cmp .expected .output
+'
+
+test_expect_success 'git ls-files -m to show modified files.' '
+ git ls-files -m >.output
+'
+
+test_expect_success 'validate git ls-files -m output.' '
+ cat >.expected <<-\EOF &&
+ path0
+ path1
+ path2/file2
+ path3/file3
+ path7
+ path8
+ pathx/ju
+ submod1
+ EOF
+ test_cmp .expected .output
+'
test_done
diff --git a/t/t3020-ls-files-error-unmatch.sh b/t/t3020-ls-files-error-unmatch.sh
index a7d8187..124e73b 100755
--- a/t/t3020-ls-files-error-unmatch.sh
+++ b/t/t3020-ls-files-error-unmatch.sh
@@ -22,8 +22,7 @@ test_expect_success \
'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 should succeed with 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
index d541544..f7b0e59 100755
--- a/t/t3030-merge-recursive.sh
+++ b/t/t3030-merge-recursive.sh
@@ -23,6 +23,9 @@ test_expect_success 'setup 1' '
git branch df-3 &&
git branch remove &&
git branch submod &&
+ git branch copy &&
+ git branch rename &&
+ git branch rename-ln &&
echo hello >>a &&
cp a d/e &&
@@ -248,22 +251,35 @@ test_expect_success 'setup 7' '
git commit -m "make d/ a submodule"
'
+test_expect_success 'setup 8' '
+ git checkout rename &&
+ git mv a e &&
+ git add e &&
+ test_tick &&
+ git commit -m "rename a->e" &&
+ c7=$(git rev-parse --verify HEAD) &&
+ git checkout rename-ln &&
+ git mv a e &&
+ test_ln_s_add e a &&
+ test_tick &&
+ git commit -m "rename a->e, symlink a->e" &&
+ oln=$(printf e | git hash-object --stdin)
+'
+
+test_expect_success 'setup 9' '
+ git checkout copy &&
+ cp a e &&
+ git add e &&
+ test_tick &&
+ git commit -m "copy a->e"
+'
+
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_code 1 git merge-recursive "$c0" -- "$c2" "$c1"
'
test_expect_success 'merge-recursive result' '
@@ -288,13 +304,13 @@ test_expect_success 'fail if the index has unresolved entries' '
test_must_fail git merge "$c5" &&
test_must_fail git merge "$c5" 2> out &&
- grep "not possible because you have unmerged files" out &&
+ test_i18ngrep "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 &&
+ test_i18ngrep "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_i18ngrep "Your local changes to the following files would be overwritten by merge:" out
'
test_expect_success 'merge-recursive remove conflict' '
@@ -302,17 +318,7 @@ 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_code 1 git merge-recursive "$c0" -- "$c1" "$c5"
'
test_expect_success 'merge-recursive remove conflict' '
@@ -356,17 +362,7 @@ test_expect_success 'merge-recursive d/f conflict' '
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_code 1 git merge-recursive "$c0" -- "$c1" "$c4"
'
test_expect_success 'merge-recursive d/f conflict result' '
@@ -390,17 +386,7 @@ test_expect_success 'merge-recursive d/f conflict the other way' '
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_code 1 git merge-recursive "$c0" -- "$c4" "$c1"
'
test_expect_success 'merge-recursive d/f conflict result the other way' '
@@ -424,17 +410,7 @@ test_expect_success 'merge-recursive d/f conflict' '
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_code 1 git merge-recursive "$c0" -- "$c1" "$c6"
'
test_expect_success 'merge-recursive d/f conflict result' '
@@ -458,17 +434,7 @@ test_expect_success 'merge-recursive d/f conflict' '
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_code 1 git merge-recursive "$c0" -- "$c6" "$c1"
'
test_expect_success 'merge-recursive d/f conflict result' '
@@ -526,7 +492,7 @@ test_expect_success 'reset and bind merge' '
echo "100644 $o0 0 c"
echo "100644 $o1 0 d/e"
) >expected &&
- test_cmp expected actual
+ test_cmp expected actual &&
git read-tree --prefix=z/ master &&
git ls-files -s >actual &&
@@ -552,6 +518,52 @@ test_expect_success 'reset and bind merge' '
'
+test_expect_success 'merge-recursive w/ empty work tree - ours has rename' '
+ (
+ GIT_WORK_TREE="$PWD/ours-has-rename-work" &&
+ export GIT_WORK_TREE &&
+ GIT_INDEX_FILE="$PWD/ours-has-rename-index" &&
+ export GIT_INDEX_FILE &&
+ mkdir "$GIT_WORK_TREE" &&
+ git read-tree -i -m $c7 &&
+ git update-index --ignore-missing --refresh &&
+ git merge-recursive $c0 -- $c7 $c3 &&
+ git ls-files -s >actual-files
+ ) 2>actual-err &&
+ >expected-err &&
+ cat >expected-files <<-EOF &&
+ 100644 $o3 0 b/c
+ 100644 $o0 0 c
+ 100644 $o0 0 d/e
+ 100644 $o0 0 e
+ EOF
+ test_cmp expected-files actual-files &&
+ test_cmp expected-err actual-err
+'
+
+test_expect_success 'merge-recursive w/ empty work tree - theirs has rename' '
+ (
+ GIT_WORK_TREE="$PWD/theirs-has-rename-work" &&
+ export GIT_WORK_TREE &&
+ GIT_INDEX_FILE="$PWD/theirs-has-rename-index" &&
+ export GIT_INDEX_FILE &&
+ mkdir "$GIT_WORK_TREE" &&
+ git read-tree -i -m $c3 &&
+ git update-index --ignore-missing --refresh &&
+ git merge-recursive $c0 -- $c3 $c7 &&
+ git ls-files -s >actual-files
+ ) 2>actual-err &&
+ >expected-err &&
+ cat >expected-files <<-EOF &&
+ 100644 $o3 0 b/c
+ 100644 $o0 0 c
+ 100644 $o0 0 d/e
+ 100644 $o0 0 e
+ EOF
+ test_cmp expected-files actual-files &&
+ test_cmp expected-err actual-err
+'
+
test_expect_success 'merge removes empty directories' '
git reset --hard master &&
@@ -580,4 +592,72 @@ test_expect_failure 'merge-recursive simple w/submodule result' '
test_cmp expected actual
'
+test_expect_success 'merge-recursive copy vs. rename' '
+ git checkout -f copy &&
+ git merge rename &&
+ ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 c"
+ echo "100644 blob $o0 d/e"
+ echo "100644 blob $o0 e"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o0 0 d/e"
+ echo "100644 $o0 0 e"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_expect_failure 'merge-recursive rename vs. rename/symlink' '
+
+ git checkout -f rename &&
+ git merge rename-ln &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "120000 blob $oln a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 c"
+ echo "100644 blob $o0 d/e"
+ echo "100644 blob $o0 e"
+ echo "120000 $oln 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o0 0 d/e"
+ echo "100644 $o0 0 e"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'merging with triple rename across D/F conflict' '
+ git reset --hard HEAD &&
+ git checkout -b main &&
+ git rm -rf . &&
+
+ echo "just a file" >sub1 &&
+ mkdir -p sub2 &&
+ echo content1 >sub2/file1 &&
+ echo content2 >sub2/file2 &&
+ echo content3 >sub2/file3 &&
+ mkdir simple &&
+ echo base >simple/bar &&
+ git add -A &&
+ test_tick &&
+ git commit -m base &&
+
+ git checkout -b other &&
+ echo more >>simple/bar &&
+ test_tick &&
+ git commit -a -m changesimplefile &&
+
+ git checkout main &&
+ git rm sub1 &&
+ git mv sub2 sub1 &&
+ test_tick &&
+ git commit -m changefiletodir &&
+
+ test_tick &&
+ git merge other
+'
+
test_done
diff --git a/t/t3031-merge-criscross.sh b/t/t3031-merge-criscross.sh
index 7f41607..e59b0a3 100755
--- a/t/t3031-merge-criscross.sh
+++ b/t/t3031-merge-criscross.sh
@@ -32,7 +32,7 @@ test_expect_success 'setup repo with criss-cross history' '
do
echo $n > data/$n &&
n=$(($n+1)) ||
- break
+ return 1
done &&
# check them in
diff --git a/t/t3032-merge-recursive-space-options.sh b/t/t3032-merge-recursive-space-options.sh
new file mode 100755
index 0000000..b56180e
--- /dev/null
+++ b/t/t3032-merge-recursive-space-options.sh
@@ -0,0 +1,207 @@
+#!/bin/sh
+
+test_description='merge-recursive space options
+
+* [master] Clarify
+ ! [remote] Remove cruft
+--
+ + [remote] Remove cruft
+* [master] Clarify
+*+ [remote^] Initial revision
+* ok 1: setup
+'
+
+. ./test-lib.sh
+
+test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
+if test_have_prereq GREP_STRIPS_CR
+then
+ GREP_OPTIONS=-U
+ export GREP_OPTIONS
+fi
+
+test_expect_success 'setup' '
+ conflict_hunks () {
+ sed $SED_OPTIONS -n -e "
+ /^<<<</ b conflict
+ b
+ : conflict
+ p
+ /^>>>>/ b
+ n
+ b conflict
+ " "$@"
+ } &&
+
+ cat <<-\EOF >text.txt &&
+ Hope, he says, cherishes the soul of him who lives in
+ justice and holiness and is the nurse of his age and the
+ companion of his journey;--hope which is mightiest to sway
+ the restless soul of man.
+
+ How admirable are his words! And the great blessing of riches, I do
+ not say to every man, but to a good man, is, that he has had no
+ occasion to deceive or to defraud others, either intentionally or
+ unintentionally; and when he departs to the world below he is not in
+ any apprehension about offerings due to the gods or debts which he owes
+ to men. Now to this peace of mind the possession of wealth greatly
+ contributes; and therefore I say, that, setting one thing against
+ another, of the many advantages which wealth has to give, to a man of
+ sense this is in my opinion the greatest.
+
+ Well said, Cephalus, I replied; but as concerning justice, what is
+ it?--to speak the truth and to pay your debts--no more than this? And
+ even to this are there not exceptions? Suppose that a friend when in
+ his right mind has deposited arms with me and he asks for them when he
+ is not in his right mind, ought I to give them back to him? No one
+ would say that I ought or that I should be right in doing so, any more
+ than they would say that I ought always to speak the truth to one who
+ is in his condition.
+
+ You are quite right, he replied.
+
+ But then, I said, speaking the truth and paying your debts is not a
+ correct definition of justice.
+
+ CEPHALUS - SOCRATES - POLEMARCHUS
+
+ Quite correct, Socrates, if Simonides is to be believed, said
+ Polemarchus interposing.
+
+ I fear, said Cephalus, that I must go now, for I have to look after the
+ sacrifices, and I hand over the argument to Polemarchus and the company.
+ EOF
+ git add text.txt &&
+ test_tick &&
+ git commit -m "Initial revision" &&
+
+ git checkout -b remote &&
+ sed -e "
+ s/\. /\. /g
+ s/[?] /? /g
+ s/ / /g
+ s/--/---/g
+ s/but as concerning/but as con cerning/
+ /CEPHALUS - SOCRATES - POLEMARCHUS/ d
+ " text.txt >text.txt+ &&
+ mv text.txt+ text.txt &&
+ git commit -a -m "Remove cruft" &&
+
+ git checkout master &&
+ sed -e "
+ s/\(not in his right mind\),\(.*\)/\1;\2Q/
+ s/Quite correct\(.*\)/It is too correct\1Q/
+ s/unintentionally/un intentionally/
+ /un intentionally/ s/$/Q/
+ s/Polemarchus interposing./Polemarchus, interposing.Q/
+ /justice and holiness/ s/$/Q/
+ /pay your debts/ s/$/Q/
+ " text.txt | q_to_cr >text.txt+ &&
+ mv text.txt+ text.txt &&
+ git commit -a -m "Clarify" &&
+ git show-branch --all
+'
+
+test_expect_success 'naive merge fails' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive HEAD^ -- HEAD remote &&
+ test_must_fail git update-index --refresh &&
+ grep "<<<<<<" text.txt
+'
+
+test_expect_success '--ignore-space-change makes merge succeed' '
+ git read-tree --reset -u HEAD &&
+ git merge-recursive --ignore-space-change HEAD^ -- HEAD remote
+'
+
+test_expect_success 'naive cherry-pick fails' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git cherry-pick --no-commit remote &&
+ git read-tree --reset -u HEAD &&
+ test_must_fail git cherry-pick remote &&
+ test_must_fail git update-index --refresh &&
+ grep "<<<<<<" text.txt
+'
+
+test_expect_success '-Xignore-space-change makes cherry-pick succeed' '
+ git read-tree --reset -u HEAD &&
+ git cherry-pick --no-commit -Xignore-space-change remote
+'
+
+test_expect_success '--ignore-space-change: our w/s-only change wins' '
+ q_to_cr <<-\EOF >expected &&
+ justice and holiness and is the nurse of his age and theQ
+ EOF
+
+ git read-tree --reset -u HEAD &&
+ git merge-recursive --ignore-space-change HEAD^ -- HEAD remote &&
+ grep "justice and holiness" text.txt >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '--ignore-space-change: their real change wins over w/s' '
+ cat <<-\EOF >expected &&
+ it?---to speak the truth and to pay your debts---no more than this? And
+ EOF
+
+ git read-tree --reset -u HEAD &&
+ git merge-recursive --ignore-space-change HEAD^ -- HEAD remote &&
+ grep "pay your debts" text.txt >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '--ignore-space-change: does not ignore new spaces' '
+ cat <<-\EOF >expected1 &&
+ Well said, Cephalus, I replied; but as con cerning justice, what is
+ EOF
+ q_to_cr <<-\EOF >expected2 &&
+ un intentionally; and when he departs to the world below he is not inQ
+ EOF
+
+ git read-tree --reset -u HEAD &&
+ git merge-recursive --ignore-space-change HEAD^ -- HEAD remote &&
+ grep "Well said" text.txt >actual1 &&
+ grep "when he departs" text.txt >actual2 &&
+ test_cmp expected1 actual1 &&
+ test_cmp expected2 actual2
+'
+
+test_expect_success '--ignore-all-space drops their new spaces' '
+ cat <<-\EOF >expected &&
+ Well said, Cephalus, I replied; but as concerning justice, what is
+ EOF
+
+ git read-tree --reset -u HEAD &&
+ git merge-recursive --ignore-all-space HEAD^ -- HEAD remote &&
+ grep "Well said" text.txt >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '--ignore-all-space keeps our new spaces' '
+ q_to_cr <<-\EOF >expected &&
+ un intentionally; and when he departs to the world below he is not inQ
+ EOF
+
+ git read-tree --reset -u HEAD &&
+ git merge-recursive --ignore-all-space HEAD^ -- HEAD remote &&
+ grep "when he departs" text.txt >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '--ignore-space-at-eol' '
+ q_to_cr <<-\EOF >expected &&
+ <<<<<<< HEAD
+ is not in his right mind; ought I to give them back to him? No oneQ
+ =======
+ is not in his right mind, ought I to give them back to him? No one
+ >>>>>>> remote
+ EOF
+
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive --ignore-space-at-eol \
+ HEAD^ -- HEAD remote &&
+ conflict_hunks text.txt >actual &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t3033-merge-toplevel.sh b/t/t3033-merge-toplevel.sh
new file mode 100755
index 0000000..d314599
--- /dev/null
+++ b/t/t3033-merge-toplevel.sh
@@ -0,0 +1,152 @@
+#!/bin/sh
+
+test_description='"git merge" top-level frontend'
+
+. ./test-lib.sh
+
+t3033_reset () {
+ git checkout -B master two &&
+ git branch -f left three &&
+ git branch -f right four
+}
+
+test_expect_success setup '
+ test_commit one &&
+ git branch left &&
+ git branch right &&
+ test_commit two &&
+ git checkout left &&
+ test_commit three &&
+ git checkout right &&
+ test_commit four &&
+ git checkout --orphan newroot &&
+ test_commit five &&
+ git checkout master
+'
+
+# Local branches
+
+test_expect_success 'merge an octopus into void' '
+ t3033_reset &&
+ git checkout --orphan test &&
+ git rm -fr . &&
+ test_must_fail git merge left right &&
+ test_must_fail git rev-parse --verify HEAD &&
+ git diff --quiet &&
+ test_must_fail git rev-parse HEAD
+'
+
+test_expect_success 'merge an octopus, fast-forward (ff)' '
+ t3033_reset &&
+ git reset --hard one &&
+ git merge left right &&
+ # one is ancestor of three (left) and four (right)
+ test_must_fail git rev-parse --verify HEAD^3 &&
+ git rev-parse HEAD^1 HEAD^2 | sort >actual &&
+ git rev-parse three four | sort >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'merge octopus, non-fast-forward (ff)' '
+ t3033_reset &&
+ git reset --hard one &&
+ git merge --no-ff left right &&
+ # one is ancestor of three (left) and four (right)
+ test_must_fail git rev-parse --verify HEAD^4 &&
+ git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
+ git rev-parse one three four | sort >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'merge octopus, fast-forward (does not ff)' '
+ t3033_reset &&
+ git merge left right &&
+ # two (master) is not an ancestor of three (left) and four (right)
+ test_must_fail git rev-parse --verify HEAD^4 &&
+ git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
+ git rev-parse two three four | sort >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'merge octopus, non-fast-forward' '
+ t3033_reset &&
+ git merge --no-ff left right &&
+ test_must_fail git rev-parse --verify HEAD^4 &&
+ git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
+ git rev-parse two three four | sort >expect &&
+ test_cmp expect actual
+'
+
+# The same set with FETCH_HEAD
+
+test_expect_success 'merge FETCH_HEAD octopus into void' '
+ t3033_reset &&
+ git checkout --orphan test &&
+ git rm -fr . &&
+ git fetch . left right &&
+ test_must_fail git merge FETCH_HEAD &&
+ test_must_fail git rev-parse --verify HEAD &&
+ git diff --quiet &&
+ test_must_fail git rev-parse HEAD
+'
+
+test_expect_success 'merge FETCH_HEAD octopus fast-forward (ff)' '
+ t3033_reset &&
+ git reset --hard one &&
+ git fetch . left right &&
+ git merge FETCH_HEAD &&
+ # one is ancestor of three (left) and four (right)
+ test_must_fail git rev-parse --verify HEAD^3 &&
+ git rev-parse HEAD^1 HEAD^2 | sort >actual &&
+ git rev-parse three four | sort >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'merge FETCH_HEAD octopus non-fast-forward (ff)' '
+ t3033_reset &&
+ git reset --hard one &&
+ git fetch . left right &&
+ git merge --no-ff FETCH_HEAD &&
+ # one is ancestor of three (left) and four (right)
+ test_must_fail git rev-parse --verify HEAD^4 &&
+ git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
+ git rev-parse one three four | sort >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'merge FETCH_HEAD octopus fast-forward (does not ff)' '
+ t3033_reset &&
+ git fetch . left right &&
+ git merge FETCH_HEAD &&
+ # two (master) is not an ancestor of three (left) and four (right)
+ test_must_fail git rev-parse --verify HEAD^4 &&
+ git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
+ git rev-parse two three four | sort >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'merge FETCH_HEAD octopus non-fast-forward' '
+ t3033_reset &&
+ git fetch . left right &&
+ git merge --no-ff FETCH_HEAD &&
+ test_must_fail git rev-parse --verify HEAD^4 &&
+ git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
+ git rev-parse two three four | sort >expect &&
+ test_cmp expect actual
+'
+
+# two-project merge
+test_expect_success 'refuse two-project merge by default' '
+ t3033_reset &&
+ git reset --hard four &&
+ test_must_fail git merge five
+'
+
+test_expect_success 'two-project merge with --allow-unrelated-histories' '
+ t3033_reset &&
+ git reset --hard four &&
+ git merge --allow-unrelated-histories five &&
+ git diff --exit-code five
+'
+
+test_done
diff --git a/t/t3034-merge-recursive-rename-options.sh b/t/t3034-merge-recursive-rename-options.sh
new file mode 100755
index 0000000..b9c4028
--- /dev/null
+++ b/t/t3034-merge-recursive-rename-options.sh
@@ -0,0 +1,312 @@
+#!/bin/sh
+
+test_description='merge-recursive rename options
+
+Test rename detection by examining rename/delete conflicts.
+
+* (HEAD -> rename) rename
+| * (master) delete
+|/
+* base
+
+git diff --name-status base master
+D 0-old
+D 1-old
+D 2-old
+D 3-old
+
+git diff --name-status -M01 base rename
+R025 0-old 0-new
+R050 1-old 1-new
+R075 2-old 2-new
+R100 3-old 3-new
+
+Actual similarity indices are parsed from diff output. We rely on the fact that
+they are rounded down (see, e.g., Documentation/diff-generate-patch.txt, which
+mentions this in a different context).
+'
+
+. ./test-lib.sh
+
+get_expected_stages () {
+ git checkout rename -- $1-new &&
+ git ls-files --stage $1-new >expected-stages-undetected-$1 &&
+ sed "s/ 0 / 2 /" <expected-stages-undetected-$1 \
+ >expected-stages-detected-$1 &&
+ git read-tree -u --reset HEAD
+}
+
+rename_detected () {
+ git ls-files --stage $1-old $1-new >stages-actual-$1 &&
+ test_cmp expected-stages-detected-$1 stages-actual-$1
+}
+
+rename_undetected () {
+ git ls-files --stage $1-old $1-new >stages-actual-$1 &&
+ test_cmp expected-stages-undetected-$1 stages-actual-$1
+}
+
+check_common () {
+ git ls-files --stage >stages-actual &&
+ test_line_count = 4 stages-actual
+}
+
+check_threshold_0 () {
+ check_common &&
+ rename_detected 0 &&
+ rename_detected 1 &&
+ rename_detected 2 &&
+ rename_detected 3
+}
+
+check_threshold_1 () {
+ check_common &&
+ rename_undetected 0 &&
+ rename_detected 1 &&
+ rename_detected 2 &&
+ rename_detected 3
+}
+
+check_threshold_2 () {
+ check_common &&
+ rename_undetected 0 &&
+ rename_undetected 1 &&
+ rename_detected 2 &&
+ rename_detected 3
+}
+
+check_exact_renames () {
+ check_common &&
+ rename_undetected 0 &&
+ rename_undetected 1 &&
+ rename_undetected 2 &&
+ rename_detected 3
+}
+
+check_no_renames () {
+ check_common &&
+ rename_undetected 0 &&
+ rename_undetected 1 &&
+ rename_undetected 2 &&
+ rename_undetected 3
+}
+
+test_expect_success 'setup repo' '
+ cat <<-\EOF >3-old &&
+ 33a
+ 33b
+ 33c
+ 33d
+ EOF
+ sed s/33/22/ <3-old >2-old &&
+ sed s/33/11/ <3-old >1-old &&
+ sed s/33/00/ <3-old >0-old &&
+ git add [0-3]-old &&
+ git commit -m base &&
+ git rm [0-3]-old &&
+ git commit -m delete &&
+ git checkout -b rename HEAD^ &&
+ cp 3-old 3-new &&
+ sed 1,1s/./x/ <2-old >2-new &&
+ sed 1,2s/./x/ <1-old >1-new &&
+ sed 1,3s/./x/ <0-old >0-new &&
+ git add [0-3]-new &&
+ git rm [0-3]-old &&
+ git commit -m rename &&
+ get_expected_stages 0 &&
+ get_expected_stages 1 &&
+ get_expected_stages 2 &&
+ get_expected_stages 3 &&
+ check_50="false" &&
+ tail="HEAD^ -- HEAD master"
+'
+
+test_expect_success 'setup thresholds' '
+ git diff --name-status -M01 HEAD^ HEAD >diff-output &&
+ test_debug "cat diff-output" &&
+ test_line_count = 4 diff-output &&
+ grep "R[0-9][0-9][0-9] \([0-3]\)-old \1-new" diff-output \
+ >grep-output &&
+ test_cmp diff-output grep-output &&
+ th0=$(sed -n "s/R\(...\) 0-old 0-new/\1/p" <diff-output) &&
+ th1=$(sed -n "s/R\(...\) 1-old 1-new/\1/p" <diff-output) &&
+ th2=$(sed -n "s/R\(...\) 2-old 2-new/\1/p" <diff-output) &&
+ th3=$(sed -n "s/R\(...\) 3-old 3-new/\1/p" <diff-output) &&
+ test "$th0" -lt "$th1" &&
+ test "$th1" -lt "$th2" &&
+ test "$th2" -lt "$th3" &&
+ test "$th3" = 100 &&
+ if test 50 -le "$th0"
+ then
+ check_50=check_threshold_0
+ elif test 50 -le "$th1"
+ then
+ check_50=check_threshold_1
+ elif test 50 -le "$th2"
+ then
+ check_50=check_threshold_2
+ fi &&
+ th0="$th0%" &&
+ th1="$th1%" &&
+ th2="$th2%" &&
+ th3="$th3%"
+'
+
+test_expect_success 'assumption for tests: rename detection with diff' '
+ git diff --name-status -M$th0 --diff-filter=R HEAD^ HEAD \
+ >diff-output-0 &&
+ git diff --name-status -M$th1 --diff-filter=R HEAD^ HEAD \
+ >diff-output-1 &&
+ git diff --name-status -M$th2 --diff-filter=R HEAD^ HEAD \
+ >diff-output-2 &&
+ git diff --name-status -M100% --diff-filter=R HEAD^ HEAD \
+ >diff-output-3 &&
+ test_line_count = 4 diff-output-0 &&
+ test_line_count = 3 diff-output-1 &&
+ test_line_count = 2 diff-output-2 &&
+ test_line_count = 1 diff-output-3
+'
+
+test_expect_success 'default similarity threshold is 50%' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive $tail &&
+ $check_50
+'
+
+test_expect_success 'low rename threshold' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive --find-renames=$th0 $tail &&
+ check_threshold_0
+'
+
+test_expect_success 'medium rename threshold' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive --find-renames=$th1 $tail &&
+ check_threshold_1
+'
+
+test_expect_success 'high rename threshold' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive --find-renames=$th2 $tail &&
+ check_threshold_2
+'
+
+test_expect_success 'exact renames only' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive --find-renames=100% $tail &&
+ check_exact_renames
+'
+
+test_expect_success 'rename threshold is truncated' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive --find-renames=200% $tail &&
+ check_exact_renames
+'
+
+test_expect_success 'disabled rename detection' '
+ git read-tree --reset -u HEAD &&
+ git merge-recursive --no-renames $tail &&
+ check_no_renames
+'
+
+test_expect_success 'last wins in --find-renames=<m> --find-renames=<n>' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive \
+ --find-renames=$th0 --find-renames=$th2 $tail &&
+ check_threshold_2
+'
+
+test_expect_success '--find-renames resets threshold' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive \
+ --find-renames=$th0 --find-renames $tail &&
+ $check_50
+'
+
+test_expect_success 'last wins in --no-renames --find-renames' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive --no-renames --find-renames $tail &&
+ $check_50
+'
+
+test_expect_success 'last wins in --find-renames --no-renames' '
+ git read-tree --reset -u HEAD &&
+ git merge-recursive --find-renames --no-renames $tail &&
+ check_no_renames
+'
+
+test_expect_success 'assumption for further tests: trivial merge succeeds' '
+ git read-tree --reset -u HEAD &&
+ git merge-recursive HEAD -- HEAD HEAD &&
+ git diff --quiet --cached &&
+ git merge-recursive --find-renames=$th0 HEAD -- HEAD HEAD &&
+ git diff --quiet --cached &&
+ git merge-recursive --find-renames=$th2 HEAD -- HEAD HEAD &&
+ git diff --quiet --cached &&
+ git merge-recursive --find-renames=100% HEAD -- HEAD HEAD &&
+ git diff --quiet --cached &&
+ git merge-recursive --no-renames HEAD -- HEAD HEAD &&
+ git diff --quiet --cached
+'
+
+test_expect_success '--find-renames rejects negative argument' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive --find-renames=-25 \
+ HEAD -- HEAD HEAD &&
+ git diff --quiet --cached
+'
+
+test_expect_success '--find-renames rejects non-numbers' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive --find-renames=0xf \
+ HEAD -- HEAD HEAD &&
+ git diff --quiet --cached
+'
+
+test_expect_success 'rename-threshold=<n> is a synonym for find-renames=<n>' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive --rename-threshold=$th0 $tail &&
+ check_threshold_0
+'
+
+test_expect_success 'last wins in --no-renames --rename-threshold=<n>' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive --no-renames --rename-threshold=$th0 $tail &&
+ check_threshold_0
+'
+
+test_expect_success 'last wins in --rename-threshold=<n> --no-renames' '
+ git read-tree --reset -u HEAD &&
+ git merge-recursive --rename-threshold=$th0 --no-renames $tail &&
+ check_no_renames
+'
+
+test_expect_success '--rename-threshold=<n> rejects negative argument' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive --rename-threshold=-25 \
+ HEAD -- HEAD HEAD &&
+ git diff --quiet --cached
+'
+
+test_expect_success '--rename-threshold=<n> rejects non-numbers' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive --rename-threshold=0xf \
+ HEAD -- HEAD HEAD &&
+ git diff --quiet --cached
+'
+
+test_expect_success 'last wins in --rename-threshold=<m> --find-renames=<n>' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive \
+ --rename-threshold=$th0 --find-renames=$th2 $tail &&
+ check_threshold_2
+'
+
+test_expect_success 'last wins in --find-renames=<m> --rename-threshold=<n>' '
+ git read-tree --reset -u HEAD &&
+ test_must_fail git merge-recursive \
+ --find-renames=$th2 --rename-threshold=$th0 $tail &&
+ check_threshold_0
+'
+
+test_done
diff --git a/t/t3040-subprojects-basic.sh b/t/t3040-subprojects-basic.sh
index f6973e9..0a4ff6d 100755
--- a/t/t3040-subprojects-basic.sh
+++ b/t/t3040-subprojects-basic.sh
@@ -3,81 +3,81 @@
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
- }'
+test_expect_success 'setup: create superproject' '
+ : >Makefile &&
+ git add Makefile &&
+ git commit -m "Superproject created"
+'
+
+test_expect_success 'setup: 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 &&
+ git branch save HEAD &&
+ cat >expected <<-\EOF &&
+ :000000 160000 00000... A sub1
+ :000000 160000 00000... A sub2
+ EOF
+ test_cmp expected current
+'
+
+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" ) &&
+ test_expect_code 1 git diff-files --exit-code
+'
+
+test_expect_success 'check if a changed subproject HEAD can be committed' '
+ git commit -q -a -m "sub1 changed" &&
+ test_expect_code 1 git diff-tree --exit-code HEAD^ HEAD
+'
+
+test_expect_success 'check if diff-index works for subproject elements' '
+ test_expect_code 1 git diff-index --exit-code --cached save -- sub1
+'
+
+test_expect_success 'check if diff-tree works for subproject elements' '
+ test_expect_code 1 git diff-tree --exit-code HEAD^ HEAD -- sub1
+'
+
+test_expect_success 'check if git diff works for subproject elements' '
+ test_expect_code 1 git diff --exit-code HEAD^ HEAD
+'
+
+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" &&
+ test_expect_code 1 git diff -M --name-status --exit-code HEAD^ HEAD
+'
# 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'
+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
diff --git a/t/t3050-subprojects-fetch.sh b/t/t3050-subprojects-fetch.sh
index 4261e96..2f5f41a 100755
--- a/t/t3050-subprojects-fetch.sh
+++ b/t/t3050-subprojects-fetch.sh
@@ -10,10 +10,10 @@ test_expect_success setup '
cd sub &&
git init &&
>subfile &&
- git add subfile
+ git add subfile &&
git commit -m "subproject commit #1"
) &&
- >mainfile
+ >mainfile &&
git add sub mainfile &&
test_tick &&
git commit -m "superproject commit #1"
diff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh
index 3ce501b..44f378c 100755
--- a/t/t3060-ls-files-with-tree.sh
+++ b/t/t3060-ls-files-with-tree.sh
@@ -18,22 +18,16 @@ test_expect_success setup '
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" &&
+ echo file-$num >>expected ||
+ return 1
+ done
+ done &&
git add . &&
git commit -m "add a bunch of files" &&
@@ -53,17 +47,15 @@ test_expect_success setup '
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
-
+ # We have to run from a sub-directory to trigger prune_path
+ # Then we finally get to run our --with-tree test
+ (
+ cd sub &&
+ 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'
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
new file mode 100755
index 0000000..ef509df
--- /dev/null
+++ b/t/t3070-wildmatch.sh
@@ -0,0 +1,268 @@
+#!/bin/sh
+
+test_description='wildmatch tests'
+
+. ./test-lib.sh
+
+match() {
+ if [ $1 = 1 ]; then
+ test_expect_success "wildmatch: match '$3' '$4'" "
+ test-wildmatch wildmatch '$3' '$4'
+ "
+ else
+ test_expect_success "wildmatch: no match '$3' '$4'" "
+ ! test-wildmatch wildmatch '$3' '$4'
+ "
+ fi
+}
+
+imatch() {
+ if [ $1 = 1 ]; then
+ test_expect_success "iwildmatch: match '$2' '$3'" "
+ test-wildmatch iwildmatch '$2' '$3'
+ "
+ else
+ test_expect_success "iwildmatch: no match '$2' '$3'" "
+ ! test-wildmatch iwildmatch '$2' '$3'
+ "
+ fi
+}
+
+pathmatch() {
+ if [ $1 = 1 ]; then
+ test_expect_success "pathmatch: match '$2' '$3'" "
+ test-wildmatch pathmatch '$2' '$3'
+ "
+ else
+ test_expect_success "pathmatch: no match '$2' '$3'" "
+ ! test-wildmatch pathmatch '$2' '$3'
+ "
+ fi
+}
+
+# Basic wildmat features
+match 1 1 foo foo
+match 0 0 foo bar
+match 1 1 '' ""
+match 1 1 foo '???'
+match 0 0 foo '??'
+match 1 1 foo '*'
+match 1 1 foo 'f*'
+match 0 0 foo '*f'
+match 1 1 foo '*foo*'
+match 1 1 foobar '*ob*a*r*'
+match 1 1 aaaaaaabababab '*ab'
+match 1 1 'foo*' 'foo\*'
+match 0 0 foobar 'foo\*bar'
+match 1 1 'f\oo' 'f\\oo'
+match 1 1 ball '*[al]?'
+match 0 0 ten '[ten]'
+match 0 1 ten '**[!te]'
+match 0 0 ten '**[!ten]'
+match 1 1 ten 't[a-g]n'
+match 0 0 ten 't[!a-g]n'
+match 1 1 ton 't[!a-g]n'
+match 1 1 ton 't[^a-g]n'
+match 1 x 'a]b' 'a[]]b'
+match 1 x a-b 'a[]-]b'
+match 1 x 'a]b' 'a[]-]b'
+match 0 x aab 'a[]-]b'
+match 1 x aab 'a[]a-]b'
+match 1 1 ']' ']'
+
+# Extended slash-matching features
+match 0 0 'foo/baz/bar' 'foo*bar'
+match 0 0 'foo/baz/bar' 'foo**bar'
+match 0 1 'foobazbar' 'foo**bar'
+match 1 1 'foo/baz/bar' 'foo/**/bar'
+match 1 0 'foo/baz/bar' 'foo/**/**/bar'
+match 1 0 'foo/b/a/z/bar' 'foo/**/bar'
+match 1 0 'foo/b/a/z/bar' 'foo/**/**/bar'
+match 1 0 'foo/bar' 'foo/**/bar'
+match 1 0 'foo/bar' 'foo/**/**/bar'
+match 0 0 'foo/bar' 'foo?bar'
+match 0 0 'foo/bar' 'foo[/]bar'
+match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'
+match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'
+match 1 0 'foo' '**/foo'
+match 1 x 'XXX/foo' '**/foo'
+match 1 0 'bar/baz/foo' '**/foo'
+match 0 0 'bar/baz/foo' '*/foo'
+match 0 0 'foo/bar/baz' '**/bar*'
+match 1 0 'deep/foo/bar/baz' '**/bar/*'
+match 0 0 'deep/foo/bar/baz/' '**/bar/*'
+match 1 0 'deep/foo/bar/baz/' '**/bar/**'
+match 0 0 'deep/foo/bar' '**/bar/*'
+match 1 0 'deep/foo/bar/' '**/bar/**'
+match 0 0 'foo/bar/baz' '**/bar**'
+match 1 0 'foo/bar/baz/x' '*/bar/**'
+match 0 0 'deep/foo/bar/baz/x' '*/bar/**'
+match 1 0 'deep/foo/bar/baz/x' '**/bar/*/*'
+
+# Various additional tests
+match 0 0 'acrt' 'a[c-c]st'
+match 1 1 'acrt' 'a[c-c]rt'
+match 0 0 ']' '[!]-]'
+match 1 x 'a' '[!]-]'
+match 0 0 '' '\'
+match 0 x '\' '\'
+match 0 x 'XXX/\' '*/\'
+match 1 x 'XXX/\' '*/\\'
+match 1 1 'foo' 'foo'
+match 1 1 '@foo' '@foo'
+match 0 0 'foo' '@foo'
+match 1 1 '[ab]' '\[ab]'
+match 1 1 '[ab]' '[[]ab]'
+match 1 x '[ab]' '[[:]ab]'
+match 0 x '[ab]' '[[::]ab]'
+match 1 x '[ab]' '[[:digit]ab]'
+match 1 x '[ab]' '[\[:]ab]'
+match 1 1 '?a?b' '\??\?b'
+match 1 1 'abc' '\a\b\c'
+match 0 0 'foo' ''
+match 1 0 'foo/bar/baz/to' '**/t[o]'
+
+# Character class tests
+match 1 x 'a1B' '[[:alpha:]][[:digit:]][[:upper:]]'
+match 0 x 'a' '[[:digit:][:upper:][:space:]]'
+match 1 x 'A' '[[:digit:][:upper:][:space:]]'
+match 1 x '1' '[[:digit:][:upper:][:space:]]'
+match 0 x '1' '[[:digit:][:upper:][:spaci:]]'
+match 1 x ' ' '[[:digit:][:upper:][:space:]]'
+match 0 x '.' '[[:digit:][:upper:][:space:]]'
+match 1 x '.' '[[:digit:][:punct:][:space:]]'
+match 1 x '5' '[[:xdigit:]]'
+match 1 x 'f' '[[:xdigit:]]'
+match 1 x 'D' '[[:xdigit:]]'
+match 1 x '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]'
+match 1 x '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]'
+match 1 x '.' '[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]'
+match 1 x '5' '[a-c[:digit:]x-z]'
+match 1 x 'b' '[a-c[:digit:]x-z]'
+match 1 x 'y' '[a-c[:digit:]x-z]'
+match 0 x 'q' '[a-c[:digit:]x-z]'
+
+# Additional tests, including some malformed wildmats
+match 1 x ']' '[\\-^]'
+match 0 0 '[' '[\\-^]'
+match 1 x '-' '[\-_]'
+match 1 x ']' '[\]]'
+match 0 0 '\]' '[\]]'
+match 0 0 '\' '[\]]'
+match 0 0 'ab' 'a[]b'
+match 0 x 'a[]b' 'a[]b'
+match 0 x 'ab[' 'ab['
+match 0 0 'ab' '[!'
+match 0 0 'ab' '[-'
+match 1 1 '-' '[-]'
+match 0 0 '-' '[a-'
+match 0 0 '-' '[!a-'
+match 1 x '-' '[--A]'
+match 1 x '5' '[--A]'
+match 1 1 ' ' '[ --]'
+match 1 1 '$' '[ --]'
+match 1 1 '-' '[ --]'
+match 0 0 '0' '[ --]'
+match 1 x '-' '[---]'
+match 1 x '-' '[------]'
+match 0 0 'j' '[a-e-n]'
+match 1 x '-' '[a-e-n]'
+match 1 x 'a' '[!------]'
+match 0 0 '[' '[]-a]'
+match 1 x '^' '[]-a]'
+match 0 0 '^' '[!]-a]'
+match 1 x '[' '[!]-a]'
+match 1 1 '^' '[a^bc]'
+match 1 x '-b]' '[a-]b]'
+match 0 0 '\' '[\]'
+match 1 1 '\' '[\\]'
+match 0 0 '\' '[!\\]'
+match 1 1 'G' '[A-\\]'
+match 0 0 'aaabbb' 'b*a'
+match 0 0 'aabcaa' '*ba*'
+match 1 1 ',' '[,]'
+match 1 1 ',' '[\\,]'
+match 1 1 '\' '[\\,]'
+match 1 1 '-' '[,-.]'
+match 0 0 '+' '[,-.]'
+match 0 0 '-.]' '[,-.]'
+match 1 1 '2' '[\1-\3]'
+match 1 1 '3' '[\1-\3]'
+match 0 0 '4' '[\1-\3]'
+match 1 1 '\' '[[-\]]'
+match 1 1 '[' '[[-\]]'
+match 1 1 ']' '[[-\]]'
+match 0 0 '-' '[[-\]]'
+
+# Test recursion and the abort code (use "wildtest -i" to see iteration counts)
+match 1 1 '-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'
+match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'
+match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'
+match 1 1 'XXX/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*'
+match 0 0 'XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*'
+match 1 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt' '**/*a*b*g*n*t'
+match 0 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz' '**/*a*b*g*n*t'
+match 0 x foo '*/*/*'
+match 0 x foo/bar '*/*/*'
+match 1 x foo/bba/arr '*/*/*'
+match 0 x foo/bb/aa/rr '*/*/*'
+match 1 x foo/bb/aa/rr '**/**/**'
+match 1 x abcXdefXghi '*X*i'
+match 0 x ab/cXd/efXg/hi '*X*i'
+match 1 x ab/cXd/efXg/hi '*/*X*/*/*i'
+match 1 x ab/cXd/efXg/hi '**/*X*/**/*i'
+
+pathmatch 1 foo foo
+pathmatch 0 foo fo
+pathmatch 1 foo/bar foo/bar
+pathmatch 1 foo/bar 'foo/*'
+pathmatch 1 foo/bba/arr 'foo/*'
+pathmatch 1 foo/bba/arr 'foo/**'
+pathmatch 1 foo/bba/arr 'foo*'
+pathmatch 1 foo/bba/arr 'foo**'
+pathmatch 1 foo/bba/arr 'foo/*arr'
+pathmatch 1 foo/bba/arr 'foo/**arr'
+pathmatch 0 foo/bba/arr 'foo/*z'
+pathmatch 0 foo/bba/arr 'foo/**z'
+pathmatch 1 foo/bar 'foo?bar'
+pathmatch 1 foo/bar 'foo[/]bar'
+pathmatch 0 foo '*/*/*'
+pathmatch 0 foo/bar '*/*/*'
+pathmatch 1 foo/bba/arr '*/*/*'
+pathmatch 1 foo/bb/aa/rr '*/*/*'
+pathmatch 1 abcXdefXghi '*X*i'
+pathmatch 1 ab/cXd/efXg/hi '*/*X*/*/*i'
+pathmatch 1 ab/cXd/efXg/hi '*Xg*i'
+
+# Case-sensitivy features
+match 0 x 'a' '[A-Z]'
+match 1 x 'A' '[A-Z]'
+match 0 x 'A' '[a-z]'
+match 1 x 'a' '[a-z]'
+match 0 x 'a' '[[:upper:]]'
+match 1 x 'A' '[[:upper:]]'
+match 0 x 'A' '[[:lower:]]'
+match 1 x 'a' '[[:lower:]]'
+match 0 x 'A' '[B-Za]'
+match 1 x 'a' '[B-Za]'
+match 0 x 'A' '[B-a]'
+match 1 x 'a' '[B-a]'
+match 0 x 'z' '[Z-y]'
+match 1 x 'Z' '[Z-y]'
+
+imatch 1 'a' '[A-Z]'
+imatch 1 'A' '[A-Z]'
+imatch 1 'A' '[a-z]'
+imatch 1 'a' '[a-z]'
+imatch 1 'a' '[[:upper:]]'
+imatch 1 'A' '[[:upper:]]'
+imatch 1 'A' '[[:lower:]]'
+imatch 1 'a' '[[:lower:]]'
+imatch 1 'A' '[B-Za]'
+imatch 1 'a' '[B-Za]'
+imatch 1 'A' '[B-a]'
+imatch 1 'a' '[B-a]'
+imatch 1 'z' '[Z-y]'
+imatch 1 'Z' '[Z-y]'
+
+test_done
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
index eee0d34..325114f 100755
--- a/t/t3100-ls-tree-restrict.sh
+++ b/t/t3100-ls-tree-restrict.sh
@@ -22,25 +22,13 @@ test_expect_success \
'setup' \
'mkdir path2 path2/baz &&
echo Hi >path0 &&
- if test_have_prereq SYMLINKS
- then
- ln -s path0 path1 &&
- ln -s ../path1 path2/bazbo
- make_expected () {
- cat >expected
- }
- else
- printf path0 > path1 &&
- printf ../path1 > path2/bazbo
- make_expected () {
- sed -e "s/120000 /100644 /" >expected
- }
- fi &&
+ test_ln_s_add path0 path1 &&
+ test_ln_s_add ../path1 path2/bazbo &&
echo Lo >path2/foo &&
echo Mi >path2/baz/b &&
find path? \( -type f -o -type l \) -print |
xargs git update-index --add &&
- tree=`git write-tree` &&
+ tree=$(git write-tree) &&
echo $tree'
test_output () {
@@ -51,7 +39,7 @@ test_output () {
test_expect_success \
'ls-tree plain' \
'git ls-tree $tree >current &&
- make_expected <<\EOF &&
+ cat >expected <<\EOF &&
100644 blob X path0
120000 blob X path1
040000 tree X path2
@@ -61,7 +49,7 @@ EOF
test_expect_success \
'ls-tree recursive' \
'git ls-tree -r $tree >current &&
- make_expected <<\EOF &&
+ cat >expected <<\EOF &&
100644 blob X path0
120000 blob X path1
100644 blob X path2/baz/b
@@ -73,7 +61,7 @@ EOF
test_expect_success \
'ls-tree recursive with -t' \
'git ls-tree -r -t $tree >current &&
- make_expected <<\EOF &&
+ cat >expected <<\EOF &&
100644 blob X path0
120000 blob X path1
040000 tree X path2
@@ -87,7 +75,7 @@ EOF
test_expect_success \
'ls-tree recursive with -d' \
'git ls-tree -r -d $tree >current &&
- make_expected <<\EOF &&
+ cat >expected <<\EOF &&
040000 tree X path2
040000 tree X path2/baz
EOF
@@ -96,7 +84,7 @@ EOF
test_expect_success \
'ls-tree filtered with path' \
'git ls-tree $tree path >current &&
- make_expected <<\EOF &&
+ cat >expected <<\EOF &&
EOF
test_output'
@@ -106,7 +94,7 @@ EOF
test_expect_success \
'ls-tree filtered with path1 path0' \
'git ls-tree $tree path1 path0 >current &&
- make_expected <<\EOF &&
+ cat >expected <<\EOF &&
100644 blob X path0
120000 blob X path1
EOF
@@ -115,7 +103,7 @@ EOF
test_expect_success \
'ls-tree filtered with path0/' \
'git ls-tree $tree path0/ >current &&
- make_expected <<\EOF &&
+ cat >expected <<\EOF &&
EOF
test_output'
@@ -124,7 +112,7 @@ EOF
test_expect_success \
'ls-tree filtered with path2' \
'git ls-tree $tree path2 >current &&
- make_expected <<\EOF &&
+ cat >expected <<\EOF &&
040000 tree X path2
EOF
test_output'
@@ -133,7 +121,7 @@ EOF
test_expect_success \
'ls-tree filtered with path2/' \
'git ls-tree $tree path2/ >current &&
- make_expected <<\EOF &&
+ cat >expected <<\EOF &&
040000 tree X path2/baz
120000 blob X path2/bazbo
100644 blob X path2/foo
@@ -145,7 +133,7 @@ EOF
test_expect_success \
'ls-tree filtered with path2/baz' \
'git ls-tree $tree path2/baz >current &&
- make_expected <<\EOF &&
+ cat >expected <<\EOF &&
040000 tree X path2/baz
EOF
test_output'
@@ -153,16 +141,25 @@ EOF
test_expect_success \
'ls-tree filtered with path2/bak' \
'git ls-tree $tree path2/bak >current &&
- make_expected <<\EOF &&
+ cat >expected <<\EOF &&
EOF
test_output'
test_expect_success \
'ls-tree -t filtered with path2/bak' \
'git ls-tree -t $tree path2/bak >current &&
- make_expected <<\EOF &&
+ cat >expected <<\EOF &&
040000 tree X path2
EOF
test_output'
+test_expect_success \
+ 'ls-tree with one path a prefix of the other' \
+ 'git ls-tree $tree path2/baz path2/bazbo >current &&
+ cat >expected <<\EOF &&
+040000 tree X path2/baz
+120000 blob X path2/bazbo
+EOF
+ test_output'
+
test_done
diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh
index 06654c6..425d858 100755
--- a/t/t3101-ls-tree-dirname.sh
+++ b/t/t3101-ls-tree-dirname.sh
@@ -21,33 +21,32 @@ 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_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
+ 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 &&
+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
@@ -55,13 +54,13 @@ test_expect_success \
040000 tree X path2
040000 tree X path3
EOF
- test_output'
+ test_output
+'
# Recursive does not show tree nodes anymore...
-test_expect_success \
- 'ls-tree recursive' \
- 'git ls-tree -r $tree >current &&
- cat >expected <<\EOF &&
+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
@@ -70,68 +69,71 @@ test_expect_success \
100644 blob X path3/1.txt
100644 blob X path3/2.txt
EOF
- test_output'
+ test_output
+'
-test_expect_success \
- 'ls-tree filter 1.txt' \
- 'git ls-tree $tree 1.txt >current &&
- cat >expected <<\EOF &&
+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_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 &&
+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_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 &&
+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'
+ 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 &&
+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'
+ 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 &&
+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_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 missing files and extra slashes' '
+ git ls-tree $tree 1.txt/ abc.txt \
+ path3//23.txt path3/2.txt/// >current &&
+ >expected &&
+ test_output
+'
test_expect_success 'ls-tree filter is leading path match' '
git ls-tree $tree pa path3/a >current &&
@@ -198,7 +200,7 @@ EOF
'
test_expect_success 'ls-tree --name-only' '
- git ls-tree --name-only $tree >current
+ git ls-tree --name-only $tree >current &&
cat >expected <<\EOF &&
1.txt
2.txt
@@ -211,7 +213,7 @@ EOF
'
test_expect_success 'ls-tree --name-only -r' '
- git ls-tree --name-only -r $tree >current
+ git ls-tree --name-only -r $tree >current &&
cat >expected <<\EOF &&
1.txt
2.txt
diff --git a/t/t3102-ls-tree-wildcards.sh b/t/t3102-ls-tree-wildcards.sh
new file mode 100755
index 0000000..4d4b02e
--- /dev/null
+++ b/t/t3102-ls-tree-wildcards.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='ls-tree with(out) globs'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ mkdir a aa "a[a]" &&
+ touch a/one aa/two "a[a]/three" &&
+ git add a/one aa/two "a[a]/three" &&
+ git commit -m test
+'
+
+test_expect_success 'ls-tree a[a] matches literally' '
+ cat >expect <<-\EOF &&
+ 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 a[a]/three
+ EOF
+ git ls-tree -r HEAD "a[a]" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'ls-tree outside prefix' '
+ cat >expect <<-\EOF &&
+ 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 ../a[a]/three
+ EOF
+ ( cd aa && git ls-tree -r HEAD "../a[a]"; ) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'ls-tree does not yet support negated pathspec' '
+ git ls-files ":(exclude)a" "a*" >expect &&
+ git ls-tree --name-only -r HEAD ":(exclude)a" "a*" >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh
new file mode 100755
index 0000000..09dcf04
--- /dev/null
+++ b/t/t3103-ls-tree-misc.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='
+Miscellaneous tests for git ls-tree.
+
+ 1. git ls-tree fails in presence of tree damage.
+
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ mkdir a &&
+ touch a/one &&
+ git add a/one &&
+ git commit -m test
+'
+
+test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '
+ rm -f .git/objects/5f/cffbd6e4c5c5b8d81f5e9314b20e338e3ffff5 &&
+ test_must_fail git ls-tree -r HEAD
+'
+
+test_done
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index f54a533..f3e3b6c 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -3,90 +3,278 @@
# Copyright (c) 2005 Amos Waterland
#
-test_description='git branch --foo should not create bogus branch
+test_description='git branch assorted tests'
-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 'prepare a trivial repository' '
+ echo Hello >A &&
+ git update-index --add A &&
+ git commit -m "Initial commit." &&
+ echo World >>A &&
+ git update-index --add A &&
+ git commit -m "Second commit." &&
+ HEAD=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'git branch --help should not have created a bogus branch' '
+ test_might_fail git branch --man --help </dev/null >/dev/null 2>&1 &&
+ test_path_is_missing .git/refs/heads/--help
+'
-test_expect_success \
- '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 'branch -h in broken repository' '
+ mkdir broken &&
+ (
+ cd broken &&
+ git init &&
+ >.git/refs/heads/master &&
+ test_expect_code 129 git branch -h >usage 2>&1
+ ) &&
+ test_i18ngrep "[Uu]sage" broken/usage
'
-test_expect_success \
- 'git branch abc should create a branch' \
- 'git branch abc && test -f .git/refs/heads/abc'
+test_expect_success 'git branch abc should create a branch' '
+ git branch abc && test_path_is_file .git/refs/heads/abc
+'
-test_expect_success \
- 'git branch a/b/c should create a branch' \
- 'git branch a/b/c && test -f .git/refs/heads/a/b/c'
+test_expect_success 'git branch a/b/c should create a branch' '
+ git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c
+'
+
+test_expect_success 'git branch HEAD should fail' '
+ test_must_fail git branch HEAD
+'
cat >expect <<EOF
-0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master
+$_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master
EOF
-test_expect_success \
- 'git branch -l d/e/f should create a branch and a log' \
- 'GIT_COMMITTER_DATE="2005-05-26 23:30" \
- git branch -l d/e/f &&
- test -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 -l d/e/f should create a branch and a log' '
+ GIT_COMMITTER_DATE="2005-05-26 23:30" \
+ git branch -l d/e/f &&
+ test_path_is_file .git/refs/heads/d/e/f &&
+ test_path_is_file .git/logs/refs/heads/d/e/f &&
+ test_cmp expect .git/logs/refs/heads/d/e/f
+'
+
+test_expect_success 'git branch -d d/e/f should delete a branch and a log' '
+ git branch -d d/e/f &&
+ test_path_is_missing .git/refs/heads/d/e/f &&
+ test_must_fail git reflog exists refs/heads/d/e/f
+'
+
+test_expect_success 'git branch j/k should work after branch j has been deleted' '
+ git branch j &&
+ git branch -d j &&
+ git branch j/k
+'
+
+test_expect_success 'git branch l should work after branch l/m has been deleted' '
+ git branch l/m &&
+ git branch -d l/m &&
+ git branch l
+'
+
+test_expect_success 'git branch -m dumps usage' '
+ test_expect_code 128 git branch -m 2>err &&
+ test_i18ngrep "branch name required" err
+'
+
+test_expect_success 'git branch -m m m/m should work' '
+ git branch -l m &&
+ git branch -m m m/m &&
+ git reflog exists 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 &&
+ git reflog exists refs/heads/n
+'
test_expect_success 'git branch -m o/o o should fail when o/p exists' '
git branch o/o &&
- git branch o/p &&
+ git branch o/p &&
test_must_fail git branch -m o/o o
'
+test_expect_success 'git branch -m o/q o/p should fail when o/p exists' '
+ git branch o/q &&
+ test_must_fail git branch -m o/q o/p
+'
+
+test_expect_success 'git branch -M o/q o/p should work when o/p exists' '
+ git branch -M o/q o/p
+'
+
+test_expect_success 'git branch -m -f o/q o/p should work when o/p exists' '
+ git branch o/q &&
+ git branch -m -f o/q o/p
+'
+
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
'
+test_expect_success 'git branch -M foo bar should fail when bar is checked out' '
+ git branch bar &&
+ git checkout -b foo &&
+ test_must_fail git branch -M bar foo
+'
+
+test_expect_success 'git branch -M baz bam should succeed when baz is checked out' '
+ git checkout -b baz &&
+ git branch bam &&
+ git branch -M baz bam &&
+ test $(git rev-parse --abbrev-ref HEAD) = bam
+'
+
+test_expect_success 'git branch -M baz bam should succeed when baz is checked out as linked working tree' '
+ git checkout master &&
+ git worktree add -b baz bazdir &&
+ git worktree add -f bazdir2 baz &&
+ git branch -M baz bam &&
+ test $(git -C bazdir rev-parse --abbrev-ref HEAD) = bam &&
+ test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam
+'
+
+test_expect_success 'git branch -M baz bam should succeed within a worktree in which baz is checked out' '
+ git checkout -b baz &&
+ git worktree add -f bazdir3 baz &&
+ (
+ cd bazdir3 &&
+ git branch -M baz bam &&
+ test $(git rev-parse --abbrev-ref HEAD) = bam
+ ) &&
+ test $(git rev-parse --abbrev-ref HEAD) = bam
+'
+
+test_expect_success 'git branch -M master should work when master is checked out' '
+ git checkout master &&
+ git branch -M master
+'
+
+test_expect_success 'git branch -M master master should work when master is checked out' '
+ git checkout master &&
+ git branch -M master master
+'
+
+test_expect_success 'git branch -M master2 master2 should work when master is checked out' '
+ git checkout master &&
+ git branch master2 &&
+ git branch -M master2 master2
+'
+
+test_expect_success 'git branch -v -d t should work' '
+ git branch t &&
+ test_path_is_file .git/refs/heads/t &&
+ git branch -v -d t &&
+ test_path_is_missing .git/refs/heads/t
+'
+
+test_expect_success 'git branch -v -m t s should work' '
+ git branch t &&
+ test_path_is_file .git/refs/heads/t &&
+ git branch -v -m t s &&
+ test_path_is_missing .git/refs/heads/t &&
+ test_path_is_file .git/refs/heads/s &&
+ git branch -d s
+'
+
+test_expect_success 'git branch -m -d t s should fail' '
+ git branch t &&
+ test_path_is_file .git/refs/heads/t &&
+ test_must_fail git branch -m -d t s &&
+ git branch -d t &&
+ test_path_is_missing .git/refs/heads/t
+'
+
+test_expect_success 'git branch --list -d t should fail' '
+ git branch t &&
+ test_path_is_file .git/refs/heads/t &&
+ test_must_fail git branch --list -d t &&
+ git branch -d t &&
+ test_path_is_missing .git/refs/heads/t
+'
+
+test_expect_success 'git branch --column' '
+ COLUMNS=81 git branch --column=column >actual &&
+ cat >expected <<\EOF &&
+ a/b/c bam foo l * master n o/p r
+ abc bar j/k m/m master2 o/o q
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'git branch --column with an extremely long branch name' '
+ long=this/is/a/part/of/long/branch/name &&
+ long=z$long/$long/$long/$long &&
+ test_when_finished "git branch -d $long" &&
+ git branch $long &&
+ COLUMNS=80 git branch --column=column >actual &&
+ cat >expected <<EOF &&
+ a/b/c
+ abc
+ bam
+ bar
+ foo
+ j/k
+ l
+ m/m
+* master
+ master2
+ n
+ o/o
+ o/p
+ q
+ r
+ $long
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'git branch with column.*' '
+ git config column.ui column &&
+ git config column.branch "dense" &&
+ COLUMNS=80 git branch >actual &&
+ git config --unset column.branch &&
+ git config --unset column.ui &&
+ cat >expected <<\EOF &&
+ a/b/c bam foo l * master n o/p r
+ abc bar j/k m/m master2 o/o q
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'git branch --column -v should fail' '
+ test_must_fail git branch --column -v
+'
+
+test_expect_success 'git branch -v with column.ui ignored' '
+ git config column.ui column &&
+ COLUMNS=80 git branch -v | cut -c -10 | sed "s/ *$//" >actual &&
+ git config --unset column.ui &&
+ cat >expected <<\EOF &&
+ a/b/c
+ abc
+ bam
+ bar
+ foo
+ j/k
+ l
+ m/m
+* master
+ master2
+ n
+ o/o
+ o/p
+ q
+ r
+EOF
+ test_cmp expected actual
+'
+
mv .git/config .git/config-saved
test_expect_success 'git branch -m q q2 without config should succeed' '
@@ -98,131 +286,312 @@ 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 'git branch -m s/s s should work when s/t is deleted' '
+ git branch -l s/s &&
+ git reflog exists refs/heads/s/s &&
+ git branch -l s/t &&
+ git reflog exists refs/heads/s/t &&
+ git branch -d s/t &&
+ git branch -m s/s s &&
+ git reflog exists refs/heads/s
+'
-test_expect_success 'config information was renamed, too' \
- "test $(git config branch.s.dummy) = Hello &&
- test_must_fail git config branch.s/s/dummy"
+test_expect_success 'config information was renamed, too' '
+ test $(git config branch.s.dummy) = Hello &&
+ test_must_fail git config branch.s/s/dummy
+'
-test_expect_success 'renaming a symref is not allowed' \
+test_expect_success 'deleting a symref' '
+ git branch target &&
+ git symbolic-ref refs/heads/symref refs/heads/target &&
+ echo "Deleted branch symref (was refs/heads/target)." >expect &&
+ git branch -d symref >actual &&
+ test_path_is_file .git/refs/heads/target &&
+ test_path_is_missing .git/refs/heads/symref &&
+ test_i18ncmp expect actual
'
+
+test_expect_success 'deleting a dangling symref' '
+ git symbolic-ref refs/heads/dangling-symref nowhere &&
+ test_path_is_file .git/refs/heads/dangling-symref &&
+ echo "Deleted branch dangling-symref (was nowhere)." >expect &&
+ git branch -d dangling-symref >actual &&
+ test_path_is_missing .git/refs/heads/dangling-symref &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'deleting a self-referential symref' '
+ git symbolic-ref refs/heads/self-reference refs/heads/self-reference &&
+ test_path_is_file .git/refs/heads/self-reference &&
+ echo "Deleted branch self-reference (was refs/heads/self-reference)." >expect &&
+ git branch -d self-reference >actual &&
+ test_path_is_missing .git/refs/heads/self-reference &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'renaming a symref is not allowed' '
git symbolic-ref refs/heads/master2 refs/heads/master &&
test_must_fail git branch -m master2 master3 &&
git symbolic-ref refs/heads/master2 &&
- 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^'
+ test_path_is_file .git/refs/heads/master &&
+ test_path_is_missing .git/refs/heads/master3
+'
+
+test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '
+ git branch -l u &&
+ mv .git/logs/refs/heads/u real-u &&
+ ln -s real-u .git/logs/refs/heads/u &&
+ test_must_fail git branch -m u v
+'
+
+test_expect_success 'test tracking setup via --track' '
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --track my1 local/master &&
+ test $(git config branch.my1.remote) = local &&
+ test $(git config branch.my1.merge) = refs/heads/master
+'
+
+test_expect_success 'test tracking setup (non-wildcard, matching)' '
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/master:refs/remotes/local/master &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --track my4 local/master &&
+ test $(git config branch.my4.remote) = local &&
+ test $(git config branch.my4.merge) = refs/heads/master
+'
+
+test_expect_success 'tracking setup fails on non-matching refspec' '
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git config remote.local.fetch refs/heads/s:refs/remotes/local/s &&
+ test_must_fail git branch --track my5 local/master &&
+ test_must_fail git config branch.my5.remote &&
+ test_must_fail git config branch.my5.merge
+'
+
+test_expect_success 'test tracking setup via config' '
+ git config branch.autosetupmerge true &&
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch my3 local/master &&
+ test $(git config branch.my3.remote) = local &&
+ test $(git config branch.my3.merge) = refs/heads/master
+'
+
+test_expect_success 'test overriding tracking setup via --no-track' '
+ git config branch.autosetupmerge true &&
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --no-track my2 local/master &&
+ git config branch.autosetupmerge false &&
+ ! test "$(git config branch.my2.remote)" = local &&
+ ! test "$(git config branch.my2.merge)" = refs/heads/master
+'
+
+test_expect_success 'no tracking without .fetch entries' '
+ git config branch.autosetupmerge true &&
+ git branch my6 s &&
+ git config branch.autosetupmerge false &&
+ test -z "$(git config branch.my6.remote)" &&
+ test -z "$(git config branch.my6.merge)"
+'
+
+test_expect_success 'test tracking setup via --track but deeper' '
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/o/o || git fetch local) &&
+ git branch --track my7 local/o/o &&
+ test "$(git config branch.my7.remote)" = local &&
+ test "$(git config branch.my7.merge)" = refs/heads/o/o
+'
+
+test_expect_success 'test deleting branch deletes branch config' '
+ git branch -d my7 &&
+ test -z "$(git config branch.my7.remote)" &&
+ test -z "$(git config branch.my7.merge)"
+'
+
+test_expect_success 'test deleting branch without config' '
+ git branch my7 s &&
+ sha1=$(git rev-parse my7 | cut -c 1-7) &&
+ echo "Deleted branch my7 (was $sha1)." >expect &&
+ git branch -d my7 >actual 2>&1 &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'deleting currently checked out branch fails' '
+ git worktree add -b my7 my7 &&
+ test_must_fail git -C my7 branch -d my7 &&
+ test_must_fail git branch -d my7
+'
+
+test_expect_success 'test --track without .fetch entries' '
+ git branch --track my8 &&
+ test "$(git config branch.my8.remote)" &&
+ test "$(git config branch.my8.merge)"
+'
+
+test_expect_success 'branch from non-branch HEAD w/autosetupmerge=always' '
+ git config branch.autosetupmerge always &&
+ git branch my9 HEAD^ &&
+ git config branch.autosetupmerge false
+'
+
+test_expect_success 'branch from non-branch HEAD w/--track causes failure' '
+ test_must_fail git branch --track my10 HEAD^
+'
+
+test_expect_success 'branch from tag w/--track causes failure' '
+ git tag foobar &&
+ test_must_fail git branch --track my11 foobar
+'
+
+test_expect_success '--set-upstream-to fails on multiple branches' '
+ test_must_fail git branch --set-upstream-to master a b c
+'
+
+test_expect_success '--set-upstream-to fails on detached HEAD' '
+ git checkout HEAD^{} &&
+ test_must_fail git branch --set-upstream-to master &&
+ git checkout -
+'
+
+test_expect_success '--set-upstream-to fails on a missing dst branch' '
+ test_must_fail git branch --set-upstream-to master does-not-exist
+'
+
+test_expect_success '--set-upstream-to fails on a missing src branch' '
+ test_must_fail git branch --set-upstream-to does-not-exist master
+'
+
+test_expect_success '--set-upstream-to fails on a non-ref' '
+ test_must_fail git branch --set-upstream-to HEAD^{}
+'
+
+test_expect_success '--set-upstream-to fails on locked config' '
+ test_when_finished "rm -f .git/config.lock" &&
+ >.git/config.lock &&
+ git branch locked &&
+ test_must_fail git branch --set-upstream-to locked
+'
+
+test_expect_success 'use --set-upstream-to modify HEAD' '
+ test_config branch.master.remote foo &&
+ test_config branch.master.merge foo &&
+ git branch my12 &&
+ git branch --set-upstream-to my12 &&
+ test "$(git config branch.master.remote)" = "." &&
+ test "$(git config branch.master.merge)" = "refs/heads/my12"
+'
+
+test_expect_success 'use --set-upstream-to modify a particular branch' '
+ git branch my13 &&
+ git branch --set-upstream-to master my13 &&
+ test "$(git config branch.my13.remote)" = "." &&
+ test "$(git config branch.my13.merge)" = "refs/heads/master"
+'
+
+test_expect_success '--unset-upstream should fail if given a non-existent branch' '
+ test_must_fail git branch --unset-upstream i-dont-exist
+'
+
+test_expect_success '--unset-upstream should fail if config is locked' '
+ test_when_finished "rm -f .git/config.lock" &&
+ git branch --set-upstream-to locked &&
+ >.git/config.lock &&
+ test_must_fail git branch --unset-upstream
+'
+
+test_expect_success 'test --unset-upstream on HEAD' '
+ git branch my14 &&
+ test_config branch.master.remote foo &&
+ test_config branch.master.merge foo &&
+ git branch --set-upstream-to my14 &&
+ git branch --unset-upstream &&
+ test_must_fail git config branch.master.remote &&
+ test_must_fail git config branch.master.merge &&
+ # fail for a branch without upstream set
+ test_must_fail git branch --unset-upstream
+'
+
+test_expect_success '--unset-upstream should fail on multiple branches' '
+ test_must_fail git branch --unset-upstream a b c
+'
+
+test_expect_success '--unset-upstream should fail on detached HEAD' '
+ git checkout HEAD^{} &&
+ test_must_fail git branch --unset-upstream &&
+ git checkout -
+'
+
+test_expect_success 'test --unset-upstream on a particular branch' '
+ git branch my15 &&
+ git branch --set-upstream-to master my14 &&
+ git branch --unset-upstream my14 &&
+ test_must_fail git config branch.my14.remote &&
+ test_must_fail git config branch.my14.merge
+'
+
+test_expect_success '--set-upstream shows message when creating a new branch that exists as remote-tracking' '
+ git update-ref refs/remotes/origin/master HEAD &&
+ git branch --set-upstream origin/master 2>actual &&
+ test_when_finished git update-ref -d refs/remotes/origin/master &&
+ test_when_finished git branch -d origin/master &&
+ cat >expected <<EOF &&
+The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
+
+If you wanted to make '"'master'"' track '"'origin/master'"', do this:
+
+ git branch -d origin/master
+ git branch --set-upstream-to origin/master
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success '--set-upstream with two args only shows the deprecation message' '
+ git branch --set-upstream master my13 2>actual &&
+ test_when_finished git branch --unset-upstream master &&
+ cat >expected <<EOF &&
+The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success '--set-upstream with one arg only shows the deprecation message if the branch existed' '
+ git branch --set-upstream my13 2>actual &&
+ test_when_finished git branch --unset-upstream my13 &&
+ cat >expected <<EOF &&
+The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success '--set-upstream-to notices an error to set branch as own upstream' '
+ git branch --set-upstream-to refs/heads/my13 my13 2>actual &&
+ cat >expected <<-\EOF &&
+ warning: Not setting branch my13 as its own upstream.
+ EOF
+ test_expect_code 1 git config branch.my13.remote &&
+ test_expect_code 1 git config branch.my13.merge &&
+ test_i18ncmp expected actual
+'
# 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
+$_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master
EOF
-test_expect_success \
- 'git checkout -b g/h/i -l should create a branch and a log' \
- 'GIT_COMMITTER_DATE="2005-05-26 23:30" \
- git checkout -b g/h/i -l master &&
- test -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 'git checkout -b g/h/i -l should create a branch and a log' '
+ GIT_COMMITTER_DATE="2005-05-26 23:30" \
+ git checkout -b g/h/i -l master &&
+ test_path_is_file .git/refs/heads/g/h/i &&
+ test_path_is_file .git/logs/refs/heads/g/h/i &&
+ test_cmp expect .git/logs/refs/heads/g/h/i
+'
test_expect_success 'checkout -b makes reflog by default' '
git checkout master &&
@@ -251,7 +620,7 @@ test_expect_success 'avoid ambiguous track' '
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_must_fail git branch all1 master &&
test -z "$(git config branch.all1.merge)"
'
@@ -477,6 +846,15 @@ test_expect_success 'autosetuprebase always on an untracked remote branch' '
test "z$(git config branch.myr20.rebase)" = z
'
+test_expect_success 'autosetuprebase always on detached HEAD' '
+ git config branch.autosetupmerge always &&
+ test_when_finished git checkout master &&
+ git checkout HEAD^0 &&
+ git branch my11 &&
+ test -z "$(git config branch.my11.remote)" &&
+ test -z "$(git config branch.my11.merge)"
+'
+
test_expect_success 'detect misconfigured autosetuprebase (bad value)' '
git config branch.autosetuprebase garbage &&
test_must_fail git branch
@@ -484,7 +862,7 @@ test_expect_success 'detect misconfigured autosetuprebase (bad value)' '
test_expect_success 'detect misconfigured autosetuprebase (no value)' '
git config --unset branch.autosetuprebase &&
- echo "[branch] autosetuprebase" >> .git/config &&
+ echo "[branch] autosetuprebase" >>.git/config &&
test_must_fail git branch &&
git config --unset branch.autosetuprebase
'
@@ -515,4 +893,84 @@ test_expect_success 'attempt to delete a branch merged to its base' '
test_must_fail git branch -d my10
'
+test_expect_success 'use set-upstream on the current branch' '
+ git checkout master &&
+ git --bare init myupstream.git &&
+ git push myupstream.git master:refs/heads/frotz &&
+ git remote add origin myupstream.git &&
+ git fetch &&
+ git branch --set-upstream master origin/frotz &&
+
+ test "z$(git config branch.master.remote)" = "zorigin" &&
+ test "z$(git config branch.master.merge)" = "zrefs/heads/frotz"
+
+'
+
+test_expect_success 'use --edit-description' '
+ write_script editor <<-\EOF &&
+ echo "New contents" >"$1"
+ EOF
+ EDITOR=./editor git branch --edit-description &&
+ write_script editor <<-\EOF &&
+ git stripspace -s <"$1" >"EDITOR_OUTPUT"
+ EOF
+ EDITOR=./editor git branch --edit-description &&
+ echo "New contents" >expect &&
+ test_cmp EDITOR_OUTPUT expect
+'
+
+test_expect_success 'detect typo in branch name when using --edit-description' '
+ write_script editor <<-\EOF &&
+ echo "New contents" >"$1"
+ EOF
+ test_must_fail env EDITOR=./editor git branch --edit-description no-such-branch
+'
+
+test_expect_success 'refuse --edit-description on unborn branch for now' '
+ write_script editor <<-\EOF &&
+ echo "New contents" >"$1"
+ EOF
+ git checkout --orphan unborn &&
+ test_must_fail env EDITOR=./editor git branch --edit-description
+'
+
+test_expect_success '--merged catches invalid object names' '
+ test_must_fail git branch --merged 0000000000000000000000000000000000000000
+'
+
+test_expect_success 'tracking with unexpected .fetch refspec' '
+ rm -rf a b c d &&
+ git init a &&
+ (
+ cd a &&
+ test_commit a
+ ) &&
+ git init b &&
+ (
+ cd b &&
+ test_commit b
+ ) &&
+ git init c &&
+ (
+ cd c &&
+ test_commit c &&
+ git remote add a ../a &&
+ git remote add b ../b &&
+ git fetch --all
+ ) &&
+ git init d &&
+ (
+ cd d &&
+ git remote add c ../c &&
+ git config remote.c.fetch "+refs/remotes/*:refs/remotes/*" &&
+ git fetch c &&
+ git branch --track local/a/master remotes/a/master &&
+ test "$(git config branch.local/a/master.remote)" = "c" &&
+ test "$(git config branch.local/a/master.merge)" = "refs/remotes/a/master" &&
+ git rev-parse --verify a >expect &&
+ git rev-parse --verify local/a/master >actual &&
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh
index f86f4bc..912a663 100755
--- a/t/t3201-branch-contains.sh
+++ b/t/t3201-branch-contains.sh
@@ -55,6 +55,16 @@ test_expect_success 'branch --contains=side' '
'
+test_expect_success 'branch --contains with pattern implies --list' '
+
+ git branch --contains=master master >actual &&
+ {
+ echo " master"
+ } >expect &&
+ test_cmp expect actual
+
+'
+
test_expect_success 'side: branch --merged' '
git branch --merged >actual &&
@@ -66,6 +76,16 @@ test_expect_success 'side: branch --merged' '
'
+test_expect_success 'branch --merged with pattern implies --list' '
+
+ git branch --merged=side master >actual &&
+ {
+ echo " master"
+ } >expect &&
+ test_cmp expect actual
+
+'
+
test_expect_success 'side: branch --no-merged' '
git branch --no-merged >actual &&
@@ -95,4 +115,48 @@ test_expect_success 'master: branch --no-merged' '
'
+test_expect_success 'branch --no-merged with pattern implies --list' '
+
+ git branch --no-merged=master master >actual &&
+ >expect &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'implicit --list conflicts with modification options' '
+
+ test_must_fail git branch --contains=master -d &&
+ test_must_fail git branch --contains=master -m foo
+
+'
+
+# We want to set up a case where the walk for the tracking info
+# of one branch crosses the tip of another branch (and make sure
+# that the latter walk does not mess up our flag to see if it was
+# merged).
+#
+# Here "topic" tracks "master" with one extra commit, and "zzz" points to the
+# same tip as master The name "zzz" must come alphabetically after "topic"
+# as we process them in that order.
+test_expect_success 'branch --merged with --verbose' '
+ git branch --track topic master &&
+ git branch zzz topic &&
+ git checkout topic &&
+ test_commit foo &&
+ git branch --merged topic >actual &&
+ cat >expect <<-\EOF &&
+ master
+ * topic
+ zzz
+ EOF
+ test_cmp expect actual &&
+ git branch --verbose --merged topic >actual &&
+ cat >expect <<-\EOF &&
+ master c77a0a9 second on master
+ * topic 2c939f4 [ahead 1] foo
+ zzz c77a0a9 second on master
+ EOF
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3202-show-branch-octopus.sh b/t/t3202-show-branch-octopus.sh
index 0a5d5e6..6adf478 100755
--- a/t/t3202-show-branch-octopus.sh
+++ b/t/t3202-show-branch-octopus.sh
@@ -19,7 +19,7 @@ test_expect_success 'setup' '
> file$i &&
git add file$i &&
test_tick &&
- git commit -m branch$i || break
+ git commit -m branch$i || return 1
done
'
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 809d1c4..c6a3ccb 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -12,13 +12,13 @@ test_expect_success 'make commits' '
'
test_expect_success 'make branches' '
- git branch branch-one
+ 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 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
'
@@ -32,6 +32,20 @@ test_expect_success 'git branch shows local branches' '
test_cmp expect actual
'
+test_expect_success 'git branch --list shows local branches' '
+ git branch --list >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+ branch-one
+ branch-two
+EOF
+test_expect_success 'git branch --list pattern shows matching local branches' '
+ git branch --list branch* >actual &&
+ test_cmp expect actual
+'
+
cat >expect <<'EOF'
origin/HEAD -> origin/branch-one
origin/branch-one
@@ -67,14 +81,118 @@ test_expect_success 'git branch -v shows branch summaries' '
'
cat >expect <<'EOF'
-* (no branch)
+two
+one
+EOF
+test_expect_success 'git branch --list -v pattern shows branch summaries' '
+ git branch --list -v branch* >tmp &&
+ awk "{print \$NF}" <tmp >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git branch -v pattern does not show branch summaries' '
+ test_must_fail git branch -v branch*
+'
+
+test_expect_success 'git branch shows detached HEAD properly' '
+ cat >expect <<EOF &&
+* (HEAD detached at $(git rev-parse --short HEAD^0))
branch-one
branch-two
master
EOF
-test_expect_success 'git branch shows detached HEAD properly' '
git checkout HEAD^0 &&
git branch >actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'git branch shows detached HEAD properly after checkout --detach' '
+ git checkout master &&
+ cat >expect <<EOF &&
+* (HEAD detached at $(git rev-parse --short HEAD^0))
+ branch-one
+ branch-two
+ master
+EOF
+ git checkout --detach &&
+ git branch >actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'git branch shows detached HEAD properly after moving' '
+ cat >expect <<EOF &&
+* (HEAD detached from $(git rev-parse --short HEAD))
+ branch-one
+ branch-two
+ master
+EOF
+ git reset --hard HEAD^1 &&
+ git branch >actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'git branch shows detached HEAD properly from tag' '
+ cat >expect <<EOF &&
+* (HEAD detached at fromtag)
+ branch-one
+ branch-two
+ master
+EOF
+ git tag fromtag master &&
+ git checkout fromtag &&
+ git branch >actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'git branch shows detached HEAD properly after moving from tag' '
+ cat >expect <<EOF &&
+* (HEAD detached from fromtag)
+ branch-one
+ branch-two
+ master
+EOF
+ git reset --hard HEAD^1 &&
+ git branch >actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'git branch `--sort` option' '
+ cat >expect <<-\EOF &&
+ * (HEAD detached from fromtag)
+ branch-two
+ branch-one
+ master
+ EOF
+ git branch --sort=objectsize >actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'git branch --points-at option' '
+ cat >expect <<-\EOF &&
+ branch-one
+ master
+ EOF
+ git branch --points-at=branch-one >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'ambiguous branch/tag not marked' '
+ git tag ambiguous &&
+ git branch ambiguous &&
+ echo " ambiguous" >expect &&
+ git branch --list ambiguous >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'local-branch symrefs shortened properly' '
+ git symbolic-ref refs/heads/ref-to-branch refs/heads/branch-one &&
+ git symbolic-ref refs/heads/ref-to-remote refs/remotes/origin/branch-one &&
+ cat >expect <<-\EOF &&
+ ref-to-branch -> branch-one
+ ref-to-remote -> refs/remotes/origin/branch-one
+ EOF
+ git branch >actual.raw &&
+ grep ref-to <actual.raw >actual &&
test_cmp expect actual
'
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
index 5251740..9b182a0 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -11,7 +11,9 @@ semantic is still the same.
'
. ./test-lib.sh
-echo '[core] logallrefupdates = true' >>.git/config
+test_expect_success 'enable reflogs' '
+ git config core.logallrefupdates true
+'
test_expect_success \
'prepare a trivial repository' \
@@ -25,7 +27,7 @@ SHA1=
test_expect_success \
'see if git show-ref works as expected' \
'git branch a &&
- SHA1=`cat .git/refs/heads/a` &&
+ SHA1=$(cat .git/refs/heads/a) &&
echo "$SHA1 refs/heads/a" >expect &&
git show-ref a >result &&
test_cmp expect result'
@@ -60,6 +62,12 @@ test_expect_success 'see if git pack-refs --prune remove ref files' '
! test -f .git/refs/heads/f
'
+test_expect_success 'see if git pack-refs --prune removes empty dirs' '
+ git branch r/s/t &&
+ git pack-refs --all --prune &&
+ ! test -e .git/refs/heads/r
+'
+
test_expect_success \
'git branch g should work when git branch g/h has been deleted' \
'git branch g/h &&
@@ -112,4 +120,95 @@ test_expect_success 'pack, prune and repack' '
test_cmp all-of-them again
'
+test_expect_success 'explicit pack-refs with dangling packed reference' '
+ git commit --allow-empty -m "soon to be garbage-collected" &&
+ git pack-refs --all &&
+ git reset --hard HEAD^ &&
+ git reflog expire --expire=all --all &&
+ git prune --expire=all &&
+ git pack-refs --all 2>result &&
+ test_cmp /dev/null result
+'
+
+test_expect_success 'delete ref with dangling packed version' '
+ git checkout -b lamb &&
+ git commit --allow-empty -m "future garbage" &&
+ git pack-refs --all &&
+ git reset --hard HEAD^ &&
+ git checkout master &&
+ git reflog expire --expire=all --all &&
+ git prune --expire=all &&
+ git branch -d lamb 2>result &&
+ test_cmp /dev/null result
+'
+
+test_expect_success 'delete ref while another dangling packed ref' '
+ git branch lamb &&
+ git commit --allow-empty -m "future garbage" &&
+ git pack-refs --all &&
+ git reset --hard HEAD^ &&
+ git reflog expire --expire=all --all &&
+ git prune --expire=all &&
+ git branch -d lamb 2>result &&
+ test_cmp /dev/null result
+'
+
+test_expect_success 'pack ref directly below refs/' '
+ git update-ref refs/top HEAD &&
+ git pack-refs --all --prune &&
+ grep refs/top .git/packed-refs &&
+ test_path_is_missing .git/refs/top
+'
+
+test_expect_success 'do not pack ref in refs/bisect' '
+ git update-ref refs/bisect/local HEAD &&
+ git pack-refs --all --prune &&
+ ! grep refs/bisect/local .git/packed-refs >/dev/null &&
+ test_path_is_file .git/refs/bisect/local
+'
+
+test_expect_success 'disable reflogs' '
+ git config core.logallrefupdates false &&
+ rm -rf .git/logs
+'
+
+test_expect_success 'create packed foo/bar/baz branch' '
+ git branch foo/bar/baz &&
+ git pack-refs --all --prune &&
+ test_path_is_missing .git/refs/heads/foo/bar/baz &&
+ test_must_fail git reflog exists refs/heads/foo/bar/baz
+'
+
+test_expect_success 'notice d/f conflict with existing directory' '
+ test_must_fail git branch foo &&
+ test_must_fail git branch foo/bar
+'
+
+test_expect_success 'existing directory reports concrete ref' '
+ test_must_fail git branch foo 2>stderr &&
+ grep refs/heads/foo/bar/baz stderr
+'
+
+test_expect_success 'notice d/f conflict with existing ref' '
+ test_must_fail git branch foo/bar/baz/extra &&
+ test_must_fail git branch foo/bar/baz/lots/of/extra/components
+'
+
+test_expect_success 'timeout if packed-refs.lock exists' '
+ LOCK=.git/packed-refs.lock &&
+ >"$LOCK" &&
+ test_when_finished "rm -f $LOCK" &&
+ test_must_fail git pack-refs --all --prune
+'
+
+test_expect_success 'retry acquiring packed-refs.lock' '
+ LOCK=.git/packed-refs.lock &&
+ >"$LOCK" &&
+ test_when_finished "wait; rm -f $LOCK" &&
+ {
+ ( sleep 1 ; rm -f $LOCK ) &
+ } &&
+ git -c core.packedrefstimeout=3000 pack-refs --all --prune
+'
+
test_done
diff --git a/t/t3211-peel-ref.sh b/t/t3211-peel-ref.sh
new file mode 100755
index 0000000..3b7caca
--- /dev/null
+++ b/t/t3211-peel-ref.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+test_description='tests for the peel_ref optimization of packed-refs'
+. ./test-lib.sh
+
+test_expect_success 'create annotated tag in refs/tags' '
+ test_commit base &&
+ git tag -m annotated foo
+'
+
+test_expect_success 'create annotated tag outside of refs/tags' '
+ git update-ref refs/outside/foo refs/tags/foo
+'
+
+# This matches show-ref's output
+print_ref() {
+ echo "$(git rev-parse "$1") $1"
+}
+
+test_expect_success 'set up expected show-ref output' '
+ {
+ print_ref "refs/heads/master" &&
+ print_ref "refs/outside/foo" &&
+ print_ref "refs/outside/foo^{}" &&
+ print_ref "refs/tags/base" &&
+ print_ref "refs/tags/foo" &&
+ print_ref "refs/tags/foo^{}"
+ } >expect
+'
+
+test_expect_success 'refs are peeled outside of refs/tags (loose)' '
+ git show-ref -d >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'refs are peeled outside of refs/tags (packed)' '
+ git pack-refs --all &&
+ git show-ref -d >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create old-style pack-refs without fully-peeled' '
+ # Git no longer writes without fully-peeled, so we just write our own
+ # from scratch; we could also munge the existing file to remove the
+ # fully-peeled bits, but that seems even more prone to failure,
+ # especially if the format ever changes again. At least this way we
+ # know we are emulating exactly what an older git would have written.
+ {
+ echo "# pack-refs with: peeled " &&
+ print_ref "refs/heads/master" &&
+ print_ref "refs/outside/foo" &&
+ print_ref "refs/tags/base" &&
+ print_ref "refs/tags/foo" &&
+ echo "^$(git rev-parse "refs/tags/foo^{}")"
+ } >tmp &&
+ mv tmp .git/packed-refs
+'
+
+test_expect_success 'refs are peeled outside of refs/tags (old packed)' '
+ git show-ref -d >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'peeled refs survive deletion of packed ref' '
+ git pack-refs --all &&
+ cp .git/packed-refs fully-peeled &&
+ git branch yadda &&
+ git pack-refs --all &&
+ git branch -d yadda &&
+ test_cmp fully-peeled .git/packed-refs
+'
+
+test_done
diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh
index a99e4d8..04de03c 100755
--- a/t/t3300-funny-names.sh
+++ b/t/t3300-funny-names.sh
@@ -11,150 +11,207 @@ tree, index, and tree objects.
. ./test-lib.sh
+HT=' '
+
+test_have_prereq MINGW ||
+echo 2>/dev/null > "Name with an${HT}HT"
+if ! test -f "Name with an${HT}HT"
+then
+ # since FAT/NTFS does not allow tabs in filenames, skip this test
+ skip_all='Your filesystem does not allow tabs in filenames'
+ test_done
+fi
+
p0='no-funny'
p1='tabs ," (dq) and spaces'
p2='just space'
-cat >"$p0" <<\EOF
-1. A quick brown fox jumps over the lazy cat, oops dog.
-2. A quick brown fox jumps over the lazy cat, oops dog.
-3. A quick brown fox jumps over the lazy cat, oops dog.
-EOF
+test_expect_success 'setup' '
+ cat >"$p0" <<-\EOF &&
+ 1. A quick brown fox jumps over the lazy cat, oops dog.
+ 2. A quick brown fox jumps over the lazy cat, oops dog.
+ 3. A quick brown fox jumps over the lazy cat, oops dog.
+ EOF
-cat 2>/dev/null >"$p1" "$p0"
-echo 'Foo Bar Baz' >"$p2"
+ { cat "$p0" >"$p1" || :; } &&
+ { echo "Foo Bar Baz" >"$p2" || :; }
+'
-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
-}
+test_expect_success 'setup: populate index and tree' '
+ git update-index --add "$p0" "$p2" &&
+ t0=$(git write-tree)
+'
-echo 'just space
-no-funny' >expected
-test_expect_success 'git ls-files no-funny' \
- 'git update-index --add "$p0" "$p2" &&
+test_expect_success 'ls-files prints space in filename verbatim' '
+ printf "%s\n" "just space" no-funny >expected &&
git ls-files >current &&
- test_cmp expected current'
-
-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" &&
+ test_cmp expected current
+'
+
+test_expect_success 'setup: add funny filename' '
+ git update-index --add "$p1" &&
+ t1=$(git write-tree)
+'
+
+test_expect_success 'ls-files quotes funny filename' '
+ cat >expected <<-\EOF &&
+ just space
+ no-funny
+ "tabs\t,\" (dq) and spaces"
+ EOF
git ls-files >current &&
- test_cmp expected current'
-
-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_cmp expected current
+'
+
+test_expect_success 'ls-files -z does not quote funny filename' '
+ cat >expected <<-\EOF &&
+ just space
+ no-funny
+ tabs ," (dq) and spaces
+ EOF
+ git ls-files -z >ls-files.z &&
+ perl -pe "y/\000/\012/" <ls-files.z >current &&
+ test_cmp expected current
+'
+
+test_expect_success 'ls-tree quotes funny filename' '
+ cat >expected <<-\EOF &&
+ just space
+ no-funny
+ "tabs\t,\" (dq) and spaces"
+ EOF
+ git ls-tree -r $t1 >ls-tree &&
+ sed -e "s/^[^ ]* //" <ls-tree >current &&
+ test_cmp expected current
+'
+
+test_expect_success 'diff-index --name-status quotes funny filename' '
+ cat >expected <<-\EOF &&
+ A "tabs\t,\" (dq) and spaces"
+ EOF
+ git diff-index --name-status $t0 >current &&
+ test_cmp expected current
+'
+
+test_expect_success 'diff-tree --name-status quotes funny filename' '
+ cat >expected <<-\EOF &&
+ A "tabs\t,\" (dq) and spaces"
+ EOF
+ git diff-tree --name-status $t0 $t1 >current &&
+ test_cmp expected current
+'
+
+test_expect_success 'diff-index -z does not quote funny filename' '
+ cat >expected <<-\EOF &&
+ A
+ tabs ," (dq) and spaces
+ EOF
+ git diff-index -z --name-status $t0 >diff-index.z &&
+ perl -pe "y/\000/\012/" <diff-index.z >current &&
+ test_cmp expected current
+'
+
+test_expect_success 'diff-tree -z does not quote funny filename' '
+ cat >expected <<-\EOF &&
+ A
+ tabs ," (dq) and spaces
+ EOF
+ git diff-tree -z --name-status $t0 $t1 >diff-tree.z &&
+ perl -pe y/\\000/\\012/ <diff-tree.z >current &&
+ test_cmp expected current
+'
+
+test_expect_success 'diff-tree --find-copies-harder quotes funny filename' '
+ cat >expected <<-\EOF &&
+ CNUM no-funny "tabs\t,\" (dq) and spaces"
+ EOF
+ git diff-tree -C --find-copies-harder --name-status $t0 $t1 >out &&
+ sed -e "s/^C[0-9]*/CNUM/" <out >current &&
+ test_cmp expected current
+'
+
+test_expect_success 'setup: remove unfunny index entry' '
+ git update-index --force-remove "$p0"
+'
+
+test_expect_success 'diff-tree -M quotes funny filename' '
+ cat >expected <<-\EOF &&
+ RNUM no-funny "tabs\t,\" (dq) and spaces"
+ EOF
+ git diff-index -M --name-status $t0 >out &&
+ sed -e "s/^R[0-9]*/RNUM/" <out >current &&
+ test_cmp expected current
+'
+
+test_expect_success 'diff-index -M -p quotes funny filename' '
+ cat >expected <<-\EOF &&
+ diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
+ similarity index NUM%
+ rename from no-funny
+ rename to "tabs\t,\" (dq) and spaces"
+ EOF
+ git diff-index -M -p $t0 >diff &&
+ sed -e "s/index [0-9]*%/index NUM%/" <diff >current &&
+ test_cmp expected current
+'
+
+test_expect_success 'setup: mode change' '
+ chmod +x "$p1"
+'
+
+test_expect_success 'diff-index -M -p with mode change quotes funny filename' '
+ cat >expected <<-\EOF &&
+ diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
+ old mode 100644
+ new mode 100755
+ similarity index NUM%
+ rename from no-funny
+ rename to "tabs\t,\" (dq) and spaces"
+ EOF
+ git diff-index -M -p $t0 >diff &&
+ sed -e "s/index [0-9]*%/index NUM%/" <diff >current &&
+ test_cmp expected current
+'
+
+test_expect_success 'diffstat for rename quotes funny filename' '
+ cat >expected <<-\EOF &&
+ "tabs\t,\" (dq) and spaces"
+ 1 file changed, 0 insertions(+), 0 deletions(-)
+ EOF
+ git diff-index -M -p $t0 >diff &&
+ git apply --stat <diff >diffstat &&
+ sed -e "s/|.*//" -e "s/ *\$//" <diffstat >current &&
+ test_i18ncmp expected current
+'
+
+test_expect_success 'numstat for rename quotes funny filename' '
+ cat >expected <<-\EOF &&
+ 0 0 "tabs\t,\" (dq) and spaces"
+ EOF
+ git diff-index -M -p $t0 >diff &&
+ git apply --numstat <diff >current &&
+ test_cmp expected current
+'
+
+test_expect_success 'numstat without -M quotes funny filename' '
+ cat >expected <<-\EOF &&
+ 0 3 no-funny
+ 3 0 "tabs\t,\" (dq) and spaces"
+ EOF
+ git diff-index -p $t0 >diff &&
+ git apply --numstat <diff >current &&
+ test_cmp expected current
+'
+
+test_expect_success 'numstat for non-git rename diff quotes funny filename' '
+ cat >expected <<-\EOF &&
+ 0 3 no-funny
+ 3 0 "tabs\t,\" (dq) and spaces"
+ EOF
+ git diff-index -p $t0 >git-diff &&
+ sed -ne "/^[-+@]/p" <git-diff >diff &&
+ git apply --numstat <diff >current &&
+ test_cmp expected current
+'
test_done
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
index 1d82f79..2d200fd 100755
--- a/t/t3301-notes.sh
+++ b/t/t3301-notes.sh
@@ -7,230 +7,233 @@ test_description='Test commit notes'
. ./test-lib.sh
-cat > fake_editor.sh << \EOF
-#!/bin/sh
-echo "$MSG" > "$1"
-echo "$MSG" >& 2
+write_script fake_editor <<\EOF
+echo "$MSG" >"$1"
+echo "$MSG" >&2
EOF
-chmod a+x fake_editor.sh
-GIT_EDITOR=./fake_editor.sh
+GIT_EDITOR=./fake_editor
export GIT_EDITOR
+indent=" "
+
test_expect_success 'cannot annotate non-existing HEAD' '
- (MSG=3 && export MSG && test_must_fail git notes add)
+ test_must_fail env MSG=3 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 'setup' '
+ test_commit 1st &&
+ test_commit 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_must_fail env MSG=1 GIT_NOTES_REF=/ git notes show &&
+ test_must_fail env MSG=2 GIT_NOTES_REF=/ 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_must_fail env MSG=1 GIT_NOTES_REF=refs/heads/bogus 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)
+ test_must_fail env MSG=1 GIT_NOTES_REF=refs/heads/bogus 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_code 1 git notes show
'
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_write_lines A B >expect &&
+ git show -s --format="A%n%NB" >actual &&
+ test_cmp expect actual
'
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) &&
+ test_path_is_missing .git/NOTES_EDITMSG &&
+ git ls-tree -r refs/notes/commits >actual &&
+ test_line_count = 1 actual &&
+ 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
+ test_write_lines A b4 B >expect &&
+ git show -s --format="A%n%NB" >actual &&
+ test_cmp expect actual
'
-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
+ cat <<-EOF >expect &&
+ a1d8fa6 refs/notes/commits@{0}: notes: Notes added by '\''git notes add'\''
+ EOF
+ git reflog show refs/notes/commits >actual &&
+ test_cmp expect actual
'
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) &&
+ test_path_is_missing .git/NOTES_EDITMSG &&
+ git ls-tree -r refs/notes/commits >actual &&
+ test_line_count = 1 actual &&
+ 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) &&
+test_expect_success 'show notes from treeish' '
+ test "b3" = "$(git notes --ref commits^{tree} show)" &&
+ test "b4" = "$(git notes --ref commits@{1} show)"
+'
+
+test_expect_success 'cannot edit notes from non-ref' '
+ test_must_fail git notes --ref commits^{tree} edit &&
+ test_must_fail git notes --ref commits@{1} edit
+'
+
+test_expect_success 'cannot "git notes add -m" where notes already exists' '
+ test_must_fail git notes add -m "b2" &&
+ test_path_is_missing .git/NOTES_EDITMSG &&
+ git ls-tree -r refs/notes/commits >actual &&
+ test_line_count = 1 actual &&
+ 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 -m"' '
+ git notes add -f -m "b1" &&
+ test_path_is_missing .git/NOTES_EDITMSG &&
+ git ls-tree -r refs/notes/commits >actual &&
+ test_line_count = 1 actual &&
+ test "b1" = "$(git notes show)" &&
+ git show HEAD^ &&
+ test_must_fail git notes show HEAD^
+'
+
+test_expect_success 'add w/no options on existing note morphs into edit' '
+ MSG=b2 git notes add &&
+ test_path_is_missing .git/NOTES_EDITMSG &&
+ git ls-tree -r refs/notes/commits >actual &&
+ test_line_count = 1 actual &&
+ test "b2" = "$(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) &&
+ test_path_is_missing .git/NOTES_EDITMSG &&
+ git ls-tree -r refs/notes/commits >actual &&
+ test_line_count = 1 actual &&
+ 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
+test_expect_success 'show notes' '
+ cat >expect <<-EOF &&
+ commit 7a4ca6ee52a974a66cbaa78e33214535dff1d691
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:14:13 2005 -0700
- 2nd
+ ${indent}2nd
-Notes:
- b1
-EOF
-
-test_expect_success 'show notes' '
+ Notes:
+ ${indent}b1
+ EOF
! (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
+ git log -1 >actual &&
+ test_cmp expect actual
'
-cat > expect-multiline << EOF
-commit 1584215f1d29c65e99c6c6848626553fdd07fd75
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:15:13 2005 -0700
-
- 3rd
+test_expect_success 'show multi-line notes' '
+ test_commit 3rd &&
+ MSG="b3${LF}c3c3c3c3${LF}d3d3d3" git notes add &&
+ cat >expect-multiline <<-EOF &&
+ commit d07d62e5208f22eb5695e7eb47667dc8b9860290
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:15:13 2005 -0700
-Notes:
- b3
- c3c3c3c3
- d3d3d3
-EOF
+ ${indent}3rd
-printf "\n" >> expect-multiline
-cat expect >> expect-multiline
+ Notes:
+ ${indent}b3
+ ${indent}c3c3c3c3
+ ${indent}d3d3d3
-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
+ EOF
+ cat expect >>expect-multiline &&
+ git log -2 >actual &&
+ test_cmp expect-multiline actual
'
-cat > expect-F << EOF
-commit 15023535574ded8b1a89052b32673f84cf9582b8
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:16:13 2005 -0700
-
- 4th
+test_expect_success 'show -F notes' '
+ test_commit 4th &&
+ echo "xyzzy" >note5 &&
+ git notes add -F note5 &&
+ cat >expect-F <<-EOF &&
+ commit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:16:13 2005 -0700
-Notes:
- xyzzy
-EOF
+ ${indent}4th
-printf "\n" >> expect-F
-cat expect-multiline >> expect-F
+ Notes:
+ ${indent}xyzzy
-test_expect_success 'show -F notes' '
- git log -3 > output &&
- test_cmp expect-F output
+ EOF
+ cat expect-multiline >>expect-F &&
+ git log -3 >actual &&
+ test_cmp expect-F actual
'
-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
+test_expect_success 'Re-adding -F notes without -f fails' '
+ echo "zyxxy" >note5 &&
+ test_must_fail git notes add -F note5 &&
+ git log -3 >actual &&
+ test_cmp expect-F actual
+'
- 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 &&
+ commit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11
+ tree 05ac65288c4c4b3b709a020ae94b2ece2f2201ae
+ parent d07d62e5208f22eb5695e7eb47667dc8b9860290
+ author A U Thor <author@example.com> 1112912173 -0700
+ committer C O Mitter <committer@example.com> 1112912173 -0700
+
+ ${indent}4th
+ EOF
+ git log -1 --pretty=raw >actual &&
+ test_cmp expect actual
'
-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
+ cat >>expect <<-EOF &&
+
+ Notes:
+ ${indent}xyzzy
+ EOF
+ git log -1 --pretty=raw --show-notes >actual &&
+ test_cmp expect actual
'
test_expect_success 'git log --no-notes' '
- git log -1 --no-notes >output &&
- ! grep xyzzy output
+ git log -1 --no-notes >actual &&
+ ! grep xyzzy actual
'
test_expect_success 'git format-patch does not show notes' '
- git format-patch -1 --stdout >output &&
- ! grep xyzzy output
+ git format-patch -1 --stdout >actual &&
+ ! grep xyzzy actual
'
test_expect_success 'git format-patch --show-notes does show notes' '
- git format-patch --show-notes -1 --stdout >output &&
- grep xyzzy output
+ git format-patch --show-notes -1 --stdout >actual &&
+ grep xyzzy actual
'
for pretty in \
@@ -242,194 +245,282 @@ do
?*) 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"
+ git show $p >actual &&
+ eval "$negate grep xyzzy actual"
'
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
+test_expect_success 'setup alternate notes ref' '
+ git notes --ref=alternate add -m alternate
+'
-printf "\n" >> expect-m
-cat expect-F >> expect-m
+test_expect_success 'git log --notes shows default notes' '
+ git log -1 --notes >actual &&
+ grep xyzzy actual &&
+ ! grep alternate actual
+'
-test_expect_success 'show -m notes' '
- git log -4 > output &&
- test_cmp expect-m output
+test_expect_success 'git log --notes=X shows only X' '
+ git log -1 --notes=alternate >actual &&
+ ! grep xyzzy actual &&
+ grep alternate actual
'
-test_expect_success 'remove note with add -f -F /dev/null (setup)' '
- git notes add -f -F /dev/null
+test_expect_success 'git log --notes --notes=X shows both' '
+ git log -1 --notes --notes=alternate >actual &&
+ grep xyzzy actual &&
+ grep alternate actual
'
-cat > expect-rm-F << EOF
-commit bd1753200303d0a0344be813e504253b3d98e74d
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:17:13 2005 -0700
+test_expect_success 'git log --no-notes resets default state' '
+ git log -1 --notes --notes=alternate \
+ --no-notes --notes=alternate \
+ >actual &&
+ ! grep xyzzy actual &&
+ grep alternate actual
+'
- 5th
-EOF
+test_expect_success 'git log --no-notes resets ref list' '
+ git log -1 --notes --notes=alternate \
+ --no-notes --notes \
+ >actual &&
+ grep xyzzy actual &&
+ ! grep alternate actual
+'
-printf "\n" >> expect-rm-F
-cat expect-F >> expect-rm-F
+test_expect_success 'show -m notes' '
+ test_commit 5th &&
+ git notes add -m spam -m "foo${LF}bar${LF}baz" &&
+ cat >expect-m <<-EOF &&
+ commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:17:13 2005 -0700
-test_expect_success 'verify note removal with -F /dev/null' '
- git log -4 > output &&
- test_cmp expect-rm-F output &&
- ! git notes show
-'
+ ${indent}5th
-test_expect_success 'do not create empty note with -m "" (setup)' '
- git notes add -m ""
+ Notes:
+ ${indent}spam
+ ${indent}
+ ${indent}foo
+ ${indent}bar
+ ${indent}baz
+
+ EOF
+ cat expect-F >>expect-m &&
+ git log -4 >actual &&
+ test_cmp expect-m actual
'
-test_expect_success 'verify non-creation of note with -m ""' '
- git log -4 > output &&
- test_cmp expect-rm-F output &&
- ! git notes show
+test_expect_success 'remove note with add -f -F /dev/null' '
+ git notes add -f -F /dev/null &&
+ cat >expect-rm-F <<-EOF &&
+ commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:17:13 2005 -0700
+
+ ${indent}5th
+
+ EOF
+ cat expect-F >>expect-rm-F &&
+ git log -4 >actual &&
+ test_cmp expect-rm-F actual &&
+ test_must_fail git notes show
'
-cat > expect-combine_m_and_F << EOF
-foo
+test_expect_success 'do not create empty note with -m ""' '
+ git notes add -m "" &&
+ git log -4 >actual &&
+ test_cmp expect-rm-F actual &&
+ test_must_fail git notes show
+'
-xyzzy
+test_expect_success 'create note with combination of -m and -F' '
+ cat >expect-combine_m_and_F <<-EOF &&
+ foo
-bar
+ xyzzy
-zyxxy
+ bar
-baz
-EOF
+ zyxxy
-test_expect_success 'create note with combination of -m and -F' '
- echo "xyzzy" > note_a &&
- echo "zyxxy" > note_b &&
+ baz
+ EOF
+ 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
+ git notes show >actual &&
+ test_cmp expect-combine_m_and_F actual
'
-test_expect_success 'remove note with "git notes remove" (setup)' '
+test_expect_success 'remove note with "git notes remove"' '
git notes remove HEAD^ &&
- git notes remove
+ git notes remove &&
+ cat >expect-rm-remove <<-EOF &&
+ commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:17:13 2005 -0700
+
+ ${indent}5th
+
+ commit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:16:13 2005 -0700
+
+ ${indent}4th
+
+ EOF
+ cat expect-multiline >>expect-rm-remove &&
+ git log -4 >actual &&
+ test_cmp expect-rm-remove actual &&
+ test_must_fail git notes show HEAD^
'
-cat > expect-rm-remove << EOF
-commit bd1753200303d0a0344be813e504253b3d98e74d
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:17:13 2005 -0700
+test_expect_success 'removing non-existing note should not create new commit' '
+ git rev-parse --verify refs/notes/commits >before_commit &&
+ test_must_fail git notes remove HEAD^ &&
+ git rev-parse --verify refs/notes/commits >after_commit &&
+ test_cmp before_commit after_commit
+'
- 5th
+test_expect_success 'removing more than one' '
+ before=$(git rev-parse --verify refs/notes/commits) &&
+ test_when_finished "git update-ref refs/notes/commits $before" &&
-commit 15023535574ded8b1a89052b32673f84cf9582b8
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:16:13 2005 -0700
+ # We have only two -- add another and make sure it stays
+ git notes add -m "extra" &&
+ git notes list HEAD >after-removal-expect &&
+ git notes remove HEAD^^ HEAD^^^ &&
+ git notes list | sed -e "s/ .*//" >actual &&
+ test_cmp after-removal-expect actual
+'
- 4th
-EOF
+test_expect_success 'removing is atomic' '
+ before=$(git rev-parse --verify refs/notes/commits) &&
+ test_when_finished "git update-ref refs/notes/commits $before" &&
+ test_must_fail git notes remove HEAD^^ HEAD^^^ HEAD^ &&
+ after=$(git rev-parse --verify refs/notes/commits) &&
+ test "$before" = "$after"
+'
-printf "\n" >> expect-rm-remove
-cat expect-multiline >> expect-rm-remove
+test_expect_success 'removing with --ignore-missing' '
+ before=$(git rev-parse --verify refs/notes/commits) &&
+ test_when_finished "git update-ref refs/notes/commits $before" &&
-test_expect_success 'verify note removal with "git notes remove"' '
- git log -4 > output &&
- test_cmp expect-rm-remove output &&
- ! git notes show HEAD^
+ # We have only two -- add another and make sure it stays
+ git notes add -m "extra" &&
+ git notes list HEAD >after-removal-expect &&
+ git notes remove --ignore-missing HEAD^^ HEAD^^^ HEAD^ &&
+ git notes list | sed -e "s/ .*//" >actual &&
+ test_cmp after-removal-expect actual
'
-cat > expect << EOF
-c18dc024e14f08d18d14eea0d747ff692d66d6a3 1584215f1d29c65e99c6c6848626553fdd07fd75
-c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 268048bfb8a1fb38e703baceb8ab235421bf80c5
-EOF
+test_expect_success 'removing with --ignore-missing but bogus ref' '
+ before=$(git rev-parse --verify refs/notes/commits) &&
+ test_when_finished "git update-ref refs/notes/commits $before" &&
+ test_must_fail git notes remove --ignore-missing HEAD^^ HEAD^^^ NO-SUCH-COMMIT &&
+ after=$(git rev-parse --verify refs/notes/commits) &&
+ test "$before" = "$after"
+'
-test_expect_success 'list notes with "git notes list"' '
- git notes list > output &&
- test_cmp expect output
+test_expect_success 'remove reads from --stdin' '
+ before=$(git rev-parse --verify refs/notes/commits) &&
+ test_when_finished "git update-ref refs/notes/commits $before" &&
+
+ # We have only two -- add another and make sure it stays
+ git notes add -m "extra" &&
+ git notes list HEAD >after-removal-expect &&
+ git rev-parse HEAD^^ HEAD^^^ >input &&
+ git notes remove --stdin <input &&
+ git notes list | sed -e "s/ .*//" >actual &&
+ test_cmp after-removal-expect actual
'
-test_expect_success 'list notes with "git notes"' '
- git notes > output &&
- test_cmp expect output
+test_expect_success 'remove --stdin is also atomic' '
+ before=$(git rev-parse --verify refs/notes/commits) &&
+ test_when_finished "git update-ref refs/notes/commits $before" &&
+ git rev-parse HEAD^^ HEAD^^^ HEAD^ >input &&
+ test_must_fail git notes remove --stdin <input &&
+ after=$(git rev-parse --verify refs/notes/commits) &&
+ test "$before" = "$after"
'
-cat > expect << EOF
-c18dc024e14f08d18d14eea0d747ff692d66d6a3
-EOF
+test_expect_success 'removing with --stdin --ignore-missing' '
+ before=$(git rev-parse --verify refs/notes/commits) &&
+ test_when_finished "git update-ref refs/notes/commits $before" &&
-test_expect_success 'list specific note with "git notes list <object>"' '
- git notes list HEAD^^ > output &&
- test_cmp expect output
+ # We have only two -- add another and make sure it stays
+ git notes add -m "extra" &&
+ git notes list HEAD >after-removal-expect &&
+ git rev-parse HEAD^^ HEAD^^^ HEAD^ >input &&
+ git notes remove --ignore-missing --stdin <input &&
+ git notes list | sed -e "s/ .*//" >actual &&
+ test_cmp after-removal-expect actual
'
-cat > expect << EOF
-EOF
+test_expect_success 'list notes with "git notes list"' '
+ cat >expect <<-EOF &&
+ c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 7a4ca6ee52a974a66cbaa78e33214535dff1d691
+ c18dc024e14f08d18d14eea0d747ff692d66d6a3 d07d62e5208f22eb5695e7eb47667dc8b9860290
+ EOF
+ git notes list >actual &&
+ test_cmp expect actual
+'
-test_expect_success 'listing non-existing notes fails' '
- test_must_fail git notes list HEAD > output &&
- test_cmp expect output
+test_expect_success 'list notes with "git notes"' '
+ git notes >actual &&
+ test_cmp expect actual
'
-cat > expect << EOF
-Initial set of notes
+test_expect_success 'list specific note with "git notes list <object>"' '
+ cat >expect <<-EOF &&
+ c18dc024e14f08d18d14eea0d747ff692d66d6a3
+ EOF
+ git notes list HEAD^^ >actual &&
+ test_cmp expect actual
+'
-More notes appended with git notes append
-EOF
+test_expect_success 'listing non-existing notes fails' '
+ cat >expect <<-EOF &&
+ EOF
+ test_must_fail git notes list HEAD >actual &&
+ test_cmp expect actual
+'
test_expect_success 'append to existing note with "git notes append"' '
+ cat >expect <<-EOF &&
+ Initial set of notes
+
+ More notes appended with git notes append
+ EOF
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
+ git notes show >actual &&
+ test_cmp expect actual
'
-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
+ cat >expect_list <<-EOF &&
+ c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 7a4ca6ee52a974a66cbaa78e33214535dff1d691
+ 4b6ad22357cc8a1296720574b8d2fbc22fab0671 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+ c18dc024e14f08d18d14eea0d747ff692d66d6a3 d07d62e5208f22eb5695e7eb47667dc8b9860290
+ EOF
+ git notes list >actual &&
+ test_cmp expect_list actual
'
test_expect_success 'appending empty string does not change existing note' '
git notes append -m "" &&
- git notes show > output &&
- test_cmp expect output
+ git notes show >actual &&
+ test_cmp expect actual
'
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
+ git notes append -m "Initial set of notes${LF}${LF}More notes appended with git notes append" &&
+ git notes show >actual &&
+ test_cmp expect actual
'
test_expect_success 'appending empty string to non-existing note does not create note' '
@@ -440,613 +531,600 @@ test_expect_success 'appending empty string to non-existing note does not create
'
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
+ test_commit 6th &&
+ GIT_NOTES_REF="refs/notes/other" git notes add -m "other note" &&
+ cat >expect-not-other <<-EOF &&
+ commit 2c125331118caba0ff8238b7f4958ac6e93fe39c
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:18:13 2005 -0700
- 6th
-
-Notes (other):
- other note
-EOF
+ ${indent}6th
+ EOF
+ cp expect-not-other expect-other &&
+ cat >>expect-other <<-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
+ Notes (other):
+ ${indent}other note
+ EOF
+'
test_expect_success 'Do not show note on other ref by default' '
- git log -1 > output &&
- test_cmp expect-not-other output
+ git log -1 >actual &&
+ test_cmp expect-not-other actual
'
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
+ GIT_NOTES_REF="refs/notes/other" git log -1 >actual &&
+ test_cmp expect-other actual
'
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_config core.notesRef "refs/notes/other" &&
+ git log -1 >actual &&
+ test_cmp expect-other actual
'
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
+ test_config core.notesRef "refs/notes/other" &&
+ GIT_NOTES_REF="refs/notes/wrong" git log -1 >actual &&
+ test_cmp expect-not-other actual
'
-cat > expect-both << EOF
-commit 387a89921c73d7ed72cd94d179c1c7048ca47756
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:18:13 2005 -0700
-
- 6th
+test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '
+ cat >expect-both <<-EOF &&
+ commit 2c125331118caba0ff8238b7f4958ac6e93fe39c
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:18:13 2005 -0700
-Notes:
- order test
+ ${indent}6th
-Notes (other):
- other note
+ Notes:
+ ${indent}order test
-commit bd1753200303d0a0344be813e504253b3d98e74d
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:17:13 2005 -0700
+ Notes (other):
+ ${indent}other note
- 5th
+ commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:17:13 2005 -0700
-Notes:
- replacement for deleted note
-EOF
+ ${indent}5th
-test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '
+ Notes:
+ ${indent}replacement for deleted note
+ EOF
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_unconfig core.notesRef &&
+ test_config notes.displayRef "refs/notes/*" &&
+ git log -2 >actual &&
+ test_cmp expect-both actual
'
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_config core.notesRef refs/notes/commits &&
+ test_config notes.displayRef refs/notes/other &&
+ git log -2 >actual &&
+ test_cmp expect-both actual
'
test_expect_success 'notes.displayRef can be given more than once' '
- git config --unset core.notesRef &&
- git config notes.displayRef refs/notes/commits &&
+ test_unconfig core.notesRef &&
+ test_config notes.displayRef refs/notes/commits &&
git config --add notes.displayRef refs/notes/other &&
- git log -2 > output &&
- test_cmp expect-both output
+ git log -2 >actual &&
+ test_cmp expect-both actual
'
-cat > expect-both-reversed << EOF
-commit 387a89921c73d7ed72cd94d179c1c7048ca47756
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:18:13 2005 -0700
-
- 6th
+test_expect_success 'notes.displayRef respects order' '
+ cat >expect-both-reversed <<-EOF &&
+ commit 2c125331118caba0ff8238b7f4958ac6e93fe39c
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:18:13 2005 -0700
-Notes (other):
- other note
+ ${indent}6th
-Notes:
- order test
-EOF
+ Notes (other):
+ ${indent}other note
-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
+ Notes:
+ ${indent}order test
+ EOF
+ test_config core.notesRef refs/notes/other &&
+ test_config notes.displayRef refs/notes/commits &&
+ git log -1 >actual &&
+ test_cmp expect-both-reversed actual
'
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
+ git log -2 >actual &&
+ test_cmp expect-both actual
'
-cat > expect-none << EOF
-commit 387a89921c73d7ed72cd94d179c1c7048ca47756
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:18:13 2005 -0700
+test_expect_success 'GIT_NOTES_DISPLAY_REF overrides config' '
+ cat >expect-none <<-EOF &&
+ commit 2c125331118caba0ff8238b7f4958ac6e93fe39c
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:18:13 2005 -0700
- 6th
+ ${indent}6th
-commit bd1753200303d0a0344be813e504253b3d98e74d
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:17:13 2005 -0700
+ commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+ 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
+ ${indent}5th
+ EOF
+ test_config notes.displayRef "refs/notes/*" &&
+ GIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log -2 >actual &&
+ test_cmp expect-none actual
'
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
+ GIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log --show-notes=* -2 >actual &&
+ test_cmp expect-both actual
'
-cat > expect-commits << EOF
-commit 387a89921c73d7ed72cd94d179c1c7048ca47756
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:18:13 2005 -0700
-
- 6th
+test_expect_success '--no-standard-notes' '
+ cat >expect-commits <<-EOF &&
+ commit 2c125331118caba0ff8238b7f4958ac6e93fe39c
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:18:13 2005 -0700
-Notes:
- order test
-EOF
+ ${indent}6th
-test_expect_success '--no-standard-notes' '
- git log --no-standard-notes --show-notes=commits -1 > output &&
- test_cmp expect-commits output
+ Notes:
+ ${indent}order test
+ EOF
+ git log --no-standard-notes --show-notes=commits -1 >actual &&
+ test_cmp expect-commits actual
'
test_expect_success '--standard-notes' '
+ test_config notes.displayRef "refs/notes/*" &&
git log --no-standard-notes --show-notes=commits \
- --standard-notes -2 > output &&
- test_cmp expect-both output
+ --standard-notes -2 >actual &&
+ test_cmp expect-both actual
'
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
+ --no-standard-notes -1 >actual &&
+ test_cmp expect-both-reversed actual
'
test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '
- git config core.notesRef refs/notes/other &&
- echo "Note on a tree" > expect
+ test_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 &&
+ git notes show HEAD: >actual &&
test_cmp expect actual &&
- echo "Note on a blob" > expect
+ 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 &&
+ git notes show HEAD:$filename >actual &&
test_cmp expect actual &&
- echo "Note on a tag" > expect
+ 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 &&
+ 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
+test_expect_success 'create note from other note with "git notes add -C"' '
+ cat >expect <<-EOF &&
+ commit fb01e0ca8c33b6cc0c6451dde747f97df567cb5c
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:19:13 2005 -0700
-Notes (other):
- other note
-EOF
+ ${indent}7th
-test_expect_success 'create note from other note with "git notes add -C"' '
- : > a7 &&
- git add a7 &&
- test_tick &&
- git commit -m 7th &&
+ Notes:
+ ${indent}order test
+ EOF
+ test_commit 7th &&
git notes add -C $(git notes list HEAD^) &&
- git log -1 > actual &&
+ 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_commit 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
+test_expect_success 'create note from non-blob with "git notes add -C" fails' '
+ commit=$(git rev-parse --verify HEAD) &&
+ tree=$(git rev-parse --verify HEAD:) &&
+ test_must_fail git notes add -C $commit &&
+ test_must_fail git notes add -C $tree &&
+ test_must_fail git notes list HEAD
+'
- 9th
+test_expect_success 'create note from blob with "git notes add -C" reuses blob id' '
+ cat >expect <<-EOF &&
+ commit 9a4c31c7f722b5d517e92c64e932dd751e1413bf
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:20:13 2005 -0700
-Notes (other):
- yet another note
-EOF
+ ${indent}8th
+
+ Notes:
+ ${indent}This is a blob object
+ EOF
+ blob=$(echo "This is a blob object" | git hash-object -w --stdin) &&
+ git notes add -C $blob &&
+ git log -1 >actual &&
+ test_cmp expect actual &&
+ test "$(git notes list HEAD)" = "$blob"
+'
test_expect_success 'create note from other note with "git notes add -c"' '
- : > a9 &&
- git add a9 &&
- test_tick &&
- git commit -m 9th &&
+ cat >expect <<-EOF &&
+ commit 2e0db4bc649e174d667a1cde19e725cf897a5bd2
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:21:13 2005 -0700
+
+ ${indent}9th
+
+ Notes:
+ ${indent}yet another note
+ EOF
+ test_commit 9th &&
MSG="yet another note" git notes add -c $(git notes list HEAD^^) &&
- git log -1 > actual &&
+ 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_commit 10th &&
+ test_must_fail env MSG="yet another note" 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"' '
+ cat >expect <<-EOF &&
+ commit 2e0db4bc649e174d667a1cde19e725cf897a5bd2
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:21:13 2005 -0700
+
+ ${indent}9th
+
+ Notes:
+ ${indent}yet another note
+ ${indent}
+ ${indent}yet another note
+ EOF
git notes append -C $(git notes list HEAD^) HEAD^ &&
- git log -1 HEAD^ > actual &&
+ 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
+test_expect_success 'create note from other note with "git notes append -c"' '
+ cat >expect <<-EOF &&
+ commit 7c3b87ab368f81e11b1ea87b2ab99a71ccd25406
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:22:13 2005 -0700
-Notes (other):
- other note
-EOF
+ ${indent}10th
-test_expect_success 'create note from other note with "git notes append -c"' '
+ Notes:
+ ${indent}other note
+ EOF
MSG="other note" git notes append -c $(git notes list HEAD^) &&
- git log -1 > actual &&
+ 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"' '
+ cat >expect <<-EOF &&
+ commit 7c3b87ab368f81e11b1ea87b2ab99a71ccd25406
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:22:13 2005 -0700
+
+ ${indent}10th
+
+ Notes:
+ ${indent}other note
+ ${indent}
+ ${indent}yet another note
+ EOF
MSG="yet another note" git notes append -c $(git notes list HEAD) &&
- git log -1 > actual &&
+ 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 &&
+ cat >expect <<-EOF &&
+ commit a446fff8777efdc6eb8f4b7c8a5ff699484df0d5
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:23:13 2005 -0700
+
+ ${indent}11th
+
+ Notes:
+ ${indent}other note
+ ${indent}
+ ${indent}yet another note
+ EOF
+ test_commit 11th &&
git notes copy HEAD^ HEAD &&
- git log -1 > actual &&
+ 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 &&
+ 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"' '
+ cat >expect <<-EOF &&
+ commit a446fff8777efdc6eb8f4b7c8a5ff699484df0d5
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:23:13 2005 -0700
+
+ ${indent}11th
+
+ Notes:
+ ${indent}yet another note
+ ${indent}
+ ${indent}yet another note
+ EOF
git notes copy -f HEAD~2 HEAD &&
- git log -1 > actual &&
+ 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_commit 12th &&
+ test_commit 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
+test_expect_success 'git notes copy --stdin' '
+ cat >expect <<-EOF &&
+ commit e871aa61182b1d95d0a6fb75445d891722863b6b
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:25:13 2005 -0700
-Notes (other):
- yet another note
-$whitespace
- yet another note
+ ${indent}13th
-commit 7038787dfe22a14c3867ce816dbba39845359719
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:24:13 2005 -0700
+ Notes:
+ ${indent}yet another note
+ ${indent}
+ ${indent}yet another note
- 12th
+ commit 65e263ded02ae4e8839bc151095113737579dc12
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:24:13 2005 -0700
-Notes (other):
- other note
-$whitespace
- yet another note
-EOF
+ ${indent}12th
-test_expect_success 'git notes copy --stdin' '
+ Notes:
+ ${indent}other note
+ ${indent}
+ ${indent}yet another note
+ EOF
(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 &&
+ git log -2 >actual &&
+ test_cmp expect actual &&
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
+test_expect_success 'git notes copy --for-rewrite (unconfigured)' '
+ cat >expect <<-EOF &&
+ commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:27:13 2005 -0700
-commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:26:13 2005 -0700
+ ${indent}15th
- 14th
-EOF
+ commit 07c85d77059393ed0154b8c96906547a59dfcddd
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:26:13 2005 -0700
-test_expect_success 'git notes copy --for-rewrite (unconfigured)' '
+ ${indent}14th
+ EOF
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
+ git log -2 >actual &&
+ test_cmp expect actual
'
-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/*" &&
+ cat >expect <<-EOF &&
+ commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:27:13 2005 -0700
+
+ ${indent}15th
+
+ Notes:
+ ${indent}yet another note
+ ${indent}
+ ${indent}yet another note
+
+ commit 07c85d77059393ed0154b8c96906547a59dfcddd
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:26:13 2005 -0700
+
+ ${indent}14th
+
+ Notes:
+ ${indent}other note
+ ${indent}
+ ${indent}yet another note
+ EOF
+ test_config notes.rewriteMode overwrite &&
+ test_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
+ git log -2 >actual &&
+ test_cmp expect actual
'
test_expect_success 'git notes copy --for-rewrite (disabled)' '
- git config notes.rewrite.bar false &&
+ test_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
+ git log -2 >actual &&
+ test_cmp expect actual
'
-cat > expect << EOF
-commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:27:13 2005 -0700
-
- 15th
+test_expect_success 'git notes copy --for-rewrite (overwrite)' '
+ cat >expect <<-EOF &&
+ commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:27:13 2005 -0700
-Notes (other):
- a fresh note
-EOF
+ ${indent}15th
-test_expect_success 'git notes copy --for-rewrite (overwrite)' '
+ Notes:
+ ${indent}a fresh note
+ EOF
git notes add -f -m"a fresh note" HEAD^ &&
+ test_config notes.rewriteMode overwrite &&
+ test_config notes.rewriteRef "refs/notes/*" &&
echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
git notes copy --for-rewrite=foo &&
- git log -1 > output &&
- test_cmp expect output
+ git log -1 >actual &&
+ test_cmp expect actual
'
test_expect_success 'git notes copy --for-rewrite (ignore)' '
- git config notes.rewriteMode ignore &&
+ test_config notes.rewriteMode ignore &&
+ test_config notes.rewriteRef "refs/notes/*" &&
echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
git notes copy --for-rewrite=foo &&
- git log -1 > output &&
- test_cmp expect output
+ git log -1 >actual &&
+ test_cmp expect actual
'
-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)' '
+ cat >expect <<-EOF &&
+ commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:27:13 2005 -0700
+
+ ${indent}15th
+
+ Notes:
+ ${indent}a fresh note
+ ${indent}
+ ${indent}another fresh note
+ EOF
git notes add -f -m"another fresh note" HEAD^ &&
- git config notes.rewriteMode concatenate &&
+ test_config notes.rewriteMode concatenate &&
+ test_config notes.rewriteRef "refs/notes/*" &&
echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
git notes copy --for-rewrite=foo &&
- git log -1 > output &&
- test_cmp expect output
+ git log -1 >actual &&
+ test_cmp expect actual
'
-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)' '
+ cat >expect <<-EOF &&
+ commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:27:13 2005 -0700
+
+ ${indent}15th
+
+ Notes:
+ ${indent}a fresh note
+ ${indent}
+ ${indent}another fresh note
+ ${indent}
+ ${indent}append 1
+ ${indent}
+ ${indent}append 2
+ EOF
git notes add -f -m"append 1" HEAD^ &&
git notes add -f -m"append 2" HEAD^^ &&
+ test_config notes.rewriteMode concatenate &&
+ test_config notes.rewriteRef "refs/notes/*" &&
(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
+ git log -1 >actual &&
+ test_cmp expect actual
'
test_expect_success 'git notes copy --for-rewrite (append empty)' '
git notes remove HEAD^ &&
+ test_config notes.rewriteMode concatenate &&
+ test_config notes.rewriteRef "refs/notes/*" &&
echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
git notes copy --for-rewrite=foo &&
- git log -1 > output &&
- test_cmp expect output
+ git log -1 >actual &&
+ test_cmp expect actual
'
-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' '
+ cat >expect <<-EOF &&
+ commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:27:13 2005 -0700
+
+ ${indent}15th
+
+ Notes:
+ ${indent}replacement note 1
+ EOF
+ test_config notes.rewriteMode concatenate &&
+ test_config notes.rewriteRef "refs/notes/*" &&
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
+ git log -1 >actual &&
+ test_cmp expect actual
'
-cat > expect << EOF
-commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:27:13 2005 -0700
+test_expect_success 'GIT_NOTES_REWRITE_REF works' '
+ cat >expect <<-EOF &&
+ commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:27:13 2005 -0700
- 15th
+ ${indent}15th
-Notes (other):
- replacement note 2
-EOF
-
-test_expect_success 'GIT_NOTES_REWRITE_REF works' '
- git config notes.rewriteMode overwrite &&
+ Notes:
+ ${indent}replacement note 2
+ EOF
git notes add -f -m"replacement note 2" HEAD^ &&
- git config --unset-all notes.rewriteRef &&
+ test_config notes.rewriteMode overwrite &&
+ test_unconfig 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
+ git log -1 >actual &&
+ test_cmp expect actual
'
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^ &&
+ test_config notes.rewriteMode overwrite &&
+ test_config notes.rewriteRef refs/notes/other &&
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
+ git log -1 >actual &&
+ test_cmp expect actual
'
test_expect_success 'git notes copy diagnoses too many or too few parameters' '
@@ -1054,4 +1132,76 @@ test_expect_success 'git notes copy diagnoses too many or too few parameters' '
test_must_fail git notes copy one two three
'
+test_expect_success 'git notes get-ref expands refs/heads/master to refs/notes/refs/heads/master' '
+ test_unconfig core.notesRef &&
+ sane_unset GIT_NOTES_REF &&
+ test "$(git notes --ref=refs/heads/master get-ref)" = "refs/notes/refs/heads/master"
+'
+
+test_expect_success 'git notes get-ref (no overrides)' '
+ test_unconfig core.notesRef &&
+ sane_unset GIT_NOTES_REF &&
+ test "$(git notes get-ref)" = "refs/notes/commits"
+'
+
+test_expect_success 'git notes get-ref (core.notesRef)' '
+ test_config core.notesRef refs/notes/foo &&
+ test "$(git notes get-ref)" = "refs/notes/foo"
+'
+
+test_expect_success 'git notes get-ref (GIT_NOTES_REF)' '
+ test "$(GIT_NOTES_REF=refs/notes/bar git notes get-ref)" = "refs/notes/bar"
+'
+
+test_expect_success 'git notes get-ref (--ref)' '
+ test "$(GIT_NOTES_REF=refs/notes/bar git notes --ref=baz get-ref)" = "refs/notes/baz"
+'
+
+test_expect_success 'setup testing of empty notes' '
+ test_unconfig core.notesRef &&
+ test_commit 16th &&
+ empty_blob=$(git hash-object -w /dev/null) &&
+ echo "$empty_blob" >expect_empty
+'
+
+while read cmd
+do
+ test_expect_success "'git notes $cmd' removes empty note" "
+ test_might_fail git notes remove HEAD &&
+ MSG= git notes $cmd &&
+ test_must_fail git notes list HEAD
+ "
+
+ test_expect_success "'git notes $cmd --allow-empty' stores empty note" "
+ test_might_fail git notes remove HEAD &&
+ MSG= git notes $cmd --allow-empty &&
+ git notes list HEAD >actual &&
+ test_cmp expect_empty actual
+ "
+done <<\EOF
+add
+add -F /dev/null
+add -m ""
+add -c "$empty_blob"
+add -C "$empty_blob"
+append
+append -F /dev/null
+append -m ""
+append -c "$empty_blob"
+append -C "$empty_blob"
+edit
+EOF
+
+test_expect_success 'empty notes are displayed by git log' '
+ test_commit 17th &&
+ git log -1 >expect &&
+ cat >>expect <<-EOF &&
+
+ Notes:
+ EOF
+ git notes add -C "$empty_blob" --allow-empty &&
+ git log -1 >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh
index 361a10a..7217c5e 100755
--- a/t/t3302-notes-index-expensive.sh
+++ b/t/t3302-notes-index-expensive.sh
@@ -7,55 +7,49 @@ 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
+ while test $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
+ 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
+ cat <<-INPUT_END &&
+ commit refs/notes/commits
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ notes
+ COMMIT
-INPUT_END
+ INPUT_END
cat note_commit
) |
@@ -67,52 +61,74 @@ INPUT_END
test_notes () {
count=$1 &&
git config core.notesRef refs/notes/commits &&
- git log | grep "^ " > output &&
+ git log | grep "^ " >output &&
i=$count &&
- while [ $i -gt 0 ]; do
+ while test $i -gt 0
+ do
echo " commit #$i" &&
echo " note for commit #$i" &&
- i=$(($i-1));
- done > expect &&
+ i=$(($i-1))
+ done >expect &&
test_cmp expect output
}
-cat > time_notes << \EOF
+write_script time_notes <<\EOF
mode=$1
i=1
- while [ $i -lt $2 ]; do
+ while test $i -lt $2
+ do
case $1 in
no-notes)
- GIT_NOTES_REF=non-existing; export GIT_NOTES_REF
- ;;
+ GIT_NOTES_REF=non-existing
+ export GIT_NOTES_REF
+ ;;
notes)
unset GIT_NOTES_REF
- ;;
+ ;;
esac
- git log >/dev/null
+ git log
i=$(($i+1))
- done
+ done >/dev/null
EOF
time_notes () {
for mode in no-notes notes
do
echo $mode
- /usr/bin/time sh ../time_notes $mode $1
+ /usr/bin/time ../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"
+do_tests () {
+ count=$1 pr=${2-}
+
+ test_expect_success $pr "setup $count" '
+ mkdir "$count" &&
+ (
+ cd "$count" &&
+ create_repo "$count"
+ )
+ '
+
+ test_expect_success $pr 'notes work' '
+ (
+ cd "$count" &&
+ test_notes "$count"
+ )
+ '
+
+ test_expect_success "USR_BIN_TIME${pr:+,$pr}" 'notes timing with /usr/bin/time' '
+ (
+ cd "$count" &&
+ time_notes 100
+ )
+ '
+}
- test_expect_success 'notes timing' "time_notes 100"
- )
+do_tests 10
+for count in 100 1000 10000
+do
+ do_tests "$count" EXPENSIVE
done
test_done
diff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh
index 75ec187..704aee8 100755
--- a/t/t3303-notes-subtrees.sh
+++ b/t/t3303-notes-subtrees.sh
@@ -168,15 +168,16 @@ INPUT_END
}
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
+ git log | grep "^ " > output &&
+ i=$number_of_commits &&
+ while [ $i -gt 0 ]; do
+ echo " commit #$i" &&
+ echo " first note for commit #$i" &&
+ echo " " &&
+ 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|^..|&/|" ""'
diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh
index b1ea64b..54460be 100755
--- a/t/t3305-notes-fanout.sh
+++ b/t/t3305-notes-fanout.sh
@@ -51,15 +51,12 @@ test_expect_success 'deleting most notes with git-notes' '
num_notes=250 &&
i=0 &&
git rev-list HEAD |
- while read sha1
+ while test $i -lt $num_notes && read sha1
do
i=$(($i + 1)) &&
- if test $i -gt $num_notes
- then
- break
- fi &&
test_tick &&
- git notes remove "$sha1"
+ git notes remove "$sha1" ||
+ exit 1
done
'
diff --git a/t/t3306-notes-prune.sh b/t/t3306-notes-prune.sh
index b455404..86bf909 100755
--- a/t/t3306-notes-prune.sh
+++ b/t/t3306-notes-prune.sh
@@ -20,6 +20,9 @@ test_expect_success 'setup: create a few commits with notes' '
git add file3 &&
test_tick &&
git commit -m 3rd &&
+ COMMIT_FILE=.git/objects/5e/e1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+ test -f $COMMIT_FILE &&
+ test-chmtime =+0 $COMMIT_FILE &&
git notes add -m "Note #3"
'
@@ -67,7 +70,7 @@ test_expect_success 'remove some commits' '
test_expect_success 'verify that commits are gone' '
- ! git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+ test_must_fail git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
git cat-file -p 08341ad9e94faa089d60fd3f523affb25c6da189 &&
git cat-file -p ab5f302035f2e7aaf04265f08b42034c23256e1f
'
@@ -106,7 +109,7 @@ test_expect_success 'prune notes' '
test_expect_success 'verify that notes are gone' '
- ! git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+ test_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
'
@@ -130,8 +133,8 @@ test_expect_success 'prune -v notes' '
test_expect_success 'verify that notes are gone' '
- ! git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
- ! git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+ test_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+ test_must_fail git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
'
diff --git a/t/t3307-notes-man.sh b/t/t3307-notes-man.sh
index 3269f2e..1aa366a 100755
--- a/t/t3307-notes-man.sh
+++ b/t/t3307-notes-man.sh
@@ -26,13 +26,13 @@ test_expect_success 'example 1: notes to add an Acked-by line' '
'
test_expect_success 'example 2: binary notes' '
- cp "$TEST_DIRECTORY"/test4012.png .
+ cp "$TEST_DIRECTORY"/test-binary-1.png . &&
git checkout B &&
- blob=$(git hash-object -w test4012.png) &&
+ blob=$(git hash-object -w test-binary-1.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_cmp test-binary-1.png actual
'
test_done
diff --git a/t/t3308-notes-merge.sh b/t/t3308-notes-merge.sh
new file mode 100755
index 0000000..19aed7e
--- /dev/null
+++ b/t/t3308-notes-merge.sh
@@ -0,0 +1,368 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Johan Herland
+#
+
+test_description='Test merging of notes trees'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit 1st &&
+ test_commit 2nd &&
+ test_commit 3rd &&
+ test_commit 4th &&
+ test_commit 5th &&
+ # Create notes on 4 first commits
+ git config core.notesRef refs/notes/x &&
+ git notes add -m "Notes on 1st commit" 1st &&
+ git notes add -m "Notes on 2nd commit" 2nd &&
+ git notes add -m "Notes on 3rd commit" 3rd &&
+ git notes add -m "Notes on 4th commit" 4th &&
+ # Copy notes to remote-notes
+ git fetch . refs/notes/*:refs/remote-notes/origin/*
+'
+
+commit_sha1=$(git rev-parse 1st^{commit})
+commit_sha2=$(git rev-parse 2nd^{commit})
+commit_sha3=$(git rev-parse 3rd^{commit})
+commit_sha4=$(git rev-parse 4th^{commit})
+commit_sha5=$(git rev-parse 5th^{commit})
+
+verify_notes () {
+ notes_ref="$1"
+ git -c core.notesRef="refs/notes/$notes_ref" notes |
+ sort >"output_notes_$notes_ref" &&
+ test_cmp "expect_notes_$notes_ref" "output_notes_$notes_ref" &&
+ git -c core.notesRef="refs/notes/$notes_ref" log --format="%H %s%n%N" \
+ >"output_log_$notes_ref" &&
+ test_cmp "expect_log_$notes_ref" "output_log_$notes_ref"
+}
+
+cat <<EOF | sort >expect_notes_x
+5e93d24084d32e1cb61f7070505b9d2530cca987 $commit_sha4
+8366731eeee53787d2bdf8fc1eff7d94757e8da0 $commit_sha3
+eede89064cd42441590d6afec6c37b321ada3389 $commit_sha2
+daa55ffad6cb99bf64226532147ffcaf5ce8bdd1 $commit_sha1
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+Notes on 4th commit
+
+$commit_sha3 3rd
+Notes on 3rd commit
+
+$commit_sha2 2nd
+Notes on 2nd commit
+
+$commit_sha1 1st
+Notes on 1st commit
+
+EOF
+
+test_expect_success 'verify initial notes (x)' '
+ verify_notes x
+'
+
+cp expect_notes_x expect_notes_y
+cp expect_notes_x expect_notes_v
+cp expect_log_x expect_log_y
+cp expect_log_x expect_log_v
+
+test_expect_success 'fail to merge empty notes ref into empty notes ref (z => y)' '
+ test_must_fail git -c "core.notesRef=refs/notes/y" notes merge z
+'
+
+test_expect_success 'fail to merge into various non-notes refs' '
+ test_must_fail git -c "core.notesRef=refs/notes" notes merge x &&
+ test_must_fail git -c "core.notesRef=refs/notes/" notes merge x &&
+ mkdir -p .git/refs/notes/dir &&
+ test_must_fail git -c "core.notesRef=refs/notes/dir" notes merge x &&
+ test_must_fail git -c "core.notesRef=refs/notes/dir/" notes merge x &&
+ test_must_fail git -c "core.notesRef=refs/heads/master" notes merge x &&
+ test_must_fail git -c "core.notesRef=refs/notes/y:" notes merge x &&
+ test_must_fail git -c "core.notesRef=refs/notes/y:foo" notes merge x &&
+ test_must_fail git -c "core.notesRef=refs/notes/foo^{bar" notes merge x
+'
+
+test_expect_success 'merge non-notes ref into empty notes ref (remote-notes/origin/x => v)' '
+ git config core.notesRef refs/notes/v &&
+ git notes merge refs/remote-notes/origin/x &&
+ verify_notes v &&
+ # refs/remote-notes/origin/x and v should point to the same notes commit
+ test "$(git rev-parse refs/remote-notes/origin/x)" = "$(git rev-parse refs/notes/v)"
+'
+
+test_expect_success 'merge notes into empty notes ref (x => y)' '
+ git config core.notesRef refs/notes/y &&
+ git notes merge x &&
+ verify_notes y &&
+ # x and y should point to the same notes commit
+ test "$(git rev-parse refs/notes/x)" = "$(git rev-parse refs/notes/y)"
+'
+
+test_expect_success 'merge empty notes ref (z => y)' '
+ git notes merge z &&
+ # y should not change (still == x)
+ test "$(git rev-parse refs/notes/x)" = "$(git rev-parse refs/notes/y)"
+'
+
+test_expect_success 'change notes on other notes ref (y)' '
+ # Not touching notes to 1st commit
+ git notes remove 2nd &&
+ git notes append -m "More notes on 3rd commit" 3rd &&
+ git notes add -f -m "New notes on 4th commit" 4th &&
+ git notes add -m "Notes on 5th commit" 5th
+'
+
+test_expect_success 'merge previous notes commit (y^ => y) => No-op' '
+ pre_state="$(git rev-parse refs/notes/y)" &&
+ git notes merge y^ &&
+ # y should not move
+ test "$pre_state" = "$(git rev-parse refs/notes/y)"
+'
+
+cat <<EOF | sort >expect_notes_y
+0f2efbd00262f2fd41dfae33df8765618eeacd99 $commit_sha5
+dec2502dac3ea161543f71930044deff93fa945c $commit_sha4
+4069cdb399fd45463ec6eef8e051a16a03592d91 $commit_sha3
+daa55ffad6cb99bf64226532147ffcaf5ce8bdd1 $commit_sha1
+EOF
+
+cat >expect_log_y <<EOF
+$commit_sha5 5th
+Notes on 5th commit
+
+$commit_sha4 4th
+New notes on 4th commit
+
+$commit_sha3 3rd
+Notes on 3rd commit
+
+More notes on 3rd commit
+
+$commit_sha2 2nd
+
+$commit_sha1 1st
+Notes on 1st commit
+
+EOF
+
+test_expect_success 'verify changed notes on other notes ref (y)' '
+ verify_notes y
+'
+
+test_expect_success 'verify unchanged notes on original notes ref (x)' '
+ verify_notes x
+'
+
+test_expect_success 'merge original notes (x) into changed notes (y) => No-op' '
+ git notes merge -vvv x &&
+ verify_notes y &&
+ verify_notes x
+'
+
+cp expect_notes_y expect_notes_x
+cp expect_log_y expect_log_x
+
+test_expect_success 'merge changed (y) into original (x) => Fast-forward' '
+ git config core.notesRef refs/notes/x &&
+ git notes merge y &&
+ verify_notes x &&
+ verify_notes y &&
+ # x and y should point to same the notes commit
+ test "$(git rev-parse refs/notes/x)" = "$(git rev-parse refs/notes/y)"
+'
+
+test_expect_success 'merge empty notes ref (z => y)' '
+ # Prepare empty (but valid) notes ref (z)
+ git config core.notesRef refs/notes/z &&
+ git notes add -m "foo" &&
+ git notes remove &&
+ git notes >output_notes_z &&
+ test_cmp /dev/null output_notes_z &&
+ # Do the merge (z => y)
+ git config core.notesRef refs/notes/y &&
+ git notes merge z &&
+ verify_notes y &&
+ # y should no longer point to the same notes commit as x
+ test "$(git rev-parse refs/notes/x)" != "$(git rev-parse refs/notes/y)"
+'
+
+cat <<EOF | sort >expect_notes_y
+0f2efbd00262f2fd41dfae33df8765618eeacd99 $commit_sha5
+dec2502dac3ea161543f71930044deff93fa945c $commit_sha4
+4069cdb399fd45463ec6eef8e051a16a03592d91 $commit_sha3
+d000d30e6ddcfce3a8122c403226a2ce2fd04d9d $commit_sha2
+43add6bd0c8c0bc871ac7991e0f5573cfba27804 $commit_sha1
+EOF
+
+cat >expect_log_y <<EOF
+$commit_sha5 5th
+Notes on 5th commit
+
+$commit_sha4 4th
+New notes on 4th commit
+
+$commit_sha3 3rd
+Notes on 3rd commit
+
+More notes on 3rd commit
+
+$commit_sha2 2nd
+New notes on 2nd commit
+
+$commit_sha1 1st
+Notes on 1st commit
+
+More notes on 1st commit
+
+EOF
+
+test_expect_success 'change notes on other notes ref (y)' '
+ # Append to 1st commit notes
+ git notes append -m "More notes on 1st commit" 1st &&
+ # Add new notes to 2nd commit
+ git notes add -m "New notes on 2nd commit" 2nd &&
+ verify_notes y
+'
+
+cat <<EOF | sort >expect_notes_x
+0f2efbd00262f2fd41dfae33df8765618eeacd99 $commit_sha5
+1f257a3a90328557c452f0817d6cc50c89d315d4 $commit_sha4
+daa55ffad6cb99bf64226532147ffcaf5ce8bdd1 $commit_sha1
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 5th
+Notes on 5th commit
+
+$commit_sha4 4th
+New notes on 4th commit
+
+More notes on 4th commit
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+
+$commit_sha1 1st
+Notes on 1st commit
+
+EOF
+
+test_expect_success 'change notes on notes ref (x)' '
+ git config core.notesRef refs/notes/x &&
+ git notes remove 3rd &&
+ git notes append -m "More notes on 4th commit" 4th &&
+ verify_notes x
+'
+
+cat <<EOF | sort >expect_notes_x
+0f2efbd00262f2fd41dfae33df8765618eeacd99 $commit_sha5
+1f257a3a90328557c452f0817d6cc50c89d315d4 $commit_sha4
+d000d30e6ddcfce3a8122c403226a2ce2fd04d9d $commit_sha2
+43add6bd0c8c0bc871ac7991e0f5573cfba27804 $commit_sha1
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 5th
+Notes on 5th commit
+
+$commit_sha4 4th
+New notes on 4th commit
+
+More notes on 4th commit
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+New notes on 2nd commit
+
+$commit_sha1 1st
+Notes on 1st commit
+
+More notes on 1st commit
+
+EOF
+
+test_expect_success 'merge y into x => Non-conflicting 3-way merge' '
+ git notes merge y &&
+ verify_notes x &&
+ verify_notes y
+'
+
+cat <<EOF | sort >expect_notes_w
+05a4927951bcef347f51486575b878b2b60137f2 $commit_sha3
+d000d30e6ddcfce3a8122c403226a2ce2fd04d9d $commit_sha2
+EOF
+
+cat >expect_log_w <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+
+$commit_sha3 3rd
+New notes on 3rd commit
+
+$commit_sha2 2nd
+New notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'create notes on new, separate notes ref (w)' '
+ git config core.notesRef refs/notes/w &&
+ # Add same note as refs/notes/y on 2nd commit
+ git notes add -m "New notes on 2nd commit" 2nd &&
+ # Add new note on 3rd commit (non-conflicting)
+ git notes add -m "New notes on 3rd commit" 3rd &&
+ # Verify state of notes on new, separate notes ref (w)
+ verify_notes w
+'
+
+cat <<EOF | sort >expect_notes_x
+0f2efbd00262f2fd41dfae33df8765618eeacd99 $commit_sha5
+1f257a3a90328557c452f0817d6cc50c89d315d4 $commit_sha4
+05a4927951bcef347f51486575b878b2b60137f2 $commit_sha3
+d000d30e6ddcfce3a8122c403226a2ce2fd04d9d $commit_sha2
+43add6bd0c8c0bc871ac7991e0f5573cfba27804 $commit_sha1
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 5th
+Notes on 5th commit
+
+$commit_sha4 4th
+New notes on 4th commit
+
+More notes on 4th commit
+
+$commit_sha3 3rd
+New notes on 3rd commit
+
+$commit_sha2 2nd
+New notes on 2nd commit
+
+$commit_sha1 1st
+Notes on 1st commit
+
+More notes on 1st commit
+
+EOF
+
+test_expect_success 'merge w into x => Non-conflicting history-less merge' '
+ git config core.notesRef refs/notes/x &&
+ git notes merge w &&
+ # Verify new state of notes on other notes ref (x)
+ verify_notes x &&
+ # Also verify that nothing changed on other notes refs (y and w)
+ verify_notes y &&
+ verify_notes w
+'
+
+test_done
diff --git a/t/t3309-notes-merge-auto-resolve.sh b/t/t3309-notes-merge-auto-resolve.sh
new file mode 100755
index 0000000..14c2adf
--- /dev/null
+++ b/t/t3309-notes-merge-auto-resolve.sh
@@ -0,0 +1,726 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Johan Herland
+#
+
+test_description='Test notes merging with auto-resolving strategies'
+
+. ./test-lib.sh
+
+# Set up a notes merge scenario with all kinds of potential conflicts
+test_expect_success 'setup commits' '
+ test_commit 1st &&
+ test_commit 2nd &&
+ test_commit 3rd &&
+ test_commit 4th &&
+ test_commit 5th &&
+ test_commit 6th &&
+ test_commit 7th &&
+ test_commit 8th &&
+ test_commit 9th &&
+ test_commit 10th &&
+ test_commit 11th &&
+ test_commit 12th &&
+ test_commit 13th &&
+ test_commit 14th &&
+ test_commit 15th
+'
+
+commit_sha1=$(git rev-parse 1st^{commit})
+commit_sha2=$(git rev-parse 2nd^{commit})
+commit_sha3=$(git rev-parse 3rd^{commit})
+commit_sha4=$(git rev-parse 4th^{commit})
+commit_sha5=$(git rev-parse 5th^{commit})
+commit_sha6=$(git rev-parse 6th^{commit})
+commit_sha7=$(git rev-parse 7th^{commit})
+commit_sha8=$(git rev-parse 8th^{commit})
+commit_sha9=$(git rev-parse 9th^{commit})
+commit_sha10=$(git rev-parse 10th^{commit})
+commit_sha11=$(git rev-parse 11th^{commit})
+commit_sha12=$(git rev-parse 12th^{commit})
+commit_sha13=$(git rev-parse 13th^{commit})
+commit_sha14=$(git rev-parse 14th^{commit})
+commit_sha15=$(git rev-parse 15th^{commit})
+
+verify_notes () {
+ notes_ref="$1"
+ suffix="$2"
+ git -c core.notesRef="refs/notes/$notes_ref" notes |
+ sort >"output_notes_$suffix" &&
+ test_cmp "expect_notes_$suffix" "output_notes_$suffix" &&
+ git -c core.notesRef="refs/notes/$notes_ref" log --format="%H %s%n%N" \
+ >"output_log_$suffix" &&
+ test_cmp "expect_log_$suffix" "output_log_$suffix"
+}
+
+test_expect_success 'setup merge base (x)' '
+ git config core.notesRef refs/notes/x &&
+ git notes add -m "x notes on 6th commit" 6th &&
+ git notes add -m "x notes on 7th commit" 7th &&
+ git notes add -m "x notes on 8th commit" 8th &&
+ git notes add -m "x notes on 9th commit" 9th &&
+ git notes add -m "x notes on 10th commit" 10th &&
+ git notes add -m "x notes on 11th commit" 11th &&
+ git notes add -m "x notes on 12th commit" 12th &&
+ git notes add -m "x notes on 13th commit" 13th &&
+ git notes add -m "x notes on 14th commit" 14th &&
+ git notes add -m "x notes on 15th commit" 15th
+'
+
+cat <<EOF | sort >expect_notes_x
+457a85d6c814ea208550f15fcc48f804ac8dc023 $commit_sha15
+b0c95b954301d69da2bc3723f4cb1680d355937c $commit_sha14
+5d30216a129eeffa97d9694ffe8c74317a560315 $commit_sha13
+dd161bc149470fd890dd4ab52a4cbd79bbd18c36 $commit_sha12
+7abbc45126d680336fb24294f013a7cdfa3ed545 $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+20c613c835011c48a5abe29170a2402ca6354910 $commit_sha9
+a3daf8a1e4e5dc3409a303ad8481d57bfea7f5d6 $commit_sha8
+897003322b53bc6ca098e9324ee508362347e734 $commit_sha7
+11d97fdebfa5ceee540a3da07bce6fa0222bc082 $commit_sha6
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha15 15th
+x notes on 15th commit
+
+$commit_sha14 14th
+x notes on 14th commit
+
+$commit_sha13 13th
+x notes on 13th commit
+
+$commit_sha12 12th
+x notes on 12th commit
+
+$commit_sha11 11th
+x notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+x notes on 9th commit
+
+$commit_sha8 8th
+x notes on 8th commit
+
+$commit_sha7 7th
+x notes on 7th commit
+
+$commit_sha6 6th
+x notes on 6th commit
+
+$commit_sha5 5th
+
+$commit_sha4 4th
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'verify state of merge base (x)' 'verify_notes x x'
+
+test_expect_success 'setup local branch (y)' '
+ git update-ref refs/notes/y refs/notes/x &&
+ git config core.notesRef refs/notes/y &&
+ git notes add -f -m "y notes on 3rd commit" 3rd &&
+ git notes add -f -m "y notes on 4th commit" 4th &&
+ git notes add -f -m "y notes on 5th commit" 5th &&
+ git notes remove 6th &&
+ git notes remove 7th &&
+ git notes remove 8th &&
+ git notes add -f -m "y notes on 12th commit" 12th &&
+ git notes add -f -m "y notes on 13th commit" 13th &&
+ git notes add -f -m "y notes on 14th commit" 14th &&
+ git notes add -f -m "y notes on 15th commit" 15th
+'
+
+cat <<EOF | sort >expect_notes_y
+68b8630d25516028bed862719855b3d6768d7833 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7abbc45126d680336fb24294f013a7cdfa3ed545 $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+20c613c835011c48a5abe29170a2402ca6354910 $commit_sha9
+154508c7a0bcad82b6fe4b472bc4c26b3bf0825b $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+EOF
+
+cat >expect_log_y <<EOF
+$commit_sha15 15th
+y notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+x notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+x notes on 9th commit
+
+$commit_sha8 8th
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+y notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'verify state of local branch (y)' 'verify_notes y y'
+
+test_expect_success 'setup remote branch (z)' '
+ git update-ref refs/notes/z refs/notes/x &&
+ git config core.notesRef refs/notes/z &&
+ git notes add -f -m "z notes on 2nd commit" 2nd &&
+ git notes add -f -m "y notes on 4th commit" 4th &&
+ git notes add -f -m "z notes on 5th commit" 5th &&
+ git notes remove 6th &&
+ git notes add -f -m "z notes on 8th commit" 8th &&
+ git notes remove 9th &&
+ git notes add -f -m "z notes on 11th commit" 11th &&
+ git notes remove 12th &&
+ git notes add -f -m "y notes on 14th commit" 14th &&
+ git notes add -f -m "z notes on 15th commit" 15th
+'
+
+cat <<EOF | sort >expect_notes_z
+9b4b2c61f0615412da3c10f98ff85b57c04ec765 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+5d30216a129eeffa97d9694ffe8c74317a560315 $commit_sha13
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+897003322b53bc6ca098e9324ee508362347e734 $commit_sha7
+99fc34adfc400b95c67b013115e37e31aa9a6d23 $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_z <<EOF
+$commit_sha15 15th
+z notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+x notes on 13th commit
+
+$commit_sha12 12th
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+x notes on 7th commit
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+z notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'verify state of remote branch (z)' 'verify_notes z z'
+
+# At this point, before merging z into y, we have the following status:
+#
+# commit | base/x | local/y | remote/z | diff from x to y/z | result
+# -------|---------|---------|----------|----------------------------|-------
+# 1st | [none] | [none] | [none] | unchanged / unchanged | [none]
+# 2nd | [none] | [none] | 283b482 | unchanged / added | 283b482
+# 3rd | [none] | 5772f42 | [none] | added / unchanged | 5772f42
+# 4th | [none] | e2bfd06 | e2bfd06 | added / added (same) | e2bfd06
+# 5th | [none] | 154508c | 99fc34a | added / added (diff) | ???
+# 6th | 11d97fd | [none] | [none] | removed / removed | [none]
+# 7th | 8970033 | [none] | 8970033 | removed / unchanged | [none]
+# 8th | a3daf8a | [none] | 851e163 | removed / changed | ???
+# 9th | 20c613c | 20c613c | [none] | unchanged / removed | [none]
+# 10th | b8d03e1 | b8d03e1 | b8d03e1 | unchanged / unchanged | b8d03e1
+# 11th | 7abbc45 | 7abbc45 | 7e3c535 | unchanged / changed | 7e3c535
+# 12th | dd161bc | a66055f | [none] | changed / removed | ???
+# 13th | 5d30216 | 3a631fd | 5d30216 | changed / unchanged | 3a631fd
+# 14th | b0c95b9 | 5de7ea7 | 5de7ea7 | changed / changed (same) | 5de7ea7
+# 15th | 457a85d | 68b8630 | 9b4b2c6 | changed / changed (diff) | ???
+
+test_expect_success 'merge z into y with invalid strategy => Fail/No changes' '
+ git config core.notesRef refs/notes/y &&
+ test_must_fail git notes merge --strategy=foo z &&
+ # Verify no changes (y)
+ verify_notes y y
+'
+
+test_expect_success 'merge z into y with invalid configuration option => Fail/No changes' '
+ git config core.notesRef refs/notes/y &&
+ test_must_fail git -c notes.mergeStrategy="foo" notes merge z &&
+ # Verify no changes (y)
+ verify_notes y y
+'
+
+cat <<EOF | sort >expect_notes_ours
+68b8630d25516028bed862719855b3d6768d7833 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+154508c7a0bcad82b6fe4b472bc4c26b3bf0825b $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_ours <<EOF
+$commit_sha15 15th
+y notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+y notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge z into y with "ours" strategy => Non-conflicting 3-way merge' '
+ git notes merge --strategy=ours z &&
+ verify_notes y ours
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+ git update-ref refs/notes/y refs/notes/y^1 &&
+ # Verify pre-merge state
+ verify_notes y y
+'
+
+test_expect_success 'merge z into y with "ours" configuration option => Non-conflicting 3-way merge' '
+ git -c notes.mergeStrategy="ours" notes merge z &&
+ verify_notes y ours
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+ git update-ref refs/notes/y refs/notes/y^1 &&
+ # Verify pre-merge state
+ verify_notes y y
+'
+
+test_expect_success 'merge z into y with "ours" per-ref configuration option => Non-conflicting 3-way merge' '
+ git -c notes.y.mergeStrategy="ours" notes merge z &&
+ verify_notes y ours
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+ git update-ref refs/notes/y refs/notes/y^1 &&
+ # Verify pre-merge state
+ verify_notes y y
+'
+
+cat <<EOF | sort >expect_notes_theirs
+9b4b2c61f0615412da3c10f98ff85b57c04ec765 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+99fc34adfc400b95c67b013115e37e31aa9a6d23 $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_theirs <<EOF
+$commit_sha15 15th
+z notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+z notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge z into y with "theirs" strategy => Non-conflicting 3-way merge' '
+ git notes merge --strategy=theirs z &&
+ verify_notes y theirs
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+ git update-ref refs/notes/y refs/notes/y^1 &&
+ # Verify pre-merge state
+ verify_notes y y
+'
+
+test_expect_success 'merge z into y with "theirs" strategy overriding configuration option "ours" => Non-conflicting 3-way merge' '
+ git -c notes.mergeStrategy="ours" notes merge --strategy=theirs z &&
+ verify_notes y theirs
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+ git update-ref refs/notes/y refs/notes/y^1 &&
+ # Verify pre-merge state
+ verify_notes y y
+'
+
+cat <<EOF | sort >expect_notes_union
+7c4e546efd0fe939f876beb262ece02797880b54 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+6c841cc36ea496027290967ca96bd2bef54dbb47 $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_union <<EOF
+$commit_sha15 15th
+y notes on 15th commit
+
+z notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+y notes on 5th commit
+
+z notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge z into y with "union" strategy => Non-conflicting 3-way merge' '
+ git notes merge --strategy=union z &&
+ verify_notes y union
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+ git update-ref refs/notes/y refs/notes/y^1 &&
+ # Verify pre-merge state
+ verify_notes y y
+'
+
+test_expect_success 'merge z into y with "union" strategy overriding per-ref configuration => Non-conflicting 3-way merge' '
+ git -c notes.y.mergeStrategy="theirs" notes merge --strategy=union z &&
+ verify_notes y union
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+ git update-ref refs/notes/y refs/notes/y^1 &&
+ # Verify pre-merge state
+ verify_notes y y
+'
+
+test_expect_success 'merge z into y with "union" per-ref overriding general configuration => Non-conflicting 3-way merge' '
+ git -c notes.y.mergeStrategy="union" -c notes.mergeStrategy="theirs" notes merge z &&
+ verify_notes y union
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+ git update-ref refs/notes/y refs/notes/y^1 &&
+ # Verify pre-merge state
+ verify_notes y y
+'
+
+test_expect_success 'merge z into y with "manual" per-ref only checks specific ref configuration => Conflicting 3-way merge' '
+ test_must_fail git -c notes.z.mergeStrategy="union" notes merge z &&
+ git notes merge --abort &&
+ verify_notes y y
+'
+
+cat <<EOF | sort >expect_notes_union2
+d682107b8bf7a7aea1e537a8d5cb6a12b60135f1 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+357b6ca14c7afd59b7f8b8aaaa6b8b723771135b $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_union2 <<EOF
+$commit_sha15 15th
+z notes on 15th commit
+
+y notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+z notes on 5th commit
+
+y notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge y into z with "union" strategy => Non-conflicting 3-way merge' '
+ git config core.notesRef refs/notes/z &&
+ git notes merge --strategy=union y &&
+ verify_notes z union2
+'
+
+test_expect_success 'reset to pre-merge state (z)' '
+ git update-ref refs/notes/z refs/notes/z^1 &&
+ # Verify pre-merge state
+ verify_notes z z
+'
+
+cat <<EOF | sort >expect_notes_cat_sort_uniq
+6be90240b5f54594203e25d9f2f64b7567175aee $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+660311d7f78dc53db12ac373a43fca7465381a7e $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_cat_sort_uniq <<EOF
+$commit_sha15 15th
+y notes on 15th commit
+z notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+y notes on 5th commit
+z notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge y into z with "cat_sort_uniq" strategy => Non-conflicting 3-way merge' '
+ git notes merge --strategy=cat_sort_uniq y &&
+ verify_notes z cat_sort_uniq
+'
+
+test_expect_success 'reset to pre-merge state (z)' '
+ git update-ref refs/notes/z refs/notes/z^1 &&
+ # Verify pre-merge state
+ verify_notes z z
+'
+
+test_expect_success 'merge y into z with "cat_sort_uniq" strategy configuration option => Non-conflicting 3-way merge' '
+ git -c notes.mergeStrategy="cat_sort_uniq" notes merge y &&
+ verify_notes z cat_sort_uniq
+'
+
+test_done
diff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh
new file mode 100755
index 0000000..d557212
--- /dev/null
+++ b/t/t3310-notes-merge-manual-resolve.sh
@@ -0,0 +1,587 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Johan Herland
+#
+
+test_description='Test notes merging with manual conflict resolution'
+
+. ./test-lib.sh
+
+# Set up a notes merge scenario with different kinds of conflicts
+test_expect_success 'setup commits' '
+ test_commit 1st &&
+ test_commit 2nd &&
+ test_commit 3rd &&
+ test_commit 4th &&
+ test_commit 5th
+'
+
+commit_sha1=$(git rev-parse 1st^{commit})
+commit_sha2=$(git rev-parse 2nd^{commit})
+commit_sha3=$(git rev-parse 3rd^{commit})
+commit_sha4=$(git rev-parse 4th^{commit})
+commit_sha5=$(git rev-parse 5th^{commit})
+
+verify_notes () {
+ notes_ref="$1"
+ git -c core.notesRef="refs/notes/$notes_ref" notes |
+ sort >"output_notes_$notes_ref" &&
+ test_cmp "expect_notes_$notes_ref" "output_notes_$notes_ref" &&
+ git -c core.notesRef="refs/notes/$notes_ref" log --format="%H %s%n%N" \
+ >"output_log_$notes_ref" &&
+ test_cmp "expect_log_$notes_ref" "output_log_$notes_ref"
+}
+
+cat <<EOF | sort >expect_notes_x
+6e8e3febca3c2bb896704335cc4d0c34cb2f8715 $commit_sha4
+e5388c10860456ee60673025345fe2e153eb8cf8 $commit_sha3
+ceefa674873670e7ecd131814d909723cce2b669 $commit_sha2
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+x notes on 4th commit
+
+$commit_sha3 3rd
+x notes on 3rd commit
+
+$commit_sha2 2nd
+x notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'setup merge base (x)' '
+ git config core.notesRef refs/notes/x &&
+ git notes add -m "x notes on 2nd commit" 2nd &&
+ git notes add -m "x notes on 3rd commit" 3rd &&
+ git notes add -m "x notes on 4th commit" 4th &&
+ verify_notes x
+'
+
+cat <<EOF | sort >expect_notes_y
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+b0a6021ec006d07e80e9b20ec9b444cbd9d560d3 $commit_sha1
+EOF
+
+cat >expect_log_y <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+
+$commit_sha1 1st
+y notes on 1st commit
+
+EOF
+
+test_expect_success 'setup local branch (y)' '
+ git update-ref refs/notes/y refs/notes/x &&
+ git config core.notesRef refs/notes/y &&
+ git notes add -f -m "y notes on 1st commit" 1st &&
+ git notes remove 2nd &&
+ git notes add -f -m "y notes on 3rd commit" 3rd &&
+ git notes add -f -m "y notes on 4th commit" 4th &&
+ verify_notes y
+'
+
+cat <<EOF | sort >expect_notes_z
+cff59c793c20bb49a4e01bc06fb06bad642e0d54 $commit_sha4
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+0a81da8956346e19bcb27a906f04af327e03e31b $commit_sha1
+EOF
+
+cat >expect_log_z <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+z notes on 4th commit
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+z notes on 1st commit
+
+EOF
+
+test_expect_success 'setup remote branch (z)' '
+ git update-ref refs/notes/z refs/notes/x &&
+ git config core.notesRef refs/notes/z &&
+ git notes add -f -m "z notes on 1st commit" 1st &&
+ git notes add -f -m "z notes on 2nd commit" 2nd &&
+ git notes remove 3rd &&
+ git notes add -f -m "z notes on 4th commit" 4th &&
+ verify_notes z
+'
+
+# At this point, before merging z into y, we have the following status:
+#
+# commit | base/x | local/y | remote/z | diff from x to y/z
+# -------|---------|---------|----------|---------------------------
+# 1st | [none] | b0a6021 | 0a81da8 | added / added (diff)
+# 2nd | ceefa67 | [none] | 283b482 | removed / changed
+# 3rd | e5388c1 | 5772f42 | [none] | changed / removed
+# 4th | 6e8e3fe | e2bfd06 | cff59c7 | changed / changed (diff)
+# 5th | [none] | [none] | [none] | [none]
+
+cat <<EOF | sort >expect_conflicts
+$commit_sha1
+$commit_sha2
+$commit_sha3
+$commit_sha4
+EOF
+
+cat >expect_conflict_$commit_sha1 <<EOF
+<<<<<<< refs/notes/m
+y notes on 1st commit
+=======
+z notes on 1st commit
+>>>>>>> refs/notes/z
+EOF
+
+cat >expect_conflict_$commit_sha2 <<EOF
+z notes on 2nd commit
+EOF
+
+cat >expect_conflict_$commit_sha3 <<EOF
+y notes on 3rd commit
+EOF
+
+cat >expect_conflict_$commit_sha4 <<EOF
+<<<<<<< refs/notes/m
+y notes on 4th commit
+=======
+z notes on 4th commit
+>>>>>>> refs/notes/z
+EOF
+
+cp expect_notes_y expect_notes_m
+cp expect_log_y expect_log_m
+
+git rev-parse refs/notes/y > pre_merge_y
+git rev-parse refs/notes/z > pre_merge_z
+
+test_expect_success 'merge z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
+ git update-ref refs/notes/m refs/notes/y &&
+ git config core.notesRef refs/notes/m &&
+ test_must_fail git notes merge z >output &&
+ # Output should point to where to resolve conflicts
+ grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
+ # Inspect merge conflicts
+ ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+ test_cmp expect_conflicts output_conflicts &&
+ ( for f in $(cat expect_conflicts); do
+ test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+ exit 1
+ done ) &&
+ # Verify that current notes tree (pre-merge) has not changed (m == y)
+ verify_notes y &&
+ verify_notes m &&
+ test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
+'
+
+cat <<EOF | sort >expect_notes_z
+00494adecf2d9635a02fa431308d67993f853968 $commit_sha4
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+0a81da8956346e19bcb27a906f04af327e03e31b $commit_sha1
+EOF
+
+cat >expect_log_z <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+z notes on 4th commit
+
+More z notes on 4th commit
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+z notes on 1st commit
+
+EOF
+
+test_expect_success 'change notes in z' '
+ git notes --ref z append -m "More z notes on 4th commit" 4th &&
+ verify_notes z
+'
+
+test_expect_success 'cannot do merge w/conflicts when previous merge is unfinished' '
+ test -d .git/NOTES_MERGE_WORKTREE &&
+ test_must_fail git notes merge z >output 2>&1 &&
+ # Output should indicate what is wrong
+ grep -q "\\.git/NOTES_MERGE_\\* exists" output
+'
+
+# Setup non-conflicting merge between x and new notes ref w
+
+cat <<EOF | sort >expect_notes_w
+ceefa674873670e7ecd131814d909723cce2b669 $commit_sha2
+f75d1df88cbfe4258d49852f26cfc83f2ad4494b $commit_sha1
+EOF
+
+cat >expect_log_w <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+x notes on 2nd commit
+
+$commit_sha1 1st
+w notes on 1st commit
+
+EOF
+
+test_expect_success 'setup unrelated notes ref (w)' '
+ git config core.notesRef refs/notes/w &&
+ git notes add -m "w notes on 1st commit" 1st &&
+ git notes add -m "x notes on 2nd commit" 2nd &&
+ verify_notes w
+'
+
+cat <<EOF | sort >expect_notes_w
+6e8e3febca3c2bb896704335cc4d0c34cb2f8715 $commit_sha4
+e5388c10860456ee60673025345fe2e153eb8cf8 $commit_sha3
+ceefa674873670e7ecd131814d909723cce2b669 $commit_sha2
+f75d1df88cbfe4258d49852f26cfc83f2ad4494b $commit_sha1
+EOF
+
+cat >expect_log_w <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+x notes on 4th commit
+
+$commit_sha3 3rd
+x notes on 3rd commit
+
+$commit_sha2 2nd
+x notes on 2nd commit
+
+$commit_sha1 1st
+w notes on 1st commit
+
+EOF
+
+test_expect_success 'can do merge without conflicts even if previous merge is unfinished (x => w)' '
+ test -d .git/NOTES_MERGE_WORKTREE &&
+ git notes merge x &&
+ verify_notes w &&
+ # Verify that other notes refs has not changed (x and y)
+ verify_notes x &&
+ verify_notes y
+'
+
+cat <<EOF | sort >expect_notes_m
+021faa20e931fb48986ffc6282b4bb05553ac946 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+0a59e787e6d688aa6309e56e8c1b89431a0fc1c1 $commit_sha1
+EOF
+
+cat >expect_log_m <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+y and z notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+y and z notes on 1st commit
+
+EOF
+
+test_expect_success 'do not allow mixing --commit and --abort' '
+ test_must_fail git notes merge --commit --abort
+'
+
+test_expect_success 'do not allow mixing --commit and --strategy' '
+ test_must_fail git notes merge --commit --strategy theirs
+'
+
+test_expect_success 'do not allow mixing --abort and --strategy' '
+ test_must_fail git notes merge --abort --strategy theirs
+'
+
+test_expect_success 'finalize conflicting merge (z => m)' '
+ # Resolve conflicts and finalize merge
+ cat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&
+y and z notes on 1st commit
+EOF
+ cat >.git/NOTES_MERGE_WORKTREE/$commit_sha4 <<EOF &&
+y and z notes on 4th commit
+EOF
+ git notes merge --commit &&
+ # No .git/NOTES_MERGE_* files left
+ test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+ test_cmp /dev/null output &&
+ # Merge commit has pre-merge y and pre-merge z as parents
+ test "$(git rev-parse refs/notes/m^1)" = "$(cat pre_merge_y)" &&
+ test "$(git rev-parse refs/notes/m^2)" = "$(cat pre_merge_z)" &&
+ # Merge commit mentions the notes refs merged
+ git log -1 --format=%B refs/notes/m > merge_commit_msg &&
+ grep -q refs/notes/m merge_commit_msg &&
+ grep -q refs/notes/z merge_commit_msg &&
+ # Merge commit mentions conflicting notes
+ grep -q "Conflicts" merge_commit_msg &&
+ ( for sha1 in $(cat expect_conflicts); do
+ grep -q "$sha1" merge_commit_msg ||
+ exit 1
+ done ) &&
+ # Verify contents of merge result
+ verify_notes m &&
+ # Verify that other notes refs has not changed (w, x, y and z)
+ verify_notes w &&
+ verify_notes x &&
+ verify_notes y &&
+ verify_notes z
+'
+
+cat >expect_conflict_$commit_sha4 <<EOF
+<<<<<<< refs/notes/m
+y notes on 4th commit
+=======
+z notes on 4th commit
+
+More z notes on 4th commit
+>>>>>>> refs/notes/z
+EOF
+
+cp expect_notes_y expect_notes_m
+cp expect_log_y expect_log_m
+
+git rev-parse refs/notes/y > pre_merge_y
+git rev-parse refs/notes/z > pre_merge_z
+
+test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
+ git update-ref refs/notes/m refs/notes/y &&
+ git config core.notesRef refs/notes/m &&
+ test_must_fail git notes merge z >output &&
+ # Output should point to where to resolve conflicts
+ grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
+ # Inspect merge conflicts
+ ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+ test_cmp expect_conflicts output_conflicts &&
+ ( for f in $(cat expect_conflicts); do
+ test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+ exit 1
+ done ) &&
+ # Verify that current notes tree (pre-merge) has not changed (m == y)
+ verify_notes y &&
+ verify_notes m &&
+ test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
+'
+
+test_expect_success 'abort notes merge' '
+ git notes merge --abort &&
+ # No .git/NOTES_MERGE_* files left
+ test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+ test_cmp /dev/null output &&
+ # m has not moved (still == y)
+ test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)" &&
+ # Verify that other notes refs has not changed (w, x, y and z)
+ verify_notes w &&
+ verify_notes x &&
+ verify_notes y &&
+ verify_notes z
+'
+
+git rev-parse refs/notes/y > pre_merge_y
+git rev-parse refs/notes/z > pre_merge_z
+
+test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
+ test_must_fail git notes merge z >output &&
+ # Output should point to where to resolve conflicts
+ grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
+ # Inspect merge conflicts
+ ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+ test_cmp expect_conflicts output_conflicts &&
+ ( for f in $(cat expect_conflicts); do
+ test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+ exit 1
+ done ) &&
+ # Verify that current notes tree (pre-merge) has not changed (m == y)
+ verify_notes y &&
+ verify_notes m &&
+ test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
+'
+
+cat <<EOF | sort >expect_notes_m
+304dfb4325cf243025b9957486eb605a9b51c199 $commit_sha5
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+0a59e787e6d688aa6309e56e8c1b89431a0fc1c1 $commit_sha1
+EOF
+
+cat >expect_log_m <<EOF
+$commit_sha5 5th
+new note on 5th commit
+
+$commit_sha4 4th
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+y and z notes on 1st commit
+
+EOF
+
+test_expect_success 'add + remove notes in finalized merge (z => m)' '
+ # Resolve one conflict
+ cat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&
+y and z notes on 1st commit
+EOF
+ # Remove another conflict
+ rm .git/NOTES_MERGE_WORKTREE/$commit_sha4 &&
+ # Remove a D/F conflict
+ rm .git/NOTES_MERGE_WORKTREE/$commit_sha3 &&
+ # Add a new note
+ echo "new note on 5th commit" > .git/NOTES_MERGE_WORKTREE/$commit_sha5 &&
+ # Finalize merge
+ git notes merge --commit &&
+ # No .git/NOTES_MERGE_* files left
+ test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+ test_cmp /dev/null output &&
+ # Merge commit has pre-merge y and pre-merge z as parents
+ test "$(git rev-parse refs/notes/m^1)" = "$(cat pre_merge_y)" &&
+ test "$(git rev-parse refs/notes/m^2)" = "$(cat pre_merge_z)" &&
+ # Merge commit mentions the notes refs merged
+ git log -1 --format=%B refs/notes/m > merge_commit_msg &&
+ grep -q refs/notes/m merge_commit_msg &&
+ grep -q refs/notes/z merge_commit_msg &&
+ # Merge commit mentions conflicting notes
+ grep -q "Conflicts" merge_commit_msg &&
+ ( for sha1 in $(cat expect_conflicts); do
+ grep -q "$sha1" merge_commit_msg ||
+ exit 1
+ done ) &&
+ # Verify contents of merge result
+ verify_notes m &&
+ # Verify that other notes refs has not changed (w, x, y and z)
+ verify_notes w &&
+ verify_notes x &&
+ verify_notes y &&
+ verify_notes z
+'
+
+cp expect_notes_y expect_notes_m
+cp expect_log_y expect_log_m
+
+test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
+ git update-ref refs/notes/m refs/notes/y &&
+ test_must_fail git notes merge z >output &&
+ # Output should point to where to resolve conflicts
+ grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
+ # Inspect merge conflicts
+ ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+ test_cmp expect_conflicts output_conflicts &&
+ ( for f in $(cat expect_conflicts); do
+ test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+ exit 1
+ done ) &&
+ # Verify that current notes tree (pre-merge) has not changed (m == y)
+ verify_notes y &&
+ verify_notes m &&
+ test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
+'
+
+cp expect_notes_w expect_notes_m
+cp expect_log_w expect_log_m
+
+test_expect_success 'reset notes ref m to somewhere else (w)' '
+ git update-ref refs/notes/m refs/notes/w &&
+ verify_notes m &&
+ test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)"
+'
+
+test_expect_success 'fail to finalize conflicting merge if underlying ref has moved in the meantime (m != NOTES_MERGE_PARTIAL^1)' '
+ # Resolve conflicts
+ cat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&
+y and z notes on 1st commit
+EOF
+ cat >.git/NOTES_MERGE_WORKTREE/$commit_sha4 <<EOF &&
+y and z notes on 4th commit
+EOF
+ # Fail to finalize merge
+ test_must_fail git notes merge --commit >output 2>&1 &&
+ # .git/NOTES_MERGE_* must remain
+ test -f .git/NOTES_MERGE_PARTIAL &&
+ test -f .git/NOTES_MERGE_REF &&
+ test -f .git/NOTES_MERGE_WORKTREE/$commit_sha1 &&
+ test -f .git/NOTES_MERGE_WORKTREE/$commit_sha2 &&
+ test -f .git/NOTES_MERGE_WORKTREE/$commit_sha3 &&
+ test -f .git/NOTES_MERGE_WORKTREE/$commit_sha4 &&
+ # Refs are unchanged
+ test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)" &&
+ test "$(git rev-parse refs/notes/y)" = "$(git rev-parse NOTES_MERGE_PARTIAL^1)" &&
+ test "$(git rev-parse refs/notes/m)" != "$(git rev-parse NOTES_MERGE_PARTIAL^1)" &&
+ # Mention refs/notes/m, and its current and expected value in output
+ grep -q "refs/notes/m" output &&
+ grep -q "$(git rev-parse refs/notes/m)" output &&
+ grep -q "$(git rev-parse NOTES_MERGE_PARTIAL^1)" output &&
+ # Verify that other notes refs has not changed (w, x, y and z)
+ verify_notes w &&
+ verify_notes x &&
+ verify_notes y &&
+ verify_notes z
+'
+
+test_expect_success 'resolve situation by aborting the notes merge' '
+ git notes merge --abort &&
+ # No .git/NOTES_MERGE_* files left
+ test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+ test_cmp /dev/null output &&
+ # m has not moved (still == w)
+ test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)" &&
+ # Verify that other notes refs has not changed (w, x, y and z)
+ verify_notes w &&
+ verify_notes x &&
+ verify_notes y &&
+ verify_notes z
+'
+
+cat >expect_notes <<EOF
+foo
+bar
+EOF
+
+test_expect_success 'switch cwd before committing notes merge' '
+ git notes add -m foo HEAD &&
+ git notes --ref=other add -m bar HEAD &&
+ test_must_fail git notes merge refs/notes/other &&
+ (
+ cd .git/NOTES_MERGE_WORKTREE &&
+ echo "foo" > $(git rev-parse HEAD) &&
+ echo "bar" >> $(git rev-parse HEAD) &&
+ git notes merge --commit
+ ) &&
+ git notes show HEAD > actual_notes &&
+ test_cmp expect_notes actual_notes
+'
+
+test_done
diff --git a/t/t3311-notes-merge-fanout.sh b/t/t3311-notes-merge-fanout.sh
new file mode 100755
index 0000000..93516ef
--- /dev/null
+++ b/t/t3311-notes-merge-fanout.sh
@@ -0,0 +1,436 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Johan Herland
+#
+
+test_description='Test notes merging at various fanout levels'
+
+. ./test-lib.sh
+
+verify_notes () {
+ notes_ref="$1"
+ commit="$2"
+ if test -f "expect_notes_$notes_ref"
+ then
+ git -c core.notesRef="refs/notes/$notes_ref" notes |
+ sort >"output_notes_$notes_ref" &&
+ test_cmp "expect_notes_$notes_ref" "output_notes_$notes_ref" ||
+ return 1
+ fi &&
+ git -c core.notesRef="refs/notes/$notes_ref" log --format="%H %s%n%N" \
+ "$commit" >"output_log_$notes_ref" &&
+ test_cmp "expect_log_$notes_ref" "output_log_$notes_ref"
+}
+
+verify_fanout () {
+ notes_ref="$1"
+ # Expect entire notes tree to have a fanout == 1
+ git rev-parse --quiet --verify "refs/notes/$notes_ref" >/dev/null &&
+ git ls-tree -r --name-only "refs/notes/$notes_ref" |
+ while read path
+ do
+ case "$path" in
+ ??/??????????????????????????????????????)
+ : true
+ ;;
+ *)
+ echo "Invalid path \"$path\"" &&
+ return 1
+ ;;
+ esac
+ done
+}
+
+verify_no_fanout () {
+ notes_ref="$1"
+ # Expect entire notes tree to have a fanout == 0
+ git rev-parse --quiet --verify "refs/notes/$notes_ref" >/dev/null &&
+ git ls-tree -r --name-only "refs/notes/$notes_ref" |
+ while read path
+ do
+ case "$path" in
+ ????????????????????????????????????????)
+ : true
+ ;;
+ *)
+ echo "Invalid path \"$path\"" &&
+ return 1
+ ;;
+ esac
+ done
+}
+
+# Set up a notes merge scenario with different kinds of conflicts
+test_expect_success 'setup a few initial commits with notes (notes ref: x)' '
+ git config core.notesRef refs/notes/x &&
+ for i in 1 2 3 4 5
+ do
+ test_commit "commit$i" >/dev/null &&
+ git notes add -m "notes for commit$i" || return 1
+ done
+'
+
+commit_sha1=$(git rev-parse commit1^{commit})
+commit_sha2=$(git rev-parse commit2^{commit})
+commit_sha3=$(git rev-parse commit3^{commit})
+commit_sha4=$(git rev-parse commit4^{commit})
+commit_sha5=$(git rev-parse commit5^{commit})
+
+cat <<EOF | sort >expect_notes_x
+aed91155c7a72c2188e781fdf40e0f3761b299db $commit_sha5
+99fab268f9d7ee7b011e091a436c78def8eeee69 $commit_sha4
+953c20ae26c7aa0b428c20693fe38bc687f9d1a9 $commit_sha3
+6358796131b8916eaa2dde6902642942a1cb37e1 $commit_sha2
+b02d459c32f0e68f2fe0981033bb34f38776ba47 $commit_sha1
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 commit5
+notes for commit5
+
+$commit_sha4 commit4
+notes for commit4
+
+$commit_sha3 commit3
+notes for commit3
+
+$commit_sha2 commit2
+notes for commit2
+
+$commit_sha1 commit1
+notes for commit1
+
+EOF
+
+test_expect_success 'sanity check (x)' '
+ verify_notes x commit5 &&
+ verify_no_fanout x
+'
+
+num=300
+
+cp expect_log_x expect_log_y
+
+test_expect_success 'Add a few hundred commits w/notes to trigger fanout (x -> y)' '
+ git update-ref refs/notes/y refs/notes/x &&
+ git config core.notesRef refs/notes/y &&
+ i=5 &&
+ while test $i -lt $num
+ do
+ i=$(($i + 1)) &&
+ test_commit "commit$i" >/dev/null &&
+ git notes add -m "notes for commit$i" || return 1
+ done &&
+ test "$(git rev-parse refs/notes/y)" != "$(git rev-parse refs/notes/x)" &&
+ # Expected number of commits and notes
+ test $(git rev-list HEAD | wc -l) = $num &&
+ test $(git notes list | wc -l) = $num &&
+ # 5 first notes unchanged
+ verify_notes y commit5
+'
+
+test_expect_success 'notes tree has fanout (y)' 'verify_fanout y'
+
+test_expect_success 'No-op merge (already included) (x => y)' '
+ git update-ref refs/notes/m refs/notes/y &&
+ git config core.notesRef refs/notes/m &&
+ git notes merge x &&
+ test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/y)"
+'
+
+test_expect_success 'Fast-forward merge (y => x)' '
+ git update-ref refs/notes/m refs/notes/x &&
+ git notes merge y &&
+ test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/y)"
+'
+
+cat <<EOF | sort >expect_notes_z
+9f506ee70e20379d7f78204c77b334f43d77410d $commit_sha3
+23a47d6ea7d589895faf800752054818e1e7627b $commit_sha2
+b02d459c32f0e68f2fe0981033bb34f38776ba47 $commit_sha1
+EOF
+
+cat >expect_log_z <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+
+$commit_sha3 commit3
+notes for commit3
+
+appended notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+notes for commit1
+
+EOF
+
+test_expect_success 'change some of the initial 5 notes (x -> z)' '
+ git update-ref refs/notes/z refs/notes/x &&
+ git config core.notesRef refs/notes/z &&
+ git notes add -f -m "new notes for commit2" commit2 &&
+ git notes append -m "appended notes for commit3" commit3 &&
+ git notes remove commit4 &&
+ git notes remove commit5 &&
+ verify_notes z commit5
+'
+
+test_expect_success 'notes tree has no fanout (z)' 'verify_no_fanout z'
+
+cp expect_log_z expect_log_m
+
+test_expect_success 'successful merge without conflicts (y => z)' '
+ git update-ref refs/notes/m refs/notes/z &&
+ git config core.notesRef refs/notes/m &&
+ git notes merge y &&
+ verify_notes m commit5 &&
+ # x/y/z unchanged
+ verify_notes x commit5 &&
+ verify_notes y commit5 &&
+ verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+cat >expect_log_w <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+other notes for commit4
+
+$commit_sha3 commit3
+other notes for commit3
+
+$commit_sha2 commit2
+notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'introduce conflicting changes (y -> w)' '
+ git update-ref refs/notes/w refs/notes/y &&
+ git config core.notesRef refs/notes/w &&
+ git notes add -f -m "other notes for commit1" commit1 &&
+ git notes add -f -m "other notes for commit3" commit3 &&
+ git notes add -f -m "other notes for commit4" commit4 &&
+ git notes remove commit5 &&
+ verify_notes w commit5
+'
+
+cat >expect_log_m <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+other notes for commit4
+
+$commit_sha3 commit3
+other notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'successful merge using "ours" strategy (z => w)' '
+ git update-ref refs/notes/m refs/notes/w &&
+ git config core.notesRef refs/notes/m &&
+ git notes merge -s ours z &&
+ verify_notes m commit5 &&
+ # w/x/y/z unchanged
+ verify_notes w commit5 &&
+ verify_notes x commit5 &&
+ verify_notes y commit5 &&
+ verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+cat >expect_log_m <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+
+$commit_sha3 commit3
+notes for commit3
+
+appended notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'successful merge using "theirs" strategy (z => w)' '
+ git update-ref refs/notes/m refs/notes/w &&
+ git notes merge -s theirs z &&
+ verify_notes m commit5 &&
+ # w/x/y/z unchanged
+ verify_notes w commit5 &&
+ verify_notes x commit5 &&
+ verify_notes y commit5 &&
+ verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+cat >expect_log_m <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+other notes for commit4
+
+$commit_sha3 commit3
+other notes for commit3
+
+notes for commit3
+
+appended notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'successful merge using "union" strategy (z => w)' '
+ git update-ref refs/notes/m refs/notes/w &&
+ git notes merge -s union z &&
+ verify_notes m commit5 &&
+ # w/x/y/z unchanged
+ verify_notes w commit5 &&
+ verify_notes x commit5 &&
+ verify_notes y commit5 &&
+ verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+cat >expect_log_m <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+other notes for commit4
+
+$commit_sha3 commit3
+appended notes for commit3
+notes for commit3
+other notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'successful merge using "cat_sort_uniq" strategy (z => w)' '
+ git update-ref refs/notes/m refs/notes/w &&
+ git notes merge -s cat_sort_uniq z &&
+ verify_notes m commit5 &&
+ # w/x/y/z unchanged
+ verify_notes w commit5 &&
+ verify_notes x commit5 &&
+ verify_notes y commit5 &&
+ verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+# We're merging z into w. Here are the conflicts we expect:
+#
+# commit | x -> w | x -> z | conflict?
+# -------|-----------|-----------|----------
+# 1 | changed | unchanged | no, use w
+# 2 | unchanged | changed | no, use z
+# 3 | changed | changed | yes (w, then z in conflict markers)
+# 4 | changed | deleted | yes (w)
+# 5 | deleted | deleted | no, deleted
+
+test_expect_success 'fails to merge using "manual" strategy (z => w)' '
+ git update-ref refs/notes/m refs/notes/w &&
+ test_must_fail git notes merge z
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+cat <<EOF | sort >expect_conflicts
+$commit_sha3
+$commit_sha4
+EOF
+
+cat >expect_conflict_$commit_sha3 <<EOF
+<<<<<<< refs/notes/m
+other notes for commit3
+=======
+notes for commit3
+
+appended notes for commit3
+>>>>>>> refs/notes/z
+EOF
+
+cat >expect_conflict_$commit_sha4 <<EOF
+other notes for commit4
+EOF
+
+test_expect_success 'verify conflict entries (with no fanout)' '
+ ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+ test_cmp expect_conflicts output_conflicts &&
+ ( for f in $(cat expect_conflicts); do
+ test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+ exit 1
+ done ) &&
+ # Verify that current notes tree (pre-merge) has not changed (m == w)
+ test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)"
+'
+
+cat >expect_log_m <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+other notes for commit4
+
+$commit_sha3 commit3
+other notes for commit3
+
+appended notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'resolve and finalize merge (z => w)' '
+ cat >.git/NOTES_MERGE_WORKTREE/$commit_sha3 <<EOF &&
+other notes for commit3
+
+appended notes for commit3
+EOF
+ git notes merge --commit &&
+ verify_notes m commit5 &&
+ # w/x/y/z unchanged
+ verify_notes w commit5 &&
+ verify_notes x commit5 &&
+ verify_notes y commit5 &&
+ verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+test_done
diff --git a/t/t3320-notes-merge-worktrees.sh b/t/t3320-notes-merge-worktrees.sh
new file mode 100755
index 0000000..1f71d58
--- /dev/null
+++ b/t/t3320-notes-merge-worktrees.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+#
+# Copyright (c) 2015 Twitter, Inc
+#
+
+test_description='Test merging of notes trees in multiple worktrees'
+
+. ./test-lib.sh
+
+test_expect_success 'setup commit' '
+ test_commit tantrum
+'
+
+commit_tantrum=$(git rev-parse tantrum^{commit})
+
+test_expect_success 'setup notes ref (x)' '
+ git config core.notesRef refs/notes/x &&
+ git notes add -m "x notes on tantrum" tantrum
+'
+
+test_expect_success 'setup local branch (y)' '
+ git update-ref refs/notes/y refs/notes/x &&
+ git config core.notesRef refs/notes/y &&
+ git notes remove tantrum
+'
+
+test_expect_success 'setup remote branch (z)' '
+ git update-ref refs/notes/z refs/notes/x &&
+ git config core.notesRef refs/notes/z &&
+ git notes add -f -m "conflicting notes on tantrum" tantrum
+'
+
+test_expect_success 'modify notes ref ourselves (x)' '
+ git config core.notesRef refs/notes/x &&
+ git notes add -f -m "more conflicting notes on tantrum" tantrum
+'
+
+test_expect_success 'create some new worktrees' '
+ git worktree add -b newbranch worktree master &&
+ git worktree add -b newbranch2 worktree2 master
+'
+
+test_expect_success 'merge z into y fails and sets NOTES_MERGE_REF' '
+ git config core.notesRef refs/notes/y &&
+ test_must_fail git notes merge z &&
+ echo "ref: refs/notes/y" >expect &&
+ test_cmp .git/NOTES_MERGE_REF expect
+'
+
+test_expect_success 'merge z into y while mid-merge in another workdir fails' '
+ (
+ cd worktree &&
+ git config core.notesRef refs/notes/y &&
+ test_must_fail git notes merge z 2>err &&
+ grep "A notes merge into refs/notes/y is already in-progress at" err
+ ) &&
+ test_path_is_missing .git/worktrees/worktree/NOTES_MERGE_REF
+'
+
+test_expect_success 'merge z into x while mid-merge on y succeeds' '
+ (
+ cd worktree2 &&
+ git config core.notesRef refs/notes/x &&
+ test_must_fail git notes merge z 2>&1 >out &&
+ grep "Automatic notes merge failed" out &&
+ grep -v "A notes merge into refs/notes/x is already in-progress in" out
+ ) &&
+ echo "ref: refs/notes/x" >expect &&
+ test_cmp .git/worktrees/worktree2/NOTES_MERGE_REF expect
+'
+
+test_done
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index d98c7b5..47b5682 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -14,140 +14,188 @@ 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 '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 force-3way &&
+ echo Dummy >Y &&
+ git update-index --add Y &&
+ git commit -m "Add Y." &&
+ git checkout -b filemove &&
+ git reset --soft master &&
+ mkdir D &&
+ git mv A D/A &&
+ git commit -m "Move A." &&
+ git checkout -b my-topic-branch master &&
+ 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 -f my-topic-branch &&
+ git tag topic
'
test_expect_success 'rebase on dirty worktree' '
- echo dirty >> A &&
- test_must_fail git rebase master'
+ 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'
+ git add A &&
+ test_must_fail git rebase master
+'
test_expect_success 'rebase against master' '
- git reset --hard HEAD &&
- git rebase 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, with <onto> and <upstream> specified as :/quuxery' '
+ test_when_finished "git branch -D torebase" &&
+ git checkout -b torebase my-topic-branch^ &&
+ upstream=$(git rev-parse ":/Add B") &&
+ onto=$(git rev-parse ":/Add A") &&
+ git rebase --onto $onto $upstream &&
+ git reset --hard my-topic-branch^ &&
+ git rebase --onto ":/Add A" ":/Add B" &&
+ git checkout my-topic-branch
'
-test_expect_success '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 'the rebase operation should not have destroyed author information' '
+ ! (git log | grep "Author:" | grep "<>")
'
-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 '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 fast-forward to master' '
- git checkout my-topic-branch^ &&
- git rebase my-topic-branch >out &&
- grep "Fast-forwarded HEAD to my-topic-branch" out
+test_expect_success 'rebase from ambiguous branch name' '
+ git checkout -b topic side &&
+ git rebase master
'
-test_expect_success \
- 'the rebase operation should not have destroyed author information' \
- '! (git log | grep "Author:" | grep "<>")'
+test_expect_success 'rebase off of the previous branch using "-"' '
+ git checkout master &&
+ git checkout HEAD^ &&
+ git rebase @{-1} >expect.messages &&
+ git merge-base master HEAD >expect.forkpoint &&
-test_expect_success \
- 'the rebase operation should not have destroyed author information (2)' \
- "git log -1 | grep 'Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>'"
+ git checkout master &&
+ git checkout HEAD^ &&
+ git rebase - >actual.messages &&
+ git merge-base master HEAD >actual.forkpoint &&
-test_expect_success 'HEAD was detached during rebase' '
- test $(git rev-parse HEAD@{1}) != $(git rev-parse my-topic-branch@{1})
+ test_cmp expect.forkpoint actual.forkpoint &&
+ # the next one is dubious---we may want to say "-",
+ # instead of @{-1}, in the message
+ test_i18ncmp expect.messages actual.messages
'
-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 a single mode change' '
+ git checkout master &&
+ git branch -D topic &&
+ 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 \
- '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 is not broken by diff.renames' '
+ test_config diff.renames copies &&
+ git checkout filemove &&
+ GIT_TRACE=1 git rebase force-3way
'
-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 'setup: recover' '
+ test_might_fail git rebase --abort &&
+ git reset --hard &&
+ git checkout modechange
'
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
+ >B &&
+ test_must_fail git rebase topic 2>output.err >output.out &&
+ grep "The following untracked working tree files would be overwritten by checkout:" output.err &&
+ grep B 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 'fail when upstream arg is missing and not on branch' '
+ git checkout topic &&
+ test_must_fail git rebase
+'
+
+test_expect_success 'fail when upstream arg is missing and not configured' '
+ git checkout -b no-config topic &&
+ test_must_fail git rebase
+'
+
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
+ git checkout -b default-base master &&
+ git checkout -b default topic &&
+ git config branch.default.remote . &&
+ git config branch.default.merge refs/heads/default-base &&
+ git rebase &&
+ git rev-parse --verify default-base >expect &&
+ git rev-parse default~1 >actual &&
+ test_cmp expect actual &&
+ git checkout default-base &&
+ git reset --hard HEAD^ &&
+ git checkout default &&
+ git rebase &&
+ git rev-parse --verify default-base >expect &&
+ git rev-parse default~1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-picked commits and fork-point work together' '
+ git checkout default-base &&
+ echo Amended >A &&
+ git commit -a --no-edit --amend &&
+ test_commit B B &&
+ test_commit new_B B "New B" &&
+ test_commit C C &&
+ git checkout default &&
+ git reset --hard default-base@{4} &&
+ test_commit D D &&
+ git cherry-pick -2 default-base^ &&
+ test_commit final_B B "Final B" &&
+ git rebase &&
+ echo Amended >expect &&
+ test_cmp A expect &&
+ echo "Final B" >expect &&
+ test_cmp B expect &&
+ echo C >expect &&
+ test_cmp C expect &&
+ echo D >expect &&
+ test_cmp D expect
'
test_expect_success 'rebase -q is quiet' '
- git checkout -b quiet topic &&
- git rebase -q master > output.out 2>&1 &&
- test ! -s output.out
+ git checkout -b quiet topic &&
+ git rebase -q master >output.out 2>&1 &&
+ test_must_be_empty output.out
'
test_expect_success 'Rebase a commit that sprinkles CRs in' '
@@ -184,4 +232,27 @@ test_expect_success 'rebase -m can copy notes' '
test "a note" = "$(git notes show HEAD)"
'
+test_expect_success 'rebase commit with an ancient timestamp' '
+ git reset --hard &&
+
+ >old.one && git add old.one && test_tick &&
+ git commit --date="@12345 +0400" -m "Old one" &&
+ >old.two && git add old.two && test_tick &&
+ git commit --date="@23456 +0500" -m "Old two" &&
+ >old.three && git add old.three && test_tick &&
+ git commit --date="@34567 +0600" -m "Old three" &&
+
+ git cat-file commit HEAD^^ >actual &&
+ grep "author .* 12345 +0400$" actual &&
+ git cat-file commit HEAD^ >actual &&
+ grep "author .* 23456 +0500$" actual &&
+ git cat-file commit HEAD >actual &&
+ grep "author .* 34567 +0600$" actual &&
+
+ git rebase --onto HEAD^^ HEAD^ &&
+
+ git cat-file commit HEAD >actual &&
+ grep "author .* 34567 +0600$" actual
+'
+
test_done
diff --git a/t/t3401-rebase-partial.sh b/t/t3401-rebase-partial.sh
deleted file mode 100755
index aea6685..0000000
--- a/t/t3401-rebase-partial.sh
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2006 Yann Dirson, based on t3400 by Amos Waterland
-#
-
-test_description='git rebase should detect patches integrated upstream
-
-This test cherry-picks one local change of two into master branch, and
-checks that git rebase succeeds with only the second patch in the
-local branch.
-'
-. ./test-lib.sh
-
-test_expect_success \
- 'prepare repository with topic branch' \
- '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
index 7b7d072..488945e 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -33,6 +33,7 @@ test_expect_success setup '
tr "[a-z]" "[A-Z]" <original >newfile &&
git add newfile &&
git commit -a -m"side edits further." &&
+ git branch second-side &&
tr "[a-m]" "[A-M]" <original >newfile &&
rm -f original &&
@@ -41,11 +42,12 @@ test_expect_success setup '
git branch test-rebase side &&
git branch test-rebase-pick side &&
git branch test-reference-pick side &&
+ git branch test-conflicts side &&
git checkout -b test-merge side
'
test_expect_success 'reference merge' '
- git merge -s recursive "reference merge" HEAD master
+ git merge -s recursive -m "reference merge" master
'
PRE_REBASE=$(git rev-parse test-rebase)
@@ -74,6 +76,24 @@ test_expect_success 'rebase the other way' '
git rebase --merge side
'
+test_expect_success 'rebase -Xtheirs' '
+ git checkout -b conflicting master~2 &&
+ echo "AB $T" >> original &&
+ git commit -mconflicting original &&
+ git rebase -Xtheirs master &&
+ grep AB original &&
+ ! grep 11 original
+'
+
+test_expect_success 'rebase -Xtheirs from orphan' '
+ git checkout --orphan orphan-conflicting master~2 &&
+ echo "AB $T" >> original &&
+ git commit -morphan-conflicting original &&
+ git rebase -Xtheirs master &&
+ grep AB original &&
+ ! grep 11 original
+'
+
test_expect_success 'merge and rebase should match' '
git diff-tree -r test-rebase test-merge >difference &&
if test -s difference
@@ -108,4 +128,38 @@ test_expect_success 'picking rebase' '
esac
'
+test_expect_success 'rebase -s funny -Xopt' '
+ test_when_finished "rm -fr test-bin funny.was.run" &&
+ mkdir test-bin &&
+ cat >test-bin/git-merge-funny <<-EOF &&
+ #!$SHELL_PATH
+ case "\$1" in --opt) ;; *) exit 2 ;; esac
+ shift &&
+ >funny.was.run &&
+ exec git merge-recursive "\$@"
+ EOF
+ chmod +x test-bin/git-merge-funny &&
+ git reset --hard &&
+ git checkout -b test-funny master^ &&
+ test_commit funny &&
+ (
+ PATH=./test-bin:$PATH
+ git rebase -s funny -Xopt master
+ ) &&
+ test -f funny.was.run
+'
+
+test_expect_success 'rebase --skip works with two conflicts in a row' '
+ git checkout second-side &&
+ tr "[A-Z]" "[a-z]" <newfile >tmp &&
+ mv tmp newfile &&
+ git commit -a -m"edit conflicting with side" &&
+ tr "[d-f]" "[D-F]" <newfile >tmp &&
+ mv tmp newfile &&
+ git commit -a -m"another edit conflicting with side" &&
+ test_must_fail git rebase --merge test-conflicts &&
+ test_must_fail git rebase --skip &&
+ git rebase --skip
+'
+
test_done
diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh
index 64446e3..1f5122b 100755
--- a/t/t3403-rebase-skip.sh
+++ b/t/t3403-rebase-skip.sh
@@ -35,6 +35,11 @@ test_expect_success 'rebase with git am -3 (default)' '
test_must_fail git rebase master
'
+test_expect_success 'rebase --skip can not be used with other options' '
+ test_must_fail git rebase -v --skip &&
+ test_must_fail git rebase --skip -v
+'
+
test_expect_success 'rebase --skip with am -3' '
git rebase --skip
'
@@ -59,10 +64,11 @@ test_expect_success 'rebase with --merge' '
test_expect_success 'rebase --skip with --merge' '
git rebase --skip
- '
+'
-test_expect_success 'merge and reference trees equal' \
- 'test -z "`git diff-tree skip-merge skip-reference`"'
+test_expect_success 'merge and reference trees equal' '
+ test -z "$(git diff-tree skip-merge skip-reference)"
+'
test_expect_success 'moved back to branch correctly' '
test refs/heads/skip-merge = $(git symbolic-ref HEAD)
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 84c3a62..c7ea8ba 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -7,34 +7,31 @@ test_description='git rebase interactive
This test runs git rebase "interactively", by faking an edit, and verifies
that the result still makes sense.
+
+Initial setup:
+
+ 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".
'
. ./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 &&
@@ -46,87 +43,92 @@ test_expect_success 'setup' '
test_commit G file1 &&
test_commit H file5 &&
git checkout -b branch2 F &&
- test_commit I file6
+ test_commit I file6 &&
git checkout -b conflict-branch A &&
- for n in one two three four
- do
- test_commit $n conflict
- done &&
+ test_commit one conflict &&
+ test_commit two conflict &&
+ test_commit three conflict &&
+ test_commit four conflict &&
git checkout -b no-conflict-branch A &&
- for n in J K L M
- do
- test_commit $n file$n
- done &&
+ test_commit J fileJ &&
+ test_commit K fileK &&
+ test_commit L fileL &&
+ test_commit M fileM &&
git checkout -b no-ff-branch A &&
- for n in N O P
- do
- test_commit $n file$n
- done
+ test_commit N fileN &&
+ test_commit O fileO &&
+ test_commit P fileP
'
# "exec" commands are run with the user shell by default, but this may
# be non-POSIX. For example, if SHELL=zsh then ">file" doesn't work
-# to create a file. Unseting SHELL avoids such non-portable behavior
-# in tests.
+# to create a file. Unsetting SHELL avoids such non-portable behavior
+# in tests. It must be exported for it to take effect where needed.
SHELL=
+export SHELL
+
+test_expect_success 'rebase --keep-empty' '
+ git checkout -b emptybranch master &&
+ git commit --allow-empty -m "empty" &&
+ git rebase --keep-empty -i HEAD~2 &&
+ git log --oneline >actual &&
+ test_line_count = 6 actual
+'
test_expect_success 'rebase -i with the exec command' '
git checkout master &&
(
+ set_fake_editor &&
FAKE_LINES="1 exec_>touch-one
2 exec_>touch-two exec_false exec_>touch-three
3 4 exec_>\"touch-file__name_with_spaces\";_>touch-after-semicolon 5" &&
export FAKE_LINES &&
test_must_fail git rebase -i A
) &&
- test -f touch-one &&
- test -f touch-two &&
- ! test -f touch-three &&
- test $(git rev-parse C) = $(git rev-parse HEAD) || {
- echo "Stopped at wrong revision:"
- echo "($(git describe --tags HEAD) instead of C)"
- false
- } &&
+ test_path_is_file touch-one &&
+ test_path_is_file touch-two &&
+ test_path_is_missing touch-three " (should have stopped before)" &&
+ test_cmp_rev C HEAD &&
git rebase --continue &&
- test -f touch-three &&
- test -f "touch-file name with spaces" &&
- test -f touch-after-semicolon &&
- test $(git rev-parse master) = $(git rev-parse HEAD) || {
- echo "Stopped at wrong revision:"
- echo "($(git describe --tags HEAD) instead of master)"
- false
- } &&
+ test_path_is_file touch-three &&
+ test_path_is_file "touch-file name with spaces" &&
+ test_path_is_file touch-after-semicolon &&
+ test_cmp_rev master HEAD &&
rm -f touch-*
'
test_expect_success 'rebase -i with the exec command runs from tree root' '
git checkout master &&
- mkdir subdir && cd subdir &&
+ mkdir subdir && (cd subdir &&
+ set_fake_editor &&
FAKE_LINES="1 exec_>touch-subdir" \
- git rebase -i HEAD^ &&
- cd .. &&
- test -f touch-subdir &&
+ git rebase -i HEAD^
+ ) &&
+ test_path_is_file touch-subdir &&
rm -fr subdir
'
test_expect_success 'rebase -i with the exec command checks tree cleanness' '
git checkout master &&
- (
- FAKE_LINES="exec_echo_foo_>file1 1" &&
- export FAKE_LINES &&
- test_must_fail git rebase -i HEAD^
- ) &&
- test $(git rev-parse master^) = $(git rev-parse HEAD) || {
- echo "Stopped at wrong revision:"
- echo "($(git describe --tags HEAD) instead of master^)"
- false
- } &&
+ set_fake_editor &&
+ test_must_fail env FAKE_LINES="exec_echo_foo_>file1 1" git rebase -i HEAD^ &&
+ test_cmp_rev master^ HEAD &&
git reset --hard &&
git rebase --continue
'
+test_expect_success 'rebase -i with exec of inexistent command' '
+ git checkout master &&
+ test_when_finished "git rebase --abort" &&
+ set_fake_editor &&
+ test_must_fail env FAKE_LINES="exec_this-command-does-not-exist 1" \
+ git rebase -i HEAD^ >actual 2>&1 &&
+ ! grep "Maybe git-rebase is broken" actual
+'
+
test_expect_success 'no changes are a nop' '
git checkout branch2 &&
+ set_fake_editor &&
git rebase -i F &&
test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
test $(git rev-parse I) = $(git rev-parse HEAD)
@@ -136,6 +138,7 @@ test_expect_success 'test the [branch] option' '
git checkout -b dead-end &&
git rm file6 &&
git commit -m "stop here" &&
+ set_fake_editor &&
git rebase -i F branch2 &&
test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
test $(git rev-parse I) = $(git rev-parse branch2) &&
@@ -144,6 +147,7 @@ test_expect_success 'test the [branch] option' '
test_expect_success 'test --onto <branch>' '
git checkout -b test-onto branch2 &&
+ set_fake_editor &&
git rebase -i --onto branch1 F &&
test "$(git symbolic-ref -q HEAD)" = "refs/heads/test-onto" &&
test $(git rev-parse HEAD^) = $(git rev-parse branch1) &&
@@ -153,6 +157,7 @@ test_expect_success 'test --onto <branch>' '
test_expect_success 'rebase on top of a non-conflicting commit' '
git checkout branch1 &&
git tag original-branch1 &&
+ set_fake_editor &&
git rebase -i branch2 &&
test file6 = $(git diff --name-only original-branch1) &&
test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch1" &&
@@ -165,6 +170,7 @@ test_expect_success 'reflog for the branch shows state before rebase' '
'
test_expect_success 'exchange two commits' '
+ set_fake_editor &&
FAKE_LINES="2 1" git rebase -i HEAD~2 &&
test H = $(git cat-file commit HEAD^ | sed -ne \$p) &&
test G = $(git cat-file commit HEAD | sed -ne \$p)
@@ -190,6 +196,7 @@ EOF
test_expect_success 'stop on conflicting pick' '
git tag new-branch1 &&
+ set_fake_editor &&
test_must_fail git rebase -i master &&
test "$(git rev-parse HEAD~3)" = "$(git rev-parse master)" &&
test_cmp expect .git/rebase-merge/patch &&
@@ -204,16 +211,18 @@ 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_path_is_missing .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" &&
+ set_fake_editor &&
test_must_fail git rebase -i master > output 2>&1 &&
- grep "Untracked working tree file .file1. would be overwritten" \
+ grep "The following untracked working tree files would be overwritten by checkout:" \
output &&
- ! test -d .git/rebase-merge &&
+ grep "file1" output &&
+ test_path_is_missing .git/rebase-merge &&
git reset --hard HEAD^
'
@@ -223,6 +232,7 @@ test_expect_success 'retain authorship' '
test_tick &&
GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" &&
git tag twerp &&
+ set_fake_editor &&
git rebase -i --onto master HEAD^ &&
git show HEAD | grep "^Author: Twerp Snog"
'
@@ -233,6 +243,7 @@ test_expect_success 'squash' '
test_tick &&
GIT_AUTHOR_NAME="Nitfol" git commit -m "nitfol" file7 &&
echo "******************************" &&
+ set_fake_editor &&
FAKE_LINES="1 squash 2" EXPECT_HEADER_COUNT=2 \
git rebase -i --onto master HEAD~2 &&
test B = $(cat file7) &&
@@ -245,6 +256,7 @@ test_expect_success 'retain authorship when squashing' '
test_expect_success '-p handles "no changes" gracefully' '
HEAD=$(git rev-parse HEAD) &&
+ set_fake_editor &&
git rebase -i -p HEAD^ &&
git update-index --refresh &&
git diff-files --quiet &&
@@ -253,6 +265,8 @@ test_expect_success '-p handles "no changes" gracefully' '
'
test_expect_failure 'exchange two commits with -p' '
+ git checkout H &&
+ set_fake_editor &&
FAKE_LINES="2 1" git rebase -i -p HEAD~2 &&
test H = $(git cat-file commit HEAD^ | sed -ne \$p) &&
test G = $(git cat-file commit HEAD | sed -ne \$p)
@@ -287,6 +301,7 @@ test_expect_success 'preserve merges with -p' '
git commit -m M file1 &&
git checkout -b to-be-rebased &&
test_tick &&
+ set_fake_editor &&
git rebase -i -p --onto branch1 master &&
git update-index --refresh &&
git diff-files --quiet &&
@@ -301,7 +316,8 @@ test_expect_success 'preserve merges with -p' '
'
test_expect_success 'edit ancestor with -p' '
- FAKE_LINES="1 edit 2 3 4" git rebase -i -p HEAD~3 &&
+ set_fake_editor &&
+ FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3 &&
echo 2 > unrelated-file &&
test_tick &&
git commit -m L2-modified --amend unrelated-file &&
@@ -314,6 +330,7 @@ test_expect_success 'edit ancestor with -p' '
test_expect_success '--continue tries to commit' '
test_tick &&
+ set_fake_editor &&
test_must_fail git rebase -i --onto new-branch1 HEAD^ &&
echo resolved > file1 &&
git add file1 &&
@@ -323,17 +340,19 @@ test_expect_success '--continue tries to commit' '
'
test_expect_success 'verbose flag is heeded, even after --continue' '
- git reset --hard HEAD@{1} &&
+ git reset --hard master@{1} &&
test_tick &&
+ set_fake_editor &&
test_must_fail git rebase -v -i --onto new-branch1 HEAD^ &&
echo resolved > file1 &&
git add file1 &&
git rebase --continue > output &&
- grep "^ file1 | 2 +-$" output
+ grep "^ file1 | 2 +-$" output
'
test_expect_success 'multi-squash only fires up editor once' '
base=$(git rev-parse HEAD~4) &&
+ set_fake_editor &&
FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 squash 2 squash 3 squash 4" \
EXPECT_HEADER_COUNT=4 \
git rebase -i $base &&
@@ -344,6 +363,7 @@ test_expect_success 'multi-squash only fires up editor once' '
test_expect_success 'multi-fixup does not fire up editor' '
git checkout -b multi-fixup E &&
base=$(git rev-parse HEAD~4) &&
+ set_fake_editor &&
FAKE_COMMIT_AMEND="NEVER" FAKE_LINES="1 fixup 2 fixup 3 fixup 4" \
git rebase -i $base &&
test $base = $(git rev-parse HEAD^) &&
@@ -355,11 +375,8 @@ test_expect_success 'multi-fixup does not fire up editor' '
test_expect_success 'commit message used after conflict' '
git checkout -b conflict-fixup conflict-branch &&
base=$(git rev-parse HEAD~4) &&
- (
- FAKE_LINES="1 fixup 3 fixup 4" &&
- export FAKE_LINES &&
- test_must_fail git rebase -i $base
- ) &&
+ set_fake_editor &&
+ test_must_fail env FAKE_LINES="1 fixup 3 fixup 4" git rebase -i $base &&
echo three > conflict &&
git add conflict &&
FAKE_COMMIT_AMEND="ONCE" EXPECT_HEADER_COUNT=2 \
@@ -373,11 +390,8 @@ test_expect_success 'commit message used after conflict' '
test_expect_success 'commit message retained after conflict' '
git checkout -b conflict-squash conflict-branch &&
base=$(git rev-parse HEAD~4) &&
- (
- FAKE_LINES="1 fixup 3 squash 4" &&
- export FAKE_LINES &&
- test_must_fail git rebase -i $base
- ) &&
+ set_fake_editor &&
+ test_must_fail env FAKE_LINES="1 fixup 3 squash 4" git rebase -i $base &&
echo three > conflict &&
git add conflict &&
FAKE_COMMIT_AMEND="TWICE" EXPECT_HEADER_COUNT=2 \
@@ -399,6 +413,7 @@ EOF
test_expect_success 'squash and fixup generate correct log messages' '
git checkout -b squash-fixup E &&
base=$(git rev-parse HEAD~4) &&
+ set_fake_editor &&
FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 fixup 2 squash 3 fixup 4" \
EXPECT_HEADER_COUNT=4 \
git rebase -i $base &&
@@ -411,6 +426,7 @@ test_expect_success 'squash and fixup generate correct log messages' '
test_expect_success 'squash ignores comments' '
git checkout -b skip-comments E &&
base=$(git rev-parse HEAD~4) &&
+ set_fake_editor &&
FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="# 1 # squash 2 # squash 3 # squash 4 #" \
EXPECT_HEADER_COUNT=4 \
git rebase -i $base &&
@@ -423,6 +439,7 @@ test_expect_success 'squash ignores comments' '
test_expect_success 'squash ignores blank lines' '
git checkout -b skip-blank-lines E &&
base=$(git rev-parse HEAD~4) &&
+ set_fake_editor &&
FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="> 1 > squash 2 > squash 3 > squash 4 >" \
EXPECT_HEADER_COUNT=4 \
git rebase -i $base &&
@@ -435,6 +452,7 @@ test_expect_success 'squash ignores blank lines' '
test_expect_success 'squash works as expected' '
git checkout -b squash-works no-conflict-branch &&
one=$(git rev-parse HEAD~3) &&
+ set_fake_editor &&
FAKE_LINES="1 squash 3 2" EXPECT_HEADER_COUNT=2 \
git rebase -i HEAD~3 &&
test $one = $(git rev-parse HEAD~2)
@@ -443,11 +461,8 @@ test_expect_success 'squash works as expected' '
test_expect_success 'interrupted squash works as expected' '
git checkout -b interrupted-squash conflict-branch &&
one=$(git rev-parse HEAD~3) &&
- (
- FAKE_LINES="1 squash 3 2" &&
- export FAKE_LINES &&
- test_must_fail git rebase -i HEAD~3
- ) &&
+ set_fake_editor &&
+ test_must_fail env FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
(echo one; echo two; echo four) > conflict &&
git add conflict &&
test_must_fail git rebase --continue &&
@@ -460,11 +475,8 @@ test_expect_success 'interrupted squash works as expected' '
test_expect_success 'interrupted squash works as expected (case 2)' '
git checkout -b interrupted-squash2 conflict-branch &&
one=$(git rev-parse HEAD~3) &&
- (
- FAKE_LINES="3 squash 1 2" &&
- export FAKE_LINES &&
- test_must_fail git rebase -i HEAD~3
- ) &&
+ set_fake_editor &&
+ test_must_fail env FAKE_LINES="3 squash 1 2" git rebase -i HEAD~3 &&
(echo one; echo four) > conflict &&
git add conflict &&
test_must_fail git rebase --continue &&
@@ -477,21 +489,14 @@ test_expect_success 'interrupted squash works as expected (case 2)' '
test $one = $(git rev-parse HEAD~2)
'
-test_expect_success 'ignore patch if in upstream' '
- HEAD=$(git rev-parse HEAD) &&
- git checkout -b has-cherry-picked HEAD^ &&
+test_expect_success '--continue tries to commit, even for "edit"' '
echo unrelated > file7 &&
git add file7 &&
test_tick &&
git commit -m "unrelated change" &&
- git cherry-pick $HEAD &&
- EXPECT_COUNT=1 git rebase -i $HEAD &&
- test $HEAD = $(git rev-parse HEAD^)
-'
-
-test_expect_success '--continue tries to commit, even for "edit"' '
parent=$(git rev-parse HEAD^) &&
test_tick &&
+ set_fake_editor &&
FAKE_LINES="edit 1" git rebase -i HEAD^ &&
echo edited > file7 &&
git add file7 &&
@@ -504,20 +509,18 @@ test_expect_success '--continue tries to commit, even for "edit"' '
test_expect_success 'aborted --continue does not squash commits after "edit"' '
old=$(git rev-parse HEAD) &&
test_tick &&
+ set_fake_editor &&
FAKE_LINES="edit 1" git rebase -i HEAD^ &&
echo "edited again" > file7 &&
git add file7 &&
- (
- FAKE_COMMIT_MESSAGE=" " &&
- export FAKE_COMMIT_MESSAGE &&
- test_must_fail git rebase --continue
- ) &&
+ test_must_fail env FAKE_COMMIT_MESSAGE=" " git rebase --continue &&
test $old = $(git rev-parse HEAD) &&
git rebase --abort
'
test_expect_success 'auto-amend only edited commits after "edit"' '
test_tick &&
+ set_fake_editor &&
FAKE_LINES="edit 1" git rebase -i HEAD^ &&
echo "edited again" > file7 &&
git add file7 &&
@@ -525,18 +528,26 @@ test_expect_success 'auto-amend only edited commits after "edit"' '
echo "and again" > file7 &&
git add file7 &&
test_tick &&
- (
- FAKE_COMMIT_MESSAGE="and again" &&
- export FAKE_COMMIT_MESSAGE &&
- test_must_fail git rebase --continue
- ) &&
+ test_must_fail env FAKE_COMMIT_MESSAGE="and again" git rebase --continue &&
git rebase --abort
'
+test_expect_success 'clean error after failed "exec"' '
+ test_tick &&
+ test_when_finished "git rebase --abort || :" &&
+ set_fake_editor &&
+ test_must_fail env FAKE_LINES="1 exec_false" git rebase -i HEAD^ &&
+ echo "edited again" > file7 &&
+ git add file7 &&
+ test_must_fail git rebase --continue 2>error &&
+ grep "You have staged changes in your working tree." error
+'
+
test_expect_success 'rebase a detached HEAD' '
grandparent=$(git rev-parse HEAD~2) &&
git checkout $(git rev-parse HEAD) &&
test_tick &&
+ set_fake_editor &&
FAKE_LINES="2 1" git rebase -i HEAD~2 &&
test $grandparent = $(git rev-parse HEAD~2)
'
@@ -544,15 +555,15 @@ test_expect_success 'rebase a detached HEAD' '
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 &&
+ write_script .git/hooks/pre-commit <<-\EOF &&
+ test -z "$(git diff --cached --check)"
+ EOF
echo "monde! " >> file1 &&
test_tick &&
test_must_fail git commit -m doesnt-verify file1 &&
git commit -m doesnt-verify --no-verify file1 &&
test_tick &&
+ set_fake_editor &&
FAKE_LINES=2 git rebase -i HEAD~2
'
@@ -574,6 +585,7 @@ test_expect_success 'rebase with a file named HEAD in worktree' '
git commit -m "Add body"
) &&
+ set_fake_editor &&
FAKE_LINES="1 squash 2" git rebase -i to-be-rebased &&
test "$(git show -s --pretty=format:%an)" = "Squashed Away"
@@ -583,8 +595,9 @@ 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>"
+ --author="Somebody else <somebody@else.com>" &&
test $(git rev-parse branch3) != $(git rev-parse branch4) &&
+ set_fake_editor &&
git rebase -i branch3 &&
test $(git rev-parse branch3) = $(git rev-parse branch4)
@@ -598,7 +611,7 @@ test_expect_success 'submodule rebase setup' '
git add elif && git commit -m "submodule initial"
) &&
echo 1 >file1 &&
- git add file1 sub
+ git add file1 sub &&
test_tick &&
git commit -m "One" &&
echo 2 >file1 &&
@@ -609,18 +622,53 @@ test_expect_success 'submodule rebase setup' '
git commit -a -m "submodule second"
) &&
test_tick &&
+ set_fake_editor &&
git commit -a -m "Three changes submodule"
'
test_expect_success 'submodule rebase -i' '
+ set_fake_editor &&
FAKE_LINES="1 squash 2 3" git rebase -i A
'
+test_expect_success 'submodule conflict setup' '
+ git tag submodule-base &&
+ git checkout HEAD^ &&
+ (
+ cd sub && git checkout HEAD^ && echo 4 >elif &&
+ git add elif && git commit -m "submodule conflict"
+ ) &&
+ git add sub &&
+ test_tick &&
+ git commit -m "Conflict in submodule" &&
+ git tag submodule-topic
+'
+
+test_expect_success 'rebase -i continue with only submodule staged' '
+ set_fake_editor &&
+ test_must_fail git rebase -i submodule-base &&
+ git add sub &&
+ git rebase --continue &&
+ test $(git rev-parse submodule-base) != $(git rev-parse HEAD)
+'
+
+test_expect_success 'rebase -i continue with unstaged submodule' '
+ git checkout submodule-topic &&
+ git reset --hard &&
+ set_fake_editor &&
+ test_must_fail git rebase -i submodule-base &&
+ git reset &&
+ git rebase --continue &&
+ test $(git rev-parse submodule-base) = $(git rev-parse HEAD)
+'
+
test_expect_success 'avoid unnecessary reset' '
git checkout master &&
+ git reset --hard &&
test-chmtime =123456789 file3 &&
git update-index --refresh &&
HEAD=$(git rev-parse HEAD) &&
+ set_fake_editor &&
git rebase -i HEAD~4 &&
test $HEAD = $(git rev-parse HEAD) &&
MTIME=$(test-chmtime -v +0 file3 | sed 's/[^0-9].*$//') &&
@@ -629,6 +677,7 @@ test_expect_success 'avoid unnecessary reset' '
test_expect_success 'reword' '
git checkout -b reword-branch master &&
+ set_fake_editor &&
FAKE_LINES="1 2 3 reword 4" FAKE_COMMIT_MESSAGE="E changed" git rebase -i A &&
git show HEAD | grep "E changed" &&
test $(git rev-parse master) != $(git rev-parse HEAD) &&
@@ -648,18 +697,21 @@ test_expect_success 'rebase -i can copy notes' '
test_commit n2 &&
test_commit n3 &&
git notes add -m"a note" n3 &&
- git rebase --onto n1 n2 &&
+ set_fake_editor &&
+ git rebase -i --onto n1 n2 &&
test "a note" = "$(git notes show HEAD)"
'
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 &&
+ set_fake_editor &&
GIT_NOTES_REWRITE_MODE=concatenate FAKE_LINES="1 fixup 2" git rebase -i n1 &&
git notes show > output &&
test_cmp expect output
@@ -669,6 +721,7 @@ test_expect_success 'rebase while detaching HEAD' '
git symbolic-ref HEAD &&
grandparent=$(git rev-parse HEAD~2) &&
test_tick &&
+ set_fake_editor &&
FAKE_LINES="2 1" git rebase -i HEAD~2 HEAD^0 &&
test $grandparent = $(git rev-parse HEAD~2) &&
test_must_fail git symbolic-ref HEAD
@@ -678,6 +731,7 @@ test_tick # Ensure that the rebased commits get a different timestamp.
test_expect_success 'always cherry-pick with --no-ff' '
git checkout no-ff-branch &&
git tag original-no-ff-branch &&
+ set_fake_editor &&
git rebase -i --no-ff A &&
touch empty &&
for p in 0 1 2
@@ -698,15 +752,533 @@ test_expect_success 'set up commits with funny messages' '
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" git rebase -i A &&
+ set_fake_editor &&
+ FAKE_LINES="1 2 3 4" git rebase -i A &&
git rev-list A.. >actual &&
test_cmp expect actual
'
+test_expect_success 'prepare for rebase -i --exec' '
+ git checkout master &&
+ git checkout -b execute &&
+ test_commit one_exec main.txt one_exec &&
+ test_commit two_exec main.txt two_exec &&
+ test_commit three_exec main.txt three_exec
+'
+
+test_expect_success 'running "git rebase -i --exec git show HEAD"' '
+ set_fake_editor &&
+ git rebase -i --exec "git show HEAD" HEAD~2 >actual &&
+ (
+ FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
+ export FAKE_LINES &&
+ git rebase -i HEAD~2 >expect
+ ) &&
+ sed -e "1,9d" expect >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'running "git rebase --exec git show HEAD -i"' '
+ git reset --hard execute &&
+ set_fake_editor &&
+ git rebase --exec "git show HEAD" -i HEAD~2 >actual &&
+ (
+ FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
+ export FAKE_LINES &&
+ git rebase -i HEAD~2 >expect
+ ) &&
+ sed -e "1,9d" expect >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'running "git rebase -ix git show HEAD"' '
+ git reset --hard execute &&
+ set_fake_editor &&
+ git rebase -ix "git show HEAD" HEAD~2 >actual &&
+ (
+ FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
+ export FAKE_LINES &&
+ git rebase -i HEAD~2 >expect
+ ) &&
+ sed -e "1,9d" expect >expected &&
+ test_cmp expected actual
+'
+
+
+test_expect_success 'rebase -ix with several <CMD>' '
+ git reset --hard execute &&
+ set_fake_editor &&
+ git rebase -ix "git show HEAD; pwd" HEAD~2 >actual &&
+ (
+ FAKE_LINES="1 exec_git_show_HEAD;_pwd 2 exec_git_show_HEAD;_pwd" &&
+ export FAKE_LINES &&
+ git rebase -i HEAD~2 >expect
+ ) &&
+ sed -e "1,9d" expect >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'rebase -ix with several instances of --exec' '
+ git reset --hard execute &&
+ set_fake_editor &&
+ git rebase -i --exec "git show HEAD" --exec "pwd" HEAD~2 >actual &&
+ (
+ FAKE_LINES="1 exec_git_show_HEAD exec_pwd 2
+ exec_git_show_HEAD exec_pwd" &&
+ export FAKE_LINES &&
+ git rebase -i HEAD~2 >expect
+ ) &&
+ sed -e "1,11d" expect >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'rebase -ix with --autosquash' '
+ git reset --hard execute &&
+ git checkout -b autosquash &&
+ echo second >second.txt &&
+ git add second.txt &&
+ git commit -m "fixup! two_exec" &&
+ echo bis >bis.txt &&
+ git add bis.txt &&
+ git commit -m "fixup! two_exec" &&
+ set_fake_editor &&
+ (
+ git checkout -b autosquash_actual &&
+ git rebase -i --exec "git show HEAD" --autosquash HEAD~4 >actual
+ ) &&
+ git checkout autosquash &&
+ (
+ git checkout -b autosquash_expected &&
+ FAKE_LINES="1 fixup 3 fixup 4 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
+ export FAKE_LINES &&
+ git rebase -i HEAD~4 >expect
+ ) &&
+ sed -e "1,13d" expect >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'rebase --exec works without -i ' '
+ git reset --hard execute &&
+ rm -rf exec_output &&
+ EDITOR="echo >invoked_editor" git rebase --exec "echo a line >>exec_output" HEAD~2 2>actual &&
+ test_i18ngrep "Successfully rebased and updated" actual &&
+ test_line_count = 2 exec_output &&
+ test_path_is_missing invoked_editor
+'
+
+test_expect_success 'rebase -i --exec without <CMD>' '
+ git reset --hard execute &&
+ set_fake_editor &&
+ test_must_fail git rebase -i --exec 2>tmp &&
+ sed -e "1d" tmp >actual &&
+ test_must_fail git rebase -h >expected &&
+ test_cmp expected actual &&
+ git checkout master
+'
+
+test_expect_success 'rebase -i --root re-order and drop commits' '
+ git checkout E &&
+ set_fake_editor &&
+ FAKE_LINES="3 1 2 5" git rebase -i --root &&
+ test E = $(git cat-file commit HEAD | sed -ne \$p) &&
+ test B = $(git cat-file commit HEAD^ | sed -ne \$p) &&
+ test A = $(git cat-file commit HEAD^^ | sed -ne \$p) &&
+ test C = $(git cat-file commit HEAD^^^ | sed -ne \$p) &&
+ test 0 = $(git cat-file commit HEAD^^^ | grep -c ^parent\ )
+'
+
+test_expect_success 'rebase -i --root retain root commit author and message' '
+ git checkout A &&
+ echo B >file7 &&
+ git add file7 &&
+ GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" &&
+ set_fake_editor &&
+ FAKE_LINES="2" git rebase -i --root &&
+ git cat-file commit HEAD | grep -q "^author Twerp Snog" &&
+ git cat-file commit HEAD | grep -q "^different author$"
+'
+
+test_expect_success 'rebase -i --root temporary sentinel commit' '
+ git checkout B &&
+ set_fake_editor &&
+ test_must_fail env FAKE_LINES="2" git rebase -i --root &&
+ git cat-file commit HEAD | grep "^tree 4b825dc642cb" &&
+ git rebase --abort
+'
+
+test_expect_success 'rebase -i --root fixup root commit' '
+ git checkout B &&
+ set_fake_editor &&
+ FAKE_LINES="1 fixup 2" git rebase -i --root &&
+ test A = $(git cat-file commit HEAD | sed -ne \$p) &&
+ test B = $(git show HEAD:file1) &&
+ test 0 = $(git cat-file commit HEAD | grep -c ^parent\ )
+'
+
+test_expect_success 'rebase --edit-todo does not works on non-interactive rebase' '
+ git reset --hard &&
+ git checkout conflict-branch &&
+ set_fake_editor &&
+ test_must_fail git rebase --onto HEAD~2 HEAD~ &&
+ test_must_fail git rebase --edit-todo &&
+ git rebase --abort
+'
+
+test_expect_success 'rebase --edit-todo can be used to modify todo' '
+ git reset --hard &&
+ git checkout no-conflict-branch^0 &&
+ set_fake_editor &&
+ FAKE_LINES="edit 1 2 3" git rebase -i HEAD~3 &&
+ FAKE_LINES="2 1" git rebase --edit-todo &&
+ git rebase --continue &&
+ test M = $(git cat-file commit HEAD^ | sed -ne \$p) &&
+ test L = $(git cat-file commit HEAD | sed -ne \$p)
+'
+
+test_expect_success 'rebase -i produces readable reflog' '
+ git reset --hard &&
+ git branch -f branch-reflog-test H &&
+ set_fake_editor &&
+ git rebase -i --onto I F branch-reflog-test &&
+ cat >expect <<-\EOF &&
+ rebase -i (finish): returning to refs/heads/branch-reflog-test
+ rebase -i (pick): H
+ rebase -i (pick): G
+ rebase -i (start): checkout I
+ EOF
+ git reflog -n4 HEAD |
+ sed "s/[^:]*: //" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase -i respects core.commentchar' '
+ git reset --hard &&
+ git checkout E^0 &&
+ test_config core.commentchar "\\" &&
+ write_script remove-all-but-first.sh <<-\EOF &&
+ sed -e "2,\$s/^/\\\\/" "$1" >"$1.tmp" &&
+ mv "$1.tmp" "$1"
+ EOF
+ test_set_editor "$(pwd)/remove-all-but-first.sh" &&
+ git rebase -i B &&
+ test B = $(git cat-file commit HEAD^ | sed -ne \$p)
+'
+
+test_expect_success 'rebase -i, with <onto> and <upstream> specified as :/quuxery' '
+ test_when_finished "git branch -D torebase" &&
+ git checkout -b torebase branch1 &&
+ upstream=$(git rev-parse ":/J") &&
+ onto=$(git rev-parse ":/A") &&
+ git rebase --onto $onto $upstream &&
+ git reset --hard branch1 &&
+ git rebase --onto ":/A" ":/J" &&
+ git checkout branch1
+'
+
+test_expect_success 'rebase -i with --strategy and -X' '
+ git checkout -b conflict-merge-use-theirs conflict-branch &&
+ git reset --hard HEAD^ &&
+ echo five >conflict &&
+ echo Z >file1 &&
+ git commit -a -m "one file conflict" &&
+ EDITOR=true git rebase -i --strategy=recursive -Xours conflict-branch &&
+ test $(git show conflict-branch:conflict) = $(cat conflict) &&
+ test $(cat file1) = Z
+'
+
+test_expect_success 'interrupted rebase -i with --strategy and -X' '
+ git checkout -b conflict-merge-use-theirs-interrupted conflict-branch &&
+ git reset --hard HEAD^ &&
+ >breakpoint &&
+ git add breakpoint &&
+ git commit -m "breakpoint for interactive mode" &&
+ echo five >conflict &&
+ echo Z >file1 &&
+ git commit -a -m "one file conflict" &&
+ set_fake_editor &&
+ FAKE_LINES="edit 1 2" git rebase -i --strategy=recursive -Xours conflict-branch &&
+ git rebase --continue &&
+ test $(git show conflict-branch:conflict) = $(cat conflict) &&
+ test $(cat file1) = Z
+'
+
+test_expect_success 'rebase -i error on commits with \ in message' '
+ current_head=$(git rev-parse HEAD) &&
+ test_when_finished "git rebase --abort; git reset --hard $current_head; rm -f error" &&
+ test_commit TO-REMOVE will-conflict old-content &&
+ test_commit "\temp" will-conflict new-content dummy &&
+ test_must_fail env EDITOR=true git rebase -i HEAD^ --onto HEAD^^ 2>error &&
+ test_expect_code 1 grep " emp" error
+'
+
+test_expect_success 'short SHA-1 setup' '
+ test_when_finished "git checkout master" &&
+ git checkout --orphan collide &&
+ git rm -rf . &&
+ (
+ unset test_tick &&
+ test_commit collide1 collide &&
+ test_commit --notick collide2 collide &&
+ test_commit --notick collide3 collide
+ )
+'
+
+test_expect_success 'short SHA-1 collide' '
+ test_when_finished "reset_rebase && git checkout master" &&
+ git checkout collide &&
+ (
+ unset test_tick &&
+ test_tick &&
+ set_fake_editor &&
+ FAKE_COMMIT_MESSAGE="collide2 ac4f2ee" \
+ FAKE_LINES="reword 1 2" git rebase -i HEAD~2
+ )
+'
+
+test_expect_success 'respect core.abbrev' '
+ git config core.abbrev 12 &&
+ set_cat_todo_editor &&
+ test_must_fail git rebase -i HEAD~4 >todo-list &&
+ test 4 = $(grep -c "pick [0-9a-f]\{12,\}" todo-list)
+'
+
+test_expect_success 'todo count' '
+ write_script dump-raw.sh <<-\EOF &&
+ cat "$1"
+ EOF
+ test_set_editor "$(pwd)/dump-raw.sh" &&
+ git rebase -i HEAD~4 >actual &&
+ grep "^# Rebase ..* onto ..* ([0-9]" actual
+'
+
+test_expect_success 'rebase -i commits that overwrite untracked files (pick)' '
+ git checkout --force branch2 &&
+ git clean -f &&
+ set_fake_editor &&
+ FAKE_LINES="edit 1 2" git rebase -i A &&
+ test_cmp_rev HEAD F &&
+ test_path_is_missing file6 &&
+ >file6 &&
+ test_must_fail git rebase --continue &&
+ test_cmp_rev HEAD F &&
+ rm file6 &&
+ git rebase --continue &&
+ test_cmp_rev HEAD I
+'
+
+test_expect_success 'rebase -i commits that overwrite untracked files (squash)' '
+ git checkout --force branch2 &&
+ git clean -f &&
+ git tag original-branch2 &&
+ set_fake_editor &&
+ FAKE_LINES="edit 1 squash 2" git rebase -i A &&
+ test_cmp_rev HEAD F &&
+ test_path_is_missing file6 &&
+ >file6 &&
+ test_must_fail git rebase --continue &&
+ test_cmp_rev HEAD F &&
+ rm file6 &&
+ git rebase --continue &&
+ test $(git cat-file commit HEAD | sed -ne \$p) = I &&
+ git reset --hard original-branch2
+'
+
+test_expect_success 'rebase -i commits that overwrite untracked files (no ff)' '
+ git checkout --force branch2 &&
+ git clean -f &&
+ set_fake_editor &&
+ FAKE_LINES="edit 1 2" git rebase -i --no-ff A &&
+ test $(git cat-file commit HEAD | sed -ne \$p) = F &&
+ test_path_is_missing file6 &&
+ >file6 &&
+ test_must_fail git rebase --continue &&
+ test $(git cat-file commit HEAD | sed -ne \$p) = F &&
+ rm file6 &&
+ git rebase --continue &&
+ test $(git cat-file commit HEAD | sed -ne \$p) = I
+'
+
+test_expect_success 'rebase --continue removes CHERRY_PICK_HEAD' '
+ git checkout -b commit-to-skip &&
+ for double in X 3 1
+ do
+ test_seq 5 | sed "s/$double/&&/" >seq &&
+ git add seq &&
+ test_tick &&
+ git commit -m seq-$double
+ done &&
+ git tag seq-onto &&
+ git reset --hard HEAD~2 &&
+ git cherry-pick seq-onto &&
+ set_fake_editor &&
+ test_must_fail env FAKE_LINES= git rebase -i seq-onto &&
+ test -d .git/rebase-merge &&
+ git rebase --continue &&
+ git diff --exit-code seq-onto &&
+ test ! -d .git/rebase-merge &&
+ test ! -f .git/CHERRY_PICK_HEAD
+'
+
+rebase_setup_and_clean () {
+ test_when_finished "
+ git checkout master &&
+ test_might_fail git branch -D $1 &&
+ test_might_fail git rebase --abort
+ " &&
+ git checkout -b $1 master
+}
+
+test_expect_success 'drop' '
+ rebase_setup_and_clean drop-test &&
+ set_fake_editor &&
+ FAKE_LINES="1 drop 2 3 drop 4 5" git rebase -i --root &&
+ test E = $(git cat-file commit HEAD | sed -ne \$p) &&
+ test C = $(git cat-file commit HEAD^ | sed -ne \$p) &&
+ test A = $(git cat-file commit HEAD^^ | sed -ne \$p)
+'
+
+cat >expect <<EOF
+Successfully rebased and updated refs/heads/missing-commit.
+EOF
+
+test_expect_success 'rebase -i respects rebase.missingCommitsCheck = ignore' '
+ test_config rebase.missingCommitsCheck ignore &&
+ rebase_setup_and_clean missing-commit &&
+ set_fake_editor &&
+ FAKE_LINES="1 2 3 4" \
+ git rebase -i --root 2>actual &&
+ test D = $(git cat-file commit HEAD | sed -ne \$p) &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+Warning: some commits may have been dropped accidentally.
+Dropped commits (newer to older):
+ - $(git rev-list --pretty=oneline --abbrev-commit -1 master)
+To avoid this message, use "drop" to explicitly remove a commit.
+
+Use 'git config rebase.missingCommitsCheck' to change the level of warnings.
+The possible behaviours are: ignore, warn, error.
+
+Successfully rebased and updated refs/heads/missing-commit.
+EOF
+
+test_expect_success 'rebase -i respects rebase.missingCommitsCheck = warn' '
+ test_config rebase.missingCommitsCheck warn &&
+ rebase_setup_and_clean missing-commit &&
+ set_fake_editor &&
+ FAKE_LINES="1 2 3 4" \
+ git rebase -i --root 2>actual &&
+ test_cmp expect actual &&
+ test D = $(git cat-file commit HEAD | sed -ne \$p)
+'
+
+cat >expect <<EOF
+Warning: some commits may have been dropped accidentally.
+Dropped commits (newer to older):
+ - $(git rev-list --pretty=oneline --abbrev-commit -1 master)
+ - $(git rev-list --pretty=oneline --abbrev-commit -1 master~2)
+To avoid this message, use "drop" to explicitly remove a commit.
+
+Use 'git config rebase.missingCommitsCheck' to change the level of warnings.
+The possible behaviours are: ignore, warn, error.
+
+You can fix this with 'git rebase --edit-todo'.
+Or you can abort the rebase with 'git rebase --abort'.
+EOF
+
+test_expect_success 'rebase -i respects rebase.missingCommitsCheck = error' '
+ test_config rebase.missingCommitsCheck error &&
+ rebase_setup_and_clean missing-commit &&
+ set_fake_editor &&
+ test_must_fail env FAKE_LINES="1 2 4" \
+ git rebase -i --root 2>actual &&
+ test_cmp expect actual &&
+ cp .git/rebase-merge/git-rebase-todo.backup \
+ .git/rebase-merge/git-rebase-todo &&
+ FAKE_LINES="1 2 drop 3 4 drop 5" \
+ git rebase --edit-todo &&
+ git rebase --continue &&
+ test D = $(git cat-file commit HEAD | sed -ne \$p) &&
+ test B = $(git cat-file commit HEAD^ | sed -ne \$p)
+'
+
+cat >expect <<EOF
+Warning: the command isn't recognized in the following line:
+ - badcmd $(git rev-list --oneline -1 master~1)
+
+You can fix this with 'git rebase --edit-todo'.
+Or you can abort the rebase with 'git rebase --abort'.
+EOF
+
+test_expect_success 'static check of bad command' '
+ rebase_setup_and_clean bad-cmd &&
+ set_fake_editor &&
+ test_must_fail env FAKE_LINES="1 2 3 bad 4 5" \
+ git rebase -i --root 2>actual &&
+ test_cmp expect actual &&
+ FAKE_LINES="1 2 3 drop 4 5" git rebase --edit-todo &&
+ git rebase --continue &&
+ test E = $(git cat-file commit HEAD | sed -ne \$p) &&
+ test C = $(git cat-file commit HEAD^ | sed -ne \$p)
+'
+
+test_expect_success 'tabs and spaces are accepted in the todolist' '
+ rebase_setup_and_clean indented-comment &&
+ write_script add-indent.sh <<-\EOF &&
+ (
+ # Turn single spaces into space/tab mix
+ sed "1s/ / /g; 2s/ / /g; 3s/ / /g" "$1"
+ printf "\n\t# comment\n #more\n\t # comment\n"
+ ) >"$1.new"
+ mv "$1.new" "$1"
+ EOF
+ test_set_editor "$(pwd)/add-indent.sh" &&
+ git rebase -i HEAD^^^ &&
+ test E = $(git cat-file commit HEAD | sed -ne \$p)
+'
+
+cat >expect <<EOF
+Warning: the SHA-1 is missing or isn't a commit in the following line:
+ - edit XXXXXXX False commit
+
+You can fix this with 'git rebase --edit-todo'.
+Or you can abort the rebase with 'git rebase --abort'.
+EOF
+
+test_expect_success 'static check of bad SHA-1' '
+ rebase_setup_and_clean bad-sha &&
+ set_fake_editor &&
+ test_must_fail env FAKE_LINES="1 2 edit fakesha 3 4 5 #" \
+ git rebase -i --root 2>actual &&
+ test_cmp expect actual &&
+ FAKE_LINES="1 2 4 5 6" git rebase --edit-todo &&
+ git rebase --continue &&
+ test E = $(git cat-file commit HEAD | sed -ne \$p)
+'
+
+test_expect_success 'editor saves as CR/LF' '
+ git checkout -b with-crlf &&
+ write_script add-crs.sh <<-\EOF &&
+ sed -e "s/\$/Q/" <"$1" | tr Q "\\015" >"$1".new &&
+ mv -f "$1".new "$1"
+ EOF
+ (
+ test_set_editor "$(pwd)/add-crs.sh" &&
+ git rebase -i HEAD^
+ )
+'
+
test_done
diff --git a/t/t3405-rebase-malformed.sh b/t/t3405-rebase-malformed.sh
index e5ad67c..ff8c360 100755
--- a/t/t3405-rebase-malformed.sh
+++ b/t/t3405-rebase-malformed.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-test_description='rebase should not insist on git message convention'
+test_description='rebase should handle arbitrary git message'
. ./test-lib.sh
@@ -12,6 +12,11 @@ It has two paragraphs, but its first paragraph is not friendly
to oneline summary format.
EOF
+cat >G <<\EOF
+commit log message containing a diff
+EOF
+
+
test_expect_success setup '
>file1 &&
@@ -19,8 +24,9 @@ test_expect_success setup '
git add file1 file2 &&
test_tick &&
git commit -m "Initial commit" &&
+ git branch diff-in-message &&
- git checkout -b side &&
+ git checkout -b multi-line-subject &&
cat F >file2 &&
git add file2 &&
test_tick &&
@@ -28,6 +34,17 @@ test_expect_success setup '
git cat-file commit HEAD | sed -e "1,/^\$/d" >F0 &&
+ git checkout diff-in-message &&
+ echo "commit log message containing a diff" >G &&
+ echo "" >>G &&
+ cat G >file2 &&
+ git add file2 &&
+ git diff --cached >>G &&
+ test_tick &&
+ git commit -F G &&
+
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >G0 &&
+
git checkout master &&
echo One >file1 &&
@@ -36,13 +53,20 @@ test_expect_success setup '
git commit -m "Second commit"
'
-test_expect_success rebase '
+test_expect_success 'rebase commit with multi-line subject' '
- git rebase master side &&
+ git rebase master multi-line-subject &&
git cat-file commit HEAD | sed -e "1,/^\$/d" >F1 &&
test_cmp F0 F1 &&
test_cmp F F0
'
+test_expect_success 'rebase commit with diff in message' '
+ git rebase master diff-in-message &&
+ git cat-file commit HEAD | sed -e "1,/^$/d" >G1 &&
+ test_cmp G0 G1 &&
+ test_cmp G G0
+'
+
test_done
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index 85fc7c4..0392e36 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -4,27 +4,17 @@ test_description='messages from rebase operation'
. ./test-lib.sh
-quick_one () {
- echo "$1" >"file$1" &&
- git add "file$1" &&
- test_tick &&
- git commit -m "$1"
-}
+test_expect_success 'setup' '
+ test_commit O fileO &&
+ test_commit X fileX &&
+ test_commit A fileA &&
+ test_commit B fileB &&
+ test_commit Y fileY &&
-test_expect_success setup '
- quick_one O &&
- git branch topic &&
- quick_one X &&
- quick_one A &&
- quick_one B &&
- quick_one Y &&
-
- git checkout topic &&
- quick_one A &&
- quick_one B &&
- quick_one Z &&
+ git checkout -b topic O &&
+ git cherry-pick A B &&
+ test_commit Z fileZ &&
git tag start
-
'
cat >expect <<\EOF
@@ -34,32 +24,64 @@ Committed: 0003 Z
EOF
test_expect_success 'rebase -m' '
-
git rebase -m master >report &&
sed -n -e "/^Already applied: /p" \
-e "/^Committed: /p" report >actual &&
test_cmp expect actual
+'
+
+test_expect_success 'rebase against master twice' '
+ git rebase master >out &&
+ test_i18ngrep "Current branch topic is up to date" out
+'
+test_expect_success 'rebase against master twice with --force' '
+ git rebase --force-rebase master >out &&
+ test_i18ngrep "Current branch topic is up to date, rebase forced" out
+'
+
+test_expect_success 'rebase against master twice from another branch' '
+ git checkout topic^ &&
+ git rebase master topic >out &&
+ test_i18ngrep "Current branch topic is up to date" out
+'
+
+test_expect_success 'rebase fast-forward to master' '
+ git checkout topic^ &&
+ git rebase topic >out &&
+ test_i18ngrep "Fast-forwarded HEAD to topic" out
'
test_expect_success 'rebase --stat' '
- git reset --hard start
+ 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 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 reset --hard start &&
git config rebase.stat true &&
git rebase -n master >diffstat.txt &&
! grep "^ fileX | *1 +$" diffstat.txt
'
+# Output to stderr:
+#
+# "Does not point to a valid commit: invalid-ref"
+#
+# NEEDSWORK: This "grep" is fine in real non-C locales, but
+# GETTEXT_POISON poisons the refname along with the enclosing
+# error message.
+test_expect_success 'rebase --onto outputs the invalid ref' '
+ test_must_fail git rebase --onto invalid-ref HEAD HEAD 2>err &&
+ test_i18ngrep "invalid-ref" err
+'
+
test_done
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
index 2999e78..a6a6c40 100755
--- a/t/t3407-rebase-abort.sh
+++ b/t/t3407-rebase-abort.sh
@@ -38,7 +38,7 @@ testrebase() {
# Clean up the state from the previous one
git reset --hard pre-rebase &&
test_must_fail git rebase$type master &&
- test -d "$dotest" &&
+ test_path_is_dir "$dotest" &&
git rebase --abort &&
test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
test ! -d "$dotest"
@@ -49,7 +49,7 @@ testrebase() {
# Clean up the state from the previous one
git reset --hard pre-rebase &&
test_must_fail git rebase$type master &&
- test -d "$dotest" &&
+ test_path_is_dir "$dotest" &&
test_must_fail git rebase --skip &&
test $(git rev-parse HEAD) = $(git rev-parse master) &&
git rebase --abort &&
@@ -62,7 +62,7 @@ testrebase() {
# Clean up the state from the previous one
git reset --hard pre-rebase &&
test_must_fail git rebase$type master &&
- test -d "$dotest" &&
+ test_path_is_dir "$dotest" &&
echo c > a &&
echo d >> a &&
git add a &&
@@ -72,6 +72,28 @@ testrebase() {
test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
test ! -d "$dotest"
'
+
+ test_expect_success "rebase$type --abort does not update reflog" '
+ cd "$work_dir" &&
+ # Clean up the state from the previous one
+ git reset --hard pre-rebase &&
+ git reflog show to-rebase > reflog_before &&
+ test_must_fail git rebase$type master &&
+ git rebase --abort &&
+ git reflog show to-rebase > reflog_after &&
+ test_cmp reflog_before reflog_after &&
+ rm reflog_before reflog_after
+ '
+
+ test_expect_success 'rebase --abort can not be used with other options' '
+ cd "$work_dir" &&
+ # Clean up the state from the previous one
+ git reset --hard pre-rebase &&
+ test_must_fail git rebase$type master &&
+ test_must_fail git rebase -v --abort &&
+ test_must_fail git rebase --abort -v &&
+ git rebase --abort
+ '
}
testrebase "" .git/rebase-apply
diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh
index 2062b85..6b84e60 100755
--- a/t/t3408-rebase-multi-line.sh
+++ b/t/t3408-rebase-multi-line.sh
@@ -16,7 +16,7 @@ test_expect_success setup '
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."
+But otherwise with a sane description." &&
git branch side &&
diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh
index 8f785e7..8c251c5 100755
--- a/t/t3409-rebase-preserve-merges.sh
+++ b/t/t3409-rebase-preserve-merges.sh
@@ -11,14 +11,6 @@ Run "git rebase -p" and check that merges are properly carried along
GIT_AUTHOR_EMAIL=bogus_email_address
export GIT_AUTHOR_EMAIL
-# Clone 1 (trivial merge):
-#
-# A1--A2 <-- origin/master
-# \ \
-# B1--M <-- topic
-# \
-# B2 <-- origin/topic
-#
# Clone 2 (conflicting merge):
#
# A1--A2--B3 <-- origin/master
@@ -27,7 +19,17 @@ export GIT_AUTHOR_EMAIL
# \
# B2 <-- origin/topic
#
-# In both cases, 'topic' is rebased onto 'origin/topic'.
+# Clone 3 (no-ff merge):
+#
+# A1--A2--B3 <-- origin/master
+# \
+# B1------M <-- topic
+# \ /
+# \--A3 <-- topic2
+# \
+# B2 <-- origin/topic
+#
+# Clone 4 (same as Clone 3)
test_expect_success 'setup for merge-preserving rebase' \
'echo First > A &&
@@ -40,41 +42,45 @@ test_expect_success 'setup for merge-preserving rebase' \
git checkout -f master &&
echo Third >> A &&
git commit -a -m "Modify A2" &&
-
- git clone ./. clone1 &&
- cd clone1 &&
- git checkout -b topic origin/topic &&
- git merge origin/master &&
- 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 .. &&
+ (
+ 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"
+ ) &&
+
+ git clone ./. clone3 &&
+ (
+ cd clone3 &&
+ git checkout -b topic2 origin/topic &&
+ echo Sixth > A &&
+ git commit -a -m "Modify A3" &&
+ git checkout -b topic origin/topic &&
+ git merge --no-ff topic2
+ ) &&
+
+ git clone ./. clone4 &&
+ (
+ cd clone4 &&
+ git checkout -b topic2 origin/topic &&
+ echo Sixth > A &&
+ git commit -a -m "Modify A3" &&
+ git checkout -b topic origin/topic &&
+ git merge --no-ff topic2
+ ) &&
git checkout topic &&
echo Fourth >> B &&
git commit -a -m "Modify B2"
'
-test_expect_success 'rebase -p fakes interactive rebase' '
- (
- cd clone1 &&
- git fetch &&
- git rebase -p origin/topic &&
- test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
- test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote branch " | wc -l)
- )
-'
-
test_expect_success '--continue works after a conflict' '
(
cd clone2 &&
@@ -92,4 +98,25 @@ test_expect_success '--continue works after a conflict' '
)
'
+test_expect_success 'rebase -p preserves no-ff merges' '
+ (
+ cd clone3 &&
+ git fetch &&
+ git rebase -p origin/topic &&
+ test 3 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
+ test 1 = $(git rev-list --all --pretty=oneline | grep "Merge branch" | wc -l)
+ )
+'
+
+test_expect_success 'rebase -p ignores merge.log config' '
+ (
+ cd clone4 &&
+ git fetch &&
+ git -c merge.log=1 rebase -p origin/topic &&
+ echo >expected &&
+ git log --format="%b" -1 >current &&
+ test_cmp expected current
+ )
+'
+
test_done
diff --git a/t/t3410-rebase-preserve-dropped-merges.sh b/t/t3410-rebase-preserve-dropped-merges.sh
index c49143a..6f73b95 100755
--- a/t/t3410-rebase-preserve-dropped-merges.sh
+++ b/t/t3410-rebase-preserve-dropped-merges.sh
@@ -43,11 +43,11 @@ test_expect_success 'setup' '
# G2 = same changes as G
test_expect_success 'skip same-resolution merges with -p' '
git checkout H &&
- ! git merge E &&
+ test_must_fail git merge E &&
test_commit L file1 23 &&
git checkout I &&
test_commit G2 file1 3 &&
- ! git merge E &&
+ test_must_fail git merge E &&
test_commit J file1 23 &&
test_commit K file7 file7 &&
git rebase -i -p L &&
@@ -65,11 +65,11 @@ test_expect_success 'skip same-resolution merges with -p' '
# G2 = different changes as G
test_expect_success 'keep different-resolution merges with -p' '
git checkout H &&
- ! git merge E &&
+ test_must_fail git merge E &&
test_commit L2 file1 23 &&
git checkout I &&
test_commit G3 file1 4 &&
- ! git merge E &&
+ test_must_fail git merge E &&
test_commit J2 file1 24 &&
test_commit K2 file7 file7 &&
test_must_fail git rebase -i -p L2 &&
diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh
index 14a23cd..dc81bf2 100755
--- a/t/t3411-rebase-preserve-around-merges.sh
+++ b/t/t3411-rebase-preserve-around-merges.sh
@@ -37,7 +37,7 @@ test_expect_success 'setup' '
# -- C1 --
#
test_expect_success 'squash F1 into D1' '
- FAKE_LINES="1 squash 3 2" git rebase -i -p B1 &&
+ FAKE_LINES="1 squash 4 2 3" 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
@@ -56,6 +56,7 @@ test_expect_success 'squash F1 into D1' '
# And rebase G1..M1 onto E2
test_expect_success 'rebase two levels of merge' '
+ git checkout A1 &&
test_commit G1 &&
test_commit H1 &&
test_commit I1 &&
diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh
index 5869061..73a39f2 100755
--- a/t/t3412-rebase-root.sh
+++ b/t/t3412-rebase-root.sh
@@ -22,8 +22,9 @@ test_expect_success 'prepare repository' '
test_commit 4 B
'
-test_expect_success 'rebase --root expects --onto' '
- test_must_fail git rebase --root
+test_expect_success 'rebase --root fails with too many args' '
+ git checkout -B fail other &&
+ test_must_fail git rebase --onto master --root fail fail
'
test_expect_success 'setup pre-rebase hook' '
@@ -42,7 +43,7 @@ cat > expect <<EOF
EOF
test_expect_success 'rebase --root --onto <newbase>' '
- git checkout -b work &&
+ git checkout -b work other &&
git rebase --root --onto master &&
git log --pretty=tformat:"%s" > rebased &&
test_cmp expect rebased
@@ -132,7 +133,7 @@ test_expect_success 'set up second root and merge' '
rm A B C &&
test_commit 6 D &&
git checkout other &&
- git merge third
+ git merge --allow-unrelated-histories third
'
cat > expect-third <<'EOF'
@@ -173,14 +174,14 @@ EOF
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 "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 "z$(git symbolic-ref HEAD)" = zrefs/heads/stops2 &&
test 0 = $(git rev-list other...stops2 | wc -l)
'
diff --git a/t/t3413-rebase-hook.sh b/t/t3413-rebase-hook.sh
index 098b755..b6833e9 100755
--- a/t/t3413-rebase-hook.sh
+++ b/t/t3413-rebase-hook.sh
@@ -118,11 +118,7 @@ test_expect_success 'pre-rebase hook stops rebase (1)' '
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_must_fail env EDITOR=: git rebase -i master &&
test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
test 0 = $(git rev-list HEAD...side | wc -l)
'
diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh
index b63f4e2..8f53e54 100755
--- a/t/t3415-rebase-autosquash.sh
+++ b/t/t3415-rebase-autosquash.sh
@@ -4,6 +4,8 @@ test_description='auto squash'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
test_expect_success setup '
echo 0 >file0 &&
git add . &&
@@ -14,6 +16,7 @@ test_expect_success setup '
git add . &&
test_tick &&
git commit -m "first commit" &&
+ git tag first-commit &&
echo 3 >file3 &&
git add . &&
test_tick &&
@@ -21,38 +24,62 @@ test_expect_success setup '
git tag base
'
-test_expect_success 'auto fixup' '
+test_auto_fixup () {
git reset --hard base &&
echo 1 >file1 &&
git add -u &&
test_tick &&
- git commit -m "fixup! first"
+ git commit -m "fixup! first" &&
- git tag final-fixup &&
+ git tag $1 &&
test_tick &&
- git rebase --autosquash -i HEAD^^^ &&
+ git rebase $2 -i HEAD^^^ &&
git log --oneline >actual &&
- test 3 = $(wc -l <actual) &&
- git diff --exit-code final-fixup &&
+ test_line_count = 3 actual &&
+ git diff --exit-code $1 &&
test 1 = "$(git cat-file blob HEAD^:file1)" &&
test 1 = $(git cat-file commit HEAD^ | grep first | wc -l)
+}
+
+test_expect_success 'auto fixup (option)' '
+ test_auto_fixup final-fixup-option --autosquash
'
-test_expect_success 'auto squash' '
+test_expect_success 'auto fixup (config)' '
+ git config rebase.autosquash true &&
+ test_auto_fixup final-fixup-config-true &&
+ test_must_fail test_auto_fixup fixup-config-true-no --no-autosquash &&
+ git config rebase.autosquash false &&
+ test_must_fail test_auto_fixup final-fixup-config-false
+'
+
+test_auto_squash () {
git reset --hard base &&
echo 1 >file1 &&
git add -u &&
test_tick &&
- git commit -m "squash! first"
+ git commit -m "squash! first" &&
- git tag final-squash &&
+ git tag $1 &&
test_tick &&
- git rebase --autosquash -i HEAD^^^ &&
+ git rebase $2 -i HEAD^^^ &&
git log --oneline >actual &&
- test 3 = $(wc -l <actual) &&
- git diff --exit-code final-squash &&
+ test_line_count = 3 actual &&
+ git diff --exit-code $1 &&
test 1 = "$(git cat-file blob HEAD^:file1)" &&
test 2 = $(git cat-file commit HEAD^ | grep first | wc -l)
+}
+
+test_expect_success 'auto squash (option)' '
+ test_auto_squash final-squash --autosquash
+'
+
+test_expect_success 'auto squash (config)' '
+ git config rebase.autosquash true &&
+ test_auto_squash final-squash-config-true &&
+ test_must_fail test_auto_squash squash-config-true-no --no-autosquash &&
+ git config rebase.autosquash false &&
+ test_must_fail test_auto_squash final-squash-config-false
'
test_expect_success 'misspelled auto squash' '
@@ -60,14 +87,188 @@ test_expect_success 'misspelled auto squash' '
echo 1 >file1 &&
git add -u &&
test_tick &&
- git commit -m "squash! forst"
+ 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) &&
+ test_line_count = 4 actual &&
git diff --exit-code final-missquash &&
test 0 = $(git rev-list final-missquash...HEAD | wc -l)
'
+test_expect_success 'auto squash that matches 2 commits' '
+ git reset --hard base &&
+ echo 4 >file4 &&
+ git add file4 &&
+ test_tick &&
+ git commit -m "first new commit" &&
+ echo 1 >file1 &&
+ git add -u &&
+ test_tick &&
+ git commit -m "squash! first" &&
+ git tag final-multisquash &&
+ test_tick &&
+ git rebase --autosquash -i HEAD~4 &&
+ git log --oneline >actual &&
+ test_line_count = 4 actual &&
+ git diff --exit-code final-multisquash &&
+ test 1 = "$(git cat-file blob HEAD^^:file1)" &&
+ test 2 = $(git cat-file commit HEAD^^ | grep first | wc -l) &&
+ test 1 = $(git cat-file commit HEAD | grep first | wc -l)
+'
+
+test_expect_success 'auto squash that matches a commit after the squash' '
+ git reset --hard base &&
+ echo 1 >file1 &&
+ git add -u &&
+ test_tick &&
+ git commit -m "squash! third" &&
+ echo 4 >file4 &&
+ git add file4 &&
+ test_tick &&
+ git commit -m "third commit" &&
+ git tag final-presquash &&
+ test_tick &&
+ git rebase --autosquash -i HEAD~4 &&
+ git log --oneline >actual &&
+ test_line_count = 5 actual &&
+ git diff --exit-code final-presquash &&
+ test 0 = "$(git cat-file blob HEAD^^:file1)" &&
+ test 1 = "$(git cat-file blob HEAD^:file1)" &&
+ test 1 = $(git cat-file commit HEAD | grep third | wc -l) &&
+ test 1 = $(git cat-file commit HEAD^ | grep third | wc -l)
+'
+test_expect_success 'auto squash that matches a sha1' '
+ git reset --hard base &&
+ echo 1 >file1 &&
+ git add -u &&
+ test_tick &&
+ git commit -m "squash! $(git rev-parse --short HEAD^)" &&
+ git tag final-shasquash &&
+ test_tick &&
+ git rebase --autosquash -i HEAD^^^ &&
+ git log --oneline >actual &&
+ test_line_count = 3 actual &&
+ git diff --exit-code final-shasquash &&
+ test 1 = "$(git cat-file blob HEAD^:file1)" &&
+ test 1 = $(git cat-file commit HEAD^ | grep squash | wc -l)
+'
+
+test_expect_success 'auto squash that matches longer sha1' '
+ git reset --hard base &&
+ echo 1 >file1 &&
+ git add -u &&
+ test_tick &&
+ git commit -m "squash! $(git rev-parse --short=11 HEAD^)" &&
+ git tag final-longshasquash &&
+ test_tick &&
+ git rebase --autosquash -i HEAD^^^ &&
+ git log --oneline >actual &&
+ test_line_count = 3 actual &&
+ git diff --exit-code final-longshasquash &&
+ test 1 = "$(git cat-file blob HEAD^:file1)" &&
+ test 1 = $(git cat-file commit HEAD^ | grep squash | wc -l)
+'
+
+test_auto_commit_flags () {
+ git reset --hard base &&
+ echo 1 >file1 &&
+ git add -u &&
+ test_tick &&
+ git commit --$1 first-commit &&
+ git tag final-commit-$1 &&
+ test_tick &&
+ git rebase --autosquash -i HEAD^^^ &&
+ git log --oneline >actual &&
+ test_line_count = 3 actual &&
+ git diff --exit-code final-commit-$1 &&
+ test 1 = "$(git cat-file blob HEAD^:file1)" &&
+ test $2 = $(git cat-file commit HEAD^ | grep first | wc -l)
+}
+
+test_expect_success 'use commit --fixup' '
+ test_auto_commit_flags fixup 1
+'
+
+test_expect_success 'use commit --squash' '
+ test_auto_commit_flags squash 2
+'
+
+test_auto_fixup_fixup () {
+ git reset --hard base &&
+ echo 1 >file1 &&
+ git add -u &&
+ test_tick &&
+ git commit -m "$1! first" &&
+ echo 2 >file1 &&
+ git add -u &&
+ test_tick &&
+ git commit -m "$1! $2! first" &&
+ git tag "final-$1-$2" &&
+ test_tick &&
+ (
+ set_cat_todo_editor &&
+ test_must_fail git rebase --autosquash -i HEAD^^^^ >actual &&
+ cat >expected <<-EOF &&
+ pick $(git rev-parse --short HEAD^^^) first commit
+ $1 $(git rev-parse --short HEAD^) $1! first
+ $1 $(git rev-parse --short HEAD) $1! $2! first
+ pick $(git rev-parse --short HEAD^^) second commit
+ EOF
+ test_cmp expected actual
+ ) &&
+ git rebase --autosquash -i HEAD^^^^ &&
+ git log --oneline >actual &&
+ test_line_count = 3 actual
+ git diff --exit-code "final-$1-$2" &&
+ test 2 = "$(git cat-file blob HEAD^:file1)" &&
+ if test "$1" = "fixup"
+ then
+ test 1 = $(git cat-file commit HEAD^ | grep first | wc -l)
+ elif test "$1" = "squash"
+ then
+ test 3 = $(git cat-file commit HEAD^ | grep first | wc -l)
+ else
+ false
+ fi
+}
+
+test_expect_success 'fixup! fixup!' '
+ test_auto_fixup_fixup fixup fixup
+'
+
+test_expect_success 'fixup! squash!' '
+ test_auto_fixup_fixup fixup squash
+'
+
+test_expect_success 'squash! squash!' '
+ test_auto_fixup_fixup squash squash
+'
+
+test_expect_success 'squash! fixup!' '
+ test_auto_fixup_fixup squash fixup
+'
+
+test_expect_success 'autosquash with custom inst format' '
+ git reset --hard base &&
+ git config --add rebase.instructionFormat "[%an @ %ar] %s" &&
+ echo 2 >file1 &&
+ git add -u &&
+ test_tick &&
+ git commit -m "squash! $(git rev-parse --short HEAD^)" &&
+ echo 1 >file1 &&
+ git add -u &&
+ test_tick &&
+ git commit -m "squash! $(git log -n 1 --format=%s HEAD~2)" &&
+ git tag final-squash-instFmt &&
+ test_tick &&
+ git rebase --autosquash -i HEAD~4 &&
+ git log --oneline >actual &&
+ test_line_count = 3 actual &&
+ git diff --exit-code final-squash-instFmt &&
+ test 1 = "$(git cat-file blob HEAD^:file1)" &&
+ test 2 = $(git cat-file commit HEAD^ | grep squash | wc -l)
+'
+
test_done
diff --git a/t/t3417-rebase-whitespace-fix.sh b/t/t3417-rebase-whitespace-fix.sh
index 220a740..1fb3e49 100755
--- a/t/t3417-rebase-whitespace-fix.sh
+++ b/t/t3417-rebase-whitespace-fix.sh
@@ -89,7 +89,7 @@ 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 rebase --whitespace=fix HEAD^^ &&
git diff --exit-code HEAD^:file expect-second &&
test_cmp file third
'
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
new file mode 100755
index 0000000..4428b90
--- /dev/null
+++ b/t/t3418-rebase-continue.sh
@@ -0,0 +1,117 @@
+#!/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_expect_success 'non-interactive rebase --continue with rerere enabled' '
+ test_config rerere.enabled true &&
+ test_when_finished "test_might_fail git rebase --abort" &&
+ git reset --hard commit-new-file-F2-on-topic-branch &&
+ git checkout master &&
+ rm -fr .git/rebase-* &&
+
+ test_must_fail git rebase --onto master master topic &&
+ echo "Resolved" >F2 &&
+ git add F2 &&
+ cp F2 F2.expected &&
+ git rebase --continue &&
+
+ git reset --hard commit-new-file-F2-on-topic-branch &&
+ git checkout master &&
+ test_must_fail git rebase --onto master master topic &&
+ test_cmp F2.expected F2
+'
+
+test_expect_success 'rebase --continue can not be used with other options' '
+ test_must_fail git rebase -v --continue &&
+ test_must_fail git rebase --continue -v
+'
+
+test_expect_success 'rebase --continue remembers merge strategy and options' '
+ rm -fr .git/rebase-* &&
+ git reset --hard commit-new-file-F2-on-topic-branch &&
+ test_commit "commit-new-file-F3-on-topic-branch" F3 32 &&
+ test_when_finished "rm -fr test-bin funny.was.run" &&
+ mkdir test-bin &&
+ cat >test-bin/git-merge-funny <<-EOF &&
+ #!$SHELL_PATH
+ case "\$1" in --opt) ;; *) exit 2 ;; esac
+ shift &&
+ >funny.was.run &&
+ exec git merge-recursive "\$@"
+ EOF
+ chmod +x test-bin/git-merge-funny &&
+ (
+ PATH=./test-bin:$PATH
+ test_must_fail git rebase -s funny -Xopt master topic
+ ) &&
+ test -f funny.was.run &&
+ rm funny.was.run &&
+ echo "Resolved" >F2 &&
+ git add F2 &&
+ (
+ PATH=./test-bin:$PATH
+ git rebase --continue
+ ) &&
+ test -f funny.was.run
+'
+
+test_expect_success 'rebase --continue remembers --rerere-autoupdate' '
+ rm -fr .git/rebase-* &&
+ git reset --hard commit-new-file-F3-on-topic-branch &&
+ git checkout master &&
+ test_commit "commit-new-file-F3" F3 3 &&
+ git config rerere.enabled true &&
+ test_must_fail git rebase -m master topic &&
+ echo "Resolved" >F2 &&
+ git add F2 &&
+ test_must_fail git rebase --continue &&
+ echo "Resolved" >F3 &&
+ git add F3 &&
+ git rebase --continue &&
+ git reset --hard topic@{1} &&
+ test_must_fail git rebase -m --rerere-autoupdate master &&
+ test "$(cat F2)" = "Resolved" &&
+ test_must_fail git rebase --continue &&
+ test "$(cat F3)" = "Resolved" &&
+ git rebase --continue
+'
+
+test_done
diff --git a/t/t3419-rebase-patch-id.sh b/t/t3419-rebase-patch-id.sh
new file mode 100755
index 0000000..49f548c
--- /dev/null
+++ b/t/t3419-rebase-patch-id.sh
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+test_description='git rebase - test patch id computation'
+
+. ./test-lib.sh
+
+count () {
+ i=0
+ while test $i -lt $1
+ do
+ echo "$i"
+ i=$(($i+1))
+ done
+}
+
+scramble () {
+ i=0
+ while read x
+ do
+ if test $i -ne 0
+ then
+ echo "$x"
+ fi
+ i=$((($i+1) % 10))
+ done <"$1" >"$1.new"
+ mv -f "$1.new" "$1"
+}
+
+run () {
+ echo \$ "$@"
+ /usr/bin/time "$@" >/dev/null
+}
+
+test_expect_success 'setup' '
+ git commit --allow-empty -m initial &&
+ git tag root
+'
+
+do_tests () {
+ nlines=$1 pr=${2-}
+
+ test_expect_success $pr "setup: $nlines lines" "
+ rm -f .gitattributes &&
+ git checkout -q -f master &&
+ git reset --hard root &&
+ count $nlines >file &&
+ git add file &&
+ git commit -q -m initial &&
+ git branch -f other &&
+
+ scramble file &&
+ git add file &&
+ git commit -q -m 'change big file' &&
+
+ git checkout -q other &&
+ : >newfile &&
+ git add newfile &&
+ git commit -q -m 'add small file' &&
+
+ git cherry-pick master >/dev/null 2>&1
+ "
+
+ test_debug "
+ run git diff master^\!
+ "
+
+ test_expect_success $pr 'setup attributes' "
+ echo 'file binary' >.gitattributes
+ "
+
+ test_debug "
+ run git format-patch --stdout master &&
+ run git format-patch --stdout --ignore-if-in-upstream master
+ "
+
+ test_expect_success $pr 'detect upstream patch' '
+ git checkout -q master &&
+ scramble file &&
+ git add file &&
+ git commit -q -m "change big file again" &&
+ git checkout -q other^{} &&
+ git rebase master &&
+ test_must_fail test -n "$(git rev-list master...HEAD~)"
+ '
+
+ test_expect_success $pr 'do not drop patch' '
+ git branch -f squashed master &&
+ git checkout -q -f squashed &&
+ git reset -q --soft HEAD~2 &&
+ git commit -q -m squashed &&
+ git checkout -q other^{} &&
+ test_must_fail git rebase squashed &&
+ rm -rf .git/rebase-apply
+ '
+}
+
+do_tests 500
+do_tests 50000 EXPENSIVE
+
+test_done
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
new file mode 100755
index 0000000..532ff5c
--- /dev/null
+++ b/t/t3420-rebase-autostash.sh
@@ -0,0 +1,226 @@
+#!/bin/sh
+#
+# Copyright (c) 2013 Ramkumar Ramachandra
+#
+
+test_description='git rebase --autostash tests'
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo hello-world >file0 &&
+ git add . &&
+ test_tick &&
+ git commit -m "initial commit" &&
+ git checkout -b feature-branch &&
+ echo another-hello >file1 &&
+ echo goodbye >file2 &&
+ git add . &&
+ test_tick &&
+ git commit -m "second commit" &&
+ echo final-goodbye >file3 &&
+ git add . &&
+ test_tick &&
+ git commit -m "third commit" &&
+ git checkout -b unrelated-onto-branch master &&
+ echo unrelated >file4 &&
+ git add . &&
+ test_tick &&
+ git commit -m "unrelated commit" &&
+ git checkout -b related-onto-branch master &&
+ echo conflicting-change >file2 &&
+ git add . &&
+ test_tick &&
+ git commit -m "related commit"
+'
+
+testrebase() {
+ type=$1
+ dotest=$2
+
+ test_expect_success "rebase$type: dirty worktree, --no-autostash" '
+ test_config rebase.autostash true &&
+ git reset --hard &&
+ git checkout -b rebased-feature-branch feature-branch &&
+ test_when_finished git branch -D rebased-feature-branch &&
+ test_when_finished git checkout feature-branch &&
+ echo dirty >>file3 &&
+ test_must_fail git rebase$type --no-autostash unrelated-onto-branch
+ '
+
+ test_expect_success "rebase$type: dirty worktree, non-conflicting rebase" '
+ test_config rebase.autostash true &&
+ git reset --hard &&
+ git checkout -b rebased-feature-branch feature-branch &&
+ test_when_finished git branch -D rebased-feature-branch &&
+ echo dirty >>file3 &&
+ git rebase$type unrelated-onto-branch &&
+ grep unrelated file4 &&
+ grep dirty file3 &&
+ git checkout feature-branch
+ '
+
+ test_expect_success "rebase$type: dirty index, non-conflicting rebase" '
+ test_config rebase.autostash true &&
+ git reset --hard &&
+ git checkout -b rebased-feature-branch feature-branch &&
+ test_when_finished git branch -D rebased-feature-branch &&
+ echo dirty >>file3 &&
+ git add file3 &&
+ git rebase$type unrelated-onto-branch &&
+ grep unrelated file4 &&
+ grep dirty file3 &&
+ git checkout feature-branch
+ '
+
+ test_expect_success "rebase$type: conflicting rebase" '
+ test_config rebase.autostash true &&
+ git reset --hard &&
+ git checkout -b rebased-feature-branch feature-branch &&
+ test_when_finished git branch -D rebased-feature-branch &&
+ echo dirty >>file3 &&
+ test_must_fail git rebase$type related-onto-branch &&
+ test_path_is_file $dotest/autostash &&
+ ! grep dirty file3 &&
+ rm -rf $dotest &&
+ git reset --hard &&
+ git checkout feature-branch
+ '
+
+ test_expect_success "rebase$type: --continue" '
+ test_config rebase.autostash true &&
+ git reset --hard &&
+ git checkout -b rebased-feature-branch feature-branch &&
+ test_when_finished git branch -D rebased-feature-branch &&
+ echo dirty >>file3 &&
+ test_must_fail git rebase$type related-onto-branch &&
+ test_path_is_file $dotest/autostash &&
+ ! grep dirty file3 &&
+ echo "conflicting-plus-goodbye" >file2 &&
+ git add file2 &&
+ git rebase --continue &&
+ test_path_is_missing $dotest/autostash &&
+ grep dirty file3 &&
+ git checkout feature-branch
+ '
+
+ test_expect_success "rebase$type: --skip" '
+ test_config rebase.autostash true &&
+ git reset --hard &&
+ git checkout -b rebased-feature-branch feature-branch &&
+ test_when_finished git branch -D rebased-feature-branch &&
+ echo dirty >>file3 &&
+ test_must_fail git rebase$type related-onto-branch &&
+ test_path_is_file $dotest/autostash &&
+ ! grep dirty file3 &&
+ git rebase --skip &&
+ test_path_is_missing $dotest/autostash &&
+ grep dirty file3 &&
+ git checkout feature-branch
+ '
+
+ test_expect_success "rebase$type: --abort" '
+ test_config rebase.autostash true &&
+ git reset --hard &&
+ git checkout -b rebased-feature-branch feature-branch &&
+ test_when_finished git branch -D rebased-feature-branch &&
+ echo dirty >>file3 &&
+ test_must_fail git rebase$type related-onto-branch &&
+ test_path_is_file $dotest/autostash &&
+ ! grep dirty file3 &&
+ git rebase --abort &&
+ test_path_is_missing $dotest/autostash &&
+ grep dirty file3 &&
+ git checkout feature-branch
+ '
+
+ test_expect_success "rebase$type: non-conflicting rebase, conflicting stash" '
+ test_config rebase.autostash true &&
+ git reset --hard &&
+ git checkout -b rebased-feature-branch feature-branch &&
+ test_when_finished git branch -D rebased-feature-branch &&
+ echo dirty >file4 &&
+ git add file4 &&
+ git rebase$type unrelated-onto-branch &&
+ test_path_is_missing $dotest &&
+ git reset --hard &&
+ grep unrelated file4 &&
+ ! grep dirty file4 &&
+ git checkout feature-branch &&
+ git stash pop &&
+ grep dirty file4
+ '
+}
+
+test_expect_success "rebase: fast-forward rebase" '
+ test_config rebase.autostash true &&
+ git reset --hard &&
+ git checkout -b behind-feature-branch feature-branch~1 &&
+ test_when_finished git branch -D behind-feature-branch &&
+ echo dirty >>file1 &&
+ git rebase feature-branch &&
+ grep dirty file1 &&
+ git checkout feature-branch
+'
+
+test_expect_success "rebase: noop rebase" '
+ test_config rebase.autostash true &&
+ git reset --hard &&
+ git checkout -b same-feature-branch feature-branch &&
+ test_when_finished git branch -D same-feature-branch &&
+ echo dirty >>file1 &&
+ git rebase feature-branch &&
+ grep dirty file1 &&
+ git checkout feature-branch
+'
+
+testrebase "" .git/rebase-apply
+testrebase " --merge" .git/rebase-merge
+testrebase " --interactive" .git/rebase-merge
+
+test_expect_success 'abort rebase -i with --autostash' '
+ test_when_finished "git reset --hard" &&
+ echo uncommited-content >file0 &&
+ (
+ write_script abort-editor.sh <<-\EOF &&
+ echo >"$1"
+ EOF
+ test_set_editor "$(pwd)/abort-editor.sh" &&
+ test_must_fail git rebase -i --autostash HEAD^ &&
+ rm -f abort-editor.sh
+ ) &&
+ echo uncommited-content >expected &&
+ test_cmp expected file0
+'
+
+test_expect_success 'restore autostash on editor failure' '
+ test_when_finished "git reset --hard" &&
+ echo uncommitted-content >file0 &&
+ (
+ test_set_editor "false" &&
+ test_must_fail git rebase -i --autostash HEAD^
+ ) &&
+ echo uncommitted-content >expected &&
+ test_cmp expected file0
+'
+
+test_expect_success 'autostash is saved on editor failure with conflict' '
+ test_when_finished "git reset --hard" &&
+ echo uncommitted-content >file0 &&
+ (
+ write_script abort-editor.sh <<-\EOF &&
+ echo conflicting-content >file0
+ exit 1
+ EOF
+ test_set_editor "$(pwd)/abort-editor.sh" &&
+ test_must_fail git rebase -i --autostash HEAD^ &&
+ rm -f abort-editor.sh
+ ) &&
+ echo conflicting-content >expected &&
+ test_cmp expected file0 &&
+ git checkout file0 &&
+ git stash pop &&
+ echo uncommitted-content >expected &&
+ test_cmp expected file0
+'
+
+test_done
diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh
new file mode 100755
index 0000000..68fe200
--- /dev/null
+++ b/t/t3421-rebase-topology-linear.sh
@@ -0,0 +1,350 @@
+#!/bin/sh
+
+test_description='basic rebase topology tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+# a---b---c
+# \
+# d---e
+test_expect_success 'setup' '
+ test_commit a &&
+ test_commit b &&
+ test_commit c &&
+ git checkout b &&
+ test_commit d &&
+ test_commit e
+'
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "simple rebase $*" "
+ reset_rebase &&
+ git rebase $* c e &&
+ test_cmp_rev c HEAD~2 &&
+ test_linear_range 'd e' c..
+ "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* is no-op if upstream is an ancestor" "
+ reset_rebase &&
+ git rebase $* b e &&
+ test_cmp_rev e HEAD
+ "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* -f rewrites even if upstream is an ancestor" "
+ reset_rebase &&
+ git rebase $* -f b e &&
+ ! test_cmp_rev e HEAD &&
+ test_cmp_rev b HEAD~2 &&
+ test_linear_range 'd e' b..
+ "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* fast-forwards from ancestor of upstream" "
+ reset_rebase &&
+ git rebase $* e b &&
+ test_cmp_rev e HEAD
+ "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+# f
+# /
+# a---b---c---g---h
+# \
+# d---gp--i
+#
+# gp = cherry-picked g
+# h = reverted g
+#
+# Reverted patches are there for tests to be able to check if a commit
+# that introduced the same change as another commit is
+# dropped. Without reverted commits, we could get false positives
+# because applying the patch succeeds, but simply results in no
+# changes.
+test_expect_success 'setup of linear history for range selection tests' '
+ git checkout c &&
+ test_commit g &&
+ revert h g &&
+ git checkout d &&
+ cherry_pick gp g &&
+ test_commit i &&
+ git checkout b &&
+ test_commit f
+'
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* drops patches in upstream" "
+ reset_rebase &&
+ git rebase $* h i &&
+ test_cmp_rev h HEAD~2 &&
+ test_linear_range 'd i' h..
+ "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* can drop last patch if in upstream" "
+ reset_rebase &&
+ git rebase $* h gp &&
+ test_cmp_rev h HEAD^ &&
+ test_linear_range 'd' h..
+ "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* --onto drops patches in upstream" "
+ reset_rebase &&
+ git rebase $* --onto f h i &&
+ test_cmp_rev f HEAD~2 &&
+ test_linear_range 'd i' f..
+ "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* --onto does not drop patches in onto" "
+ reset_rebase &&
+ git rebase $* --onto h f i &&
+ test_cmp_rev h HEAD~3 &&
+ test_linear_range 'd gp i' h..
+ "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+# a---b---c---j!
+# \
+# d---k!--l
+#
+# ! = empty
+test_expect_success 'setup of linear history for empty commit tests' '
+ git checkout c &&
+ make_empty j &&
+ git checkout d &&
+ make_empty k &&
+ test_commit l
+'
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* drops empty commit" "
+ reset_rebase &&
+ git rebase $* c l &&
+ test_cmp_rev c HEAD~2 &&
+ test_linear_range 'd l' c..
+ "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* --keep-empty" "
+ reset_rebase &&
+ git rebase $* --keep-empty c l &&
+ test_cmp_rev c HEAD~3 &&
+ test_linear_range 'd k l' c..
+ "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* --keep-empty keeps empty even if already in upstream" "
+ reset_rebase &&
+ git rebase $* --keep-empty j l &&
+ test_cmp_rev j HEAD~3 &&
+ test_linear_range 'd k l' j..
+ "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase failure -i
+test_run_rebase failure -p
+
+# m
+# /
+# a---b---c---g
+#
+# x---y---bp
+#
+# bp = cherry-picked b
+# m = reverted b
+#
+# Reverted patches are there for tests to be able to check if a commit
+# that introduced the same change as another commit is
+# dropped. Without reverted commits, we could get false positives
+# because applying the patch succeeds, but simply results in no
+# changes.
+test_expect_success 'setup of linear history for test involving root' '
+ git checkout b &&
+ revert m b &&
+ git checkout --orphan disjoint &&
+ git rm -rf . &&
+ test_commit x &&
+ test_commit y &&
+ cherry_pick bp b
+'
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* --onto --root" "
+ reset_rebase &&
+ git rebase $* --onto c --root y &&
+ test_cmp_rev c HEAD~2 &&
+ test_linear_range 'x y' c..
+ "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* without --onto --root with disjoint history" "
+ reset_rebase &&
+ git rebase $* c y &&
+ test_cmp_rev c HEAD~2 &&
+ test_linear_range 'x y' c..
+ "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* --onto --root drops patch in onto" "
+ reset_rebase &&
+ git rebase $* --onto m --root bp &&
+ test_cmp_rev m HEAD~2 &&
+ test_linear_range 'x y' m..
+ "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* --onto --root with merge-base does not go to root" "
+ reset_rebase &&
+ git rebase $* --onto m --root g &&
+ test_cmp_rev m HEAD~2 &&
+ test_linear_range 'c g' m..
+ "
+}
+
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* without --onto --root with disjoint history drops patch in onto" "
+ reset_rebase &&
+ git rebase $* m bp &&
+ test_cmp_rev m HEAD~2 &&
+ test_linear_range 'x y' m..
+ "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* --root on linear history is a no-op" "
+ reset_rebase &&
+ git rebase $* --root c &&
+ test_cmp_rev c HEAD
+ "
+}
+test_run_rebase failure ''
+test_run_rebase failure -m
+test_run_rebase failure -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* -f --root on linear history causes re-write" "
+ reset_rebase &&
+ git rebase $* -f --root c &&
+ ! test_cmp_rev a HEAD~2 &&
+ test_linear_range 'a b c' HEAD
+ "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_done
diff --git a/t/t3425-rebase-topology-merges.sh b/t/t3425-rebase-topology-merges.sh
new file mode 100755
index 0000000..846f85c
--- /dev/null
+++ b/t/t3425-rebase-topology-merges.sh
@@ -0,0 +1,258 @@
+#!/bin/sh
+
+test_description='rebase topology tests with merges'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+test_revision_subjects () {
+ expected="$1"
+ shift
+ set -- $(git log --format=%s --no-walk=unsorted "$@")
+ test "$expected" = "$*"
+}
+
+# a---b-----------c
+# \ \
+# d-------e \
+# \ \ \
+# n---o---w---v
+# \
+# z
+test_expect_success 'setup of non-linear-history' '
+ test_commit a &&
+ test_commit b &&
+ test_commit c &&
+ git checkout b &&
+ test_commit d &&
+ test_commit e &&
+
+ git checkout c &&
+ test_commit g &&
+ revert h g &&
+ git checkout d &&
+ cherry_pick gp g &&
+ test_commit i &&
+ git checkout b &&
+ test_commit f &&
+
+ git checkout d &&
+ test_commit n &&
+ test_commit o &&
+ test_merge w e &&
+ test_merge v c &&
+ git checkout o &&
+ test_commit z
+'
+
+test_run_rebase () {
+ result=$1
+ shift
+ test_expect_$result "rebase $* after merge from upstream" "
+ reset_rebase &&
+ git rebase $* e w &&
+ test_cmp_rev e HEAD~2 &&
+ test_linear_range 'n o' e..
+ "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+
+test_run_rebase () {
+ result=$1
+ shift
+ expected=$1
+ shift
+ test_expect_$result "rebase $* of non-linear history is linearized in place" "
+ reset_rebase &&
+ git rebase $* d w &&
+ test_cmp_rev d HEAD~3 &&
+ test_linear_range "\'"$expected"\'" d..
+ "
+}
+#TODO: make order consistent across all flavors of rebase
+test_run_rebase success 'e n o' ''
+test_run_rebase success 'e n o' -m
+test_run_rebase success 'n o e' -i
+
+test_run_rebase () {
+ result=$1
+ shift
+ expected=$1
+ shift
+ test_expect_$result "rebase $* of non-linear history is linearized upstream" "
+ reset_rebase &&
+ git rebase $* c w &&
+ test_cmp_rev c HEAD~4 &&
+ test_linear_range "\'"$expected"\'" c..
+ "
+}
+#TODO: make order consistent across all flavors of rebase
+test_run_rebase success 'd e n o' ''
+test_run_rebase success 'd e n o' -m
+test_run_rebase success 'd n o e' -i
+
+test_run_rebase () {
+ result=$1
+ shift
+ expected=$1
+ shift
+ test_expect_$result "rebase $* of non-linear history with merges after upstream merge is linearized" "
+ reset_rebase &&
+ git rebase $* c v &&
+ test_cmp_rev c HEAD~4 &&
+ test_linear_range "\'"$expected"\'" c..
+ "
+}
+#TODO: make order consistent across all flavors of rebase
+test_run_rebase success 'd e n o' ''
+test_run_rebase success 'd e n o' -m
+test_run_rebase success 'd n o e' -i
+
+test_expect_success "rebase -p is no-op in non-linear history" "
+ reset_rebase &&
+ git rebase -p d w &&
+ test_cmp_rev w HEAD
+"
+
+test_expect_success "rebase -p is no-op when base inside second parent" "
+ reset_rebase &&
+ git rebase -p e w &&
+ test_cmp_rev w HEAD
+"
+
+test_expect_failure "rebase -p --root on non-linear history is a no-op" "
+ reset_rebase &&
+ git rebase -p --root w &&
+ test_cmp_rev w HEAD
+"
+
+test_expect_success "rebase -p re-creates merge from side branch" "
+ reset_rebase &&
+ git rebase -p z w &&
+ test_cmp_rev z HEAD^ &&
+ test_cmp_rev w^2 HEAD^2
+"
+
+test_expect_success "rebase -p re-creates internal merge" "
+ reset_rebase &&
+ git rebase -p c w &&
+ test_cmp_rev c HEAD~4 &&
+ test_cmp_rev HEAD^2^ HEAD~3 &&
+ test_revision_subjects 'd n e o w' HEAD~3 HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+test_expect_success "rebase -p can re-create two branches on onto" "
+ reset_rebase &&
+ git rebase -p --onto c d w &&
+ test_cmp_rev c HEAD~3 &&
+ test_cmp_rev c HEAD^2^ &&
+ test_revision_subjects 'n e o w' HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+# f
+# /
+# a---b---c---g---h
+# \
+# d---gp--i
+# \ \
+# e-------u
+#
+# gp = cherry-picked g
+# h = reverted g
+test_expect_success 'setup of non-linear-history for patch-equivalence tests' '
+ git checkout e &&
+ test_merge u i
+'
+
+test_expect_success "rebase -p re-creates history around dropped commit matching upstream" "
+ reset_rebase &&
+ git rebase -p h u &&
+ test_cmp_rev h HEAD~3 &&
+ test_cmp_rev HEAD^2^ HEAD~2 &&
+ test_revision_subjects 'd i e u' HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+test_expect_success "rebase -p --onto in merged history drops patches in upstream" "
+ reset_rebase &&
+ git rebase -p --onto f h u &&
+ test_cmp_rev f HEAD~3 &&
+ test_cmp_rev HEAD^2^ HEAD~2 &&
+ test_revision_subjects 'd i e u' HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+test_expect_success "rebase -p --onto in merged history does not drop patches in onto" "
+ reset_rebase &&
+ git rebase -p --onto h f u &&
+ test_cmp_rev h HEAD~3 &&
+ test_cmp_rev HEAD^2~2 HEAD~2 &&
+ test_revision_subjects 'd gp i e u' HEAD~2 HEAD^2^ HEAD^2 HEAD^ HEAD
+"
+
+# a---b---c---g---h
+# \
+# d---gp--s
+# \ \ /
+# \ X
+# \ / \
+# e---t
+#
+# gp = cherry-picked g
+# h = reverted g
+test_expect_success 'setup of non-linear-history for dropping whole side' '
+ git checkout gp &&
+ test_merge s e &&
+ git checkout e &&
+ test_merge t gp
+'
+
+test_expect_failure "rebase -p drops merge commit when entire first-parent side is dropped" "
+ reset_rebase &&
+ git rebase -p h s &&
+ test_cmp_rev h HEAD~2 &&
+ test_linear_range 'd e' h..
+"
+
+test_expect_success "rebase -p drops merge commit when entire second-parent side is dropped" "
+ reset_rebase &&
+ git rebase -p h t &&
+ test_cmp_rev h HEAD~2 &&
+ test_linear_range 'd e' h..
+"
+
+# a---b---c
+# \
+# d---e
+# \ \
+# n---r
+# \
+# o
+#
+# r = tree-same with n
+test_expect_success 'setup of non-linear-history for empty commits' '
+ git checkout n &&
+ git merge --no-commit e &&
+ git reset n . &&
+ git commit -m r &&
+ git reset --hard &&
+ git clean -f &&
+ git tag r
+'
+
+test_expect_success "rebase -p re-creates empty internal merge commit" "
+ reset_rebase &&
+ git rebase -p c r &&
+ test_cmp_rev c HEAD~3 &&
+ test_cmp_rev HEAD^2^ HEAD~2 &&
+ test_revision_subjects 'd e n r' HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+test_expect_success "rebase -p re-creates empty merge commit" "
+ reset_rebase &&
+ git rebase -p o r &&
+ test_cmp_rev e HEAD^2 &&
+ test_cmp_rev o HEAD^ &&
+ test_revision_subjects 'r' HEAD
+"
+
+test_done
diff --git a/t/t3426-rebase-submodule.sh b/t/t3426-rebase-submodule.sh
new file mode 100755
index 0000000..d5b896d
--- /dev/null
+++ b/t/t3426-rebase-submodule.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='rebase can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+git_rebase () {
+ git status -su >expect &&
+ ls -1pR * >>expect &&
+ git checkout -b ours HEAD &&
+ echo x >>file1 &&
+ git add file1 &&
+ git commit -m add_x &&
+ git revert HEAD &&
+ git status -su >actual &&
+ ls -1pR * >>actual &&
+ test_cmp expect actual &&
+ git rebase "$1"
+}
+
+test_submodule_switch "git_rebase"
+
+git_rebase_interactive () {
+ git status -su >expect &&
+ ls -1pR * >>expect &&
+ git checkout -b ours HEAD &&
+ echo x >>file1 &&
+ git add file1 &&
+ git commit -m add_x &&
+ git revert HEAD &&
+ git status -su >actual &&
+ ls -1pR * >>actual &&
+ test_cmp expect actual &&
+ set_fake_editor &&
+ echo "fake-editor.sh" >.git/info/exclude &&
+ git rebase -i "$1"
+}
+
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+# The real reason "replace directory with submodule" fails is because a
+# directory "sub1" exists, but we reuse the suppression added for merge here
+test_submodule_switch "git_rebase_interactive"
+
+test_done
diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh
new file mode 100755
index 0000000..3780877
--- /dev/null
+++ b/t/t3427-rebase-subtree.sh
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+test_description='git rebase tests for -Xsubtree
+
+This test runs git rebase and tests the subtree strategy.
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+commit_message() {
+ git log --pretty=format:%s -1 "$1"
+}
+
+test_expect_success 'setup' '
+ test_commit README &&
+ mkdir files &&
+ (
+ cd files &&
+ git init &&
+ test_commit master1 &&
+ test_commit master2 &&
+ test_commit master3
+ ) &&
+ git fetch files master &&
+ git branch files-master FETCH_HEAD &&
+ git read-tree --prefix=files_subtree files-master &&
+ git checkout -- files_subtree &&
+ tree=$(git write-tree) &&
+ head=$(git rev-parse HEAD) &&
+ rev=$(git rev-parse --verify files-master^0) &&
+ commit=$(git commit-tree -p $head -p $rev -m "Add subproject master" $tree) &&
+ git update-ref HEAD $commit &&
+ (
+ cd files_subtree &&
+ test_commit master4
+ ) &&
+ test_commit files_subtree/master5
+'
+
+# FAILURE: Does not preserve master4.
+test_expect_failure 'Rebase -Xsubtree --preserve-merges --onto commit 4' '
+ reset_rebase &&
+ git checkout -b rebase-preserve-merges-4 master &&
+ git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
+ git commit -m "Empty commit" --allow-empty &&
+ git rebase -Xsubtree=files_subtree --preserve-merges --onto files-master master &&
+ verbose test "$(commit_message HEAD~)" = "files_subtree/master4"
+'
+
+# FAILURE: Does not preserve master5.
+test_expect_failure 'Rebase -Xsubtree --preserve-merges --onto commit 5' '
+ reset_rebase &&
+ git checkout -b rebase-preserve-merges-5 master &&
+ git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
+ git commit -m "Empty commit" --allow-empty &&
+ git rebase -Xsubtree=files_subtree --preserve-merges --onto files-master master &&
+ verbose test "$(commit_message HEAD)" = "files_subtree/master5"
+'
+
+# FAILURE: Does not preserve master4.
+test_expect_failure 'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit 4' '
+ reset_rebase &&
+ git checkout -b rebase-keep-empty-4 master &&
+ git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
+ git commit -m "Empty commit" --allow-empty &&
+ git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master &&
+ verbose test "$(commit_message HEAD~2)" = "files_subtree/master4"
+'
+
+# FAILURE: Does not preserve master5.
+test_expect_failure 'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit 5' '
+ reset_rebase &&
+ git checkout -b rebase-keep-empty-5 master &&
+ git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
+ git commit -m "Empty commit" --allow-empty &&
+ git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master &&
+ verbose test "$(commit_message HEAD~)" = "files_subtree/master5"
+'
+
+# FAILURE: Does not preserve Empty.
+test_expect_failure 'Rebase -Xsubtree --keep-empty --preserve-merges --onto empty commit' '
+ reset_rebase &&
+ git checkout -b rebase-keep-empty-empty master &&
+ git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
+ git commit -m "Empty commit" --allow-empty &&
+ git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master &&
+ verbose test "$(commit_message HEAD)" = "Empty commit"
+'
+
+# FAILURE: fatal: Could not parse object
+test_expect_failure 'Rebase -Xsubtree --onto commit 4' '
+ reset_rebase &&
+ git checkout -b rebase-onto-4 master &&
+ git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
+ git commit -m "Empty commit" --allow-empty &&
+ git rebase -Xsubtree=files_subtree --onto files-master master &&
+ verbose test "$(commit_message HEAD~2)" = "files_subtree/master4"
+'
+
+# FAILURE: fatal: Could not parse object
+test_expect_failure 'Rebase -Xsubtree --onto commit 5' '
+ reset_rebase &&
+ git checkout -b rebase-onto-5 master &&
+ git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
+ git commit -m "Empty commit" --allow-empty &&
+ git rebase -Xsubtree=files_subtree --onto files-master master &&
+ verbose test "$(commit_message HEAD~)" = "files_subtree/master5"
+'
+# FAILURE: fatal: Could not parse object
+test_expect_failure 'Rebase -Xsubtree --onto empty commit' '
+ reset_rebase &&
+ git checkout -b rebase-onto-empty master &&
+ git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
+ git commit -m "Empty commit" --allow-empty &&
+ git rebase -Xsubtree=files_subtree --onto files-master master &&
+ verbose test "$(commit_message HEAD)" = "Empty commit"
+'
+
+test_done
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index bc7aedd..51f3bbb 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -47,7 +47,7 @@ test_expect_success 'cherry-pick --nonsense' '
git diff --exit-code HEAD &&
test_must_fail git cherry-pick --nonsense 2>msg &&
git diff --exit-code HEAD "$pos" &&
- grep '[Uu]sage:' msg
+ test_i18ngrep '[Uu]sage:' msg
'
test_expect_success 'revert --nonsense' '
@@ -56,7 +56,7 @@ test_expect_success 'revert --nonsense' '
git diff --exit-code HEAD &&
test_must_fail git revert --nonsense 2>msg &&
git diff --exit-code HEAD "$pos" &&
- grep '[Uu]sage:' msg
+ test_i18ngrep '[Uu]sage:' msg
'
test_expect_success 'cherry-pick after renaming branch' '
@@ -81,13 +81,64 @@ test_expect_success 'revert after renaming branch' '
'
+test_expect_success 'cherry-pick on stat-dirty working tree' '
+ git clone . copy &&
+ (
+ cd copy &&
+ git checkout initial &&
+ test-chmtime +40 oops &&
+ git cherry-pick added
+ )
+'
+
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_i18ngrep "Your local changes would be overwritten by " errors
+
+'
+
+test_expect_success 'cherry-pick on unborn branch' '
+ git checkout --orphan unborn &&
+ git rm --cached -r . &&
+ rm -rf * &&
+ git cherry-pick initial &&
+ git diff --quiet initial &&
+ ! test_cmp_rev initial HEAD
+'
+
+test_expect_success 'cherry-pick "-" to pick from previous branch' '
+ git checkout unborn &&
+ test_commit to-pick actual content &&
+ git checkout master &&
+ git cherry-pick - &&
+ echo content >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick "-" is meaningless without checkout' '
+ test_create_repo afresh &&
+ (
+ cd afresh &&
+ test_commit one &&
+ test_commit two &&
+ test_commit three &&
+ test_must_fail git cherry-pick -
+ )
+'
+test_expect_success 'cherry-pick "-" works with arguments' '
+ git checkout -b side-branch &&
+ test_commit change actual change &&
+ git checkout master &&
+ git cherry-pick -s - &&
+ echo "Signed-off-by: C O Mitter <committer@example.com>" >expect &&
+ git cat-file commit HEAD | grep ^Signed-off-by: >signoff &&
+ test_cmp expect signoff &&
+ echo change >expect &&
+ test_cmp expect actual
'
test_done
diff --git a/t/t3502-cherry-pick-merge.sh b/t/t3502-cherry-pick-merge.sh
index 0ab52da..e37547f 100755
--- a/t/t3502-cherry-pick-merge.sh
+++ b/t/t3502-cherry-pick-merge.sh
@@ -35,7 +35,7 @@ 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 &&
+ test_expect_code 128 git cherry-pick -m 1 b &&
git diff --exit-code a --
'
diff --git a/t/t3503-cherry-pick-root.sh b/t/t3503-cherry-pick-root.sh
index b0faa29..e27f39d 100755
--- a/t/t3503-cherry-pick-root.sh
+++ b/t/t3503-cherry-pick-root.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-test_description='test cherry-picking a root commit'
+test_description='test cherry-picking (and reverting) a root commit'
. ./test-lib.sh
@@ -16,14 +16,62 @@ test_expect_success setup '
echo second > file2 &&
git add file2 &&
test_tick &&
- git commit -m "second"
+ git commit -m "second" &&
+
+ git symbolic-ref HEAD refs/heads/third &&
+ rm .git/index file2 &&
+ echo third > file3 &&
+ git add file3 &&
+ test_tick &&
+ git commit -m "third"
'
test_expect_success 'cherry-pick a root commit' '
+ git checkout second^0 &&
git cherry-pick master &&
- test first = $(cat file1)
+ echo first >expect &&
+ test_cmp expect file1
+
+'
+
+test_expect_success 'revert a root commit' '
+
+ git revert master &&
+ test_path_is_missing file1
+
+'
+
+test_expect_success 'cherry-pick a root commit with an external strategy' '
+
+ git cherry-pick --strategy=resolve master &&
+ echo first >expect &&
+ test_cmp expect file1
+
+'
+
+test_expect_success 'revert a root commit with an external strategy' '
+
+ git revert --strategy=resolve master &&
+ test_path_is_missing file1
+
+'
+
+test_expect_success 'cherry-pick two root commits' '
+
+ echo first >expect.file1 &&
+ echo second >expect.file2 &&
+ echo third >expect.file3 &&
+
+ git checkout second^0 &&
+ git cherry-pick master third &&
+
+ test_cmp expect.file1 file1 &&
+ test_cmp expect.file2 file2 &&
+ test_cmp expect.file3 file3 &&
+ git rev-parse --verify HEAD^^ &&
+ test_must_fail git rev-parse --verify HEAD^^^
'
diff --git a/t/t3504-cherry-pick-rerere.sh b/t/t3504-cherry-pick-rerere.sh
index f7b3518..e6a6481 100755
--- a/t/t3504-cherry-pick-rerere.sh
+++ b/t/t3504-cherry-pick-rerere.sh
@@ -23,7 +23,7 @@ test_expect_success 'conflicting merge' '
test_expect_success 'fixup' '
echo foo-dev >foo &&
git add foo && test_tick && git commit -q -m 4 &&
- git reset --hard HEAD^
+ git reset --hard HEAD^ &&
echo foo-dev >expect
'
@@ -33,7 +33,7 @@ test_expect_success 'cherry-pick conflict' '
'
test_expect_success 'reconfigure' '
- git config rerere.enabled false
+ git config rerere.enabled false &&
git reset --hard
'
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index e51e505..fbdc47c 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -13,21 +13,89 @@ test_expect_success setup '
git checkout -b empty-branch &&
test_tick &&
+ git commit --allow-empty -m "empty" &&
+
+ echo third >> file1 &&
+ git add file1 &&
+ test_tick &&
+ git commit --allow-empty-message -m "" &&
+
+ git checkout master &&
+ git checkout -b empty-branch2 &&
+ test_tick &&
git commit --allow-empty -m "empty"
'
test_expect_success 'cherry-pick an empty commit' '
- git checkout master && {
- git cherry-pick empty-branch
- test "$?" = 1
- }
+ git checkout master &&
+ test_expect_code 1 git cherry-pick empty-branch^
'
test_expect_success 'index lockfile was removed' '
+ test ! -f .git/index.lock
+'
+test_expect_success 'cherry-pick a commit with an empty message' '
+ git checkout master &&
+ test_expect_code 1 git cherry-pick empty-branch
+'
+
+test_expect_success 'index lockfile was removed' '
test ! -f .git/index.lock
+'
+
+test_expect_success 'cherry-pick a commit with an empty message with --allow-empty-message' '
+ git checkout -f master &&
+ git cherry-pick --allow-empty-message empty-branch
+'
+
+test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' '
+ git checkout master &&
+ echo fourth >>file2 &&
+ git add file2 &&
+ git commit -m "fourth" &&
+ test_must_fail git cherry-pick empty-branch2
+'
+
+test_expect_success 'cherry pick an empty non-ff commit with --allow-empty' '
+ git checkout master &&
+ git cherry-pick --allow-empty empty-branch2
+'
+
+test_expect_success 'cherry pick with --keep-redundant-commits' '
+ git checkout master &&
+ git cherry-pick --keep-redundant-commits HEAD^
+'
+
+test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '
+ git checkout master &&
+ git branch fork &&
+ echo foo >file2 &&
+ git add file2 &&
+ test_tick &&
+ git commit -m "add file2 on master" &&
+
+ git checkout fork &&
+ echo foo >file2 &&
+ git add file2 &&
+ test_tick &&
+ git commit -m "add file2 on the side"
+'
+
+test_expect_success 'cherry-pick a no-op without --keep-redundant' '
+ git reset --hard &&
+ git checkout fork^0 &&
+ test_must_fail git cherry-pick master
+'
+test_expect_success 'cherry-pick a no-op with --keep-redundant' '
+ git reset --hard &&
+ git checkout fork^0 &&
+ git cherry-pick --keep-redundant-commits master &&
+ git show -s --format=%s >actual &&
+ echo "add file2 on master" >expect &&
+ test_cmp expect actual
'
test_done
diff --git a/t/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh
index e17ae71..fb889ac 100755
--- a/t/t3506-cherry-pick-ff.sh
+++ b/t/t3506-cherry-pick-ff.sh
@@ -95,4 +95,22 @@ test_expect_success 'cherry pick a merge relative to nonexistent parent with --f
test_must_fail git cherry-pick --ff -m 3 C
'
+test_expect_success 'cherry pick a root commit with --ff' '
+ git reset --hard first -- &&
+ git rm file1 &&
+ echo first >file2 &&
+ git add file2 &&
+ git commit --amend -m "file2" &&
+ git cherry-pick --ff first &&
+ test "$(git rev-parse --verify HEAD)" = "1df192cd8bc58a2b275d842cede4d221ad9000d1"
+'
+
+test_expect_success 'cherry-pick --ff on unborn branch' '
+ git checkout --orphan unborn &&
+ git rm --cached -r . &&
+ rm -rf * &&
+ git cherry-pick --ff first &&
+ test_cmp_rev first HEAD
+'
+
test_done
diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh
index e25cf80..7c5ad08 100755
--- a/t/t3507-cherry-pick-conflict.sh
+++ b/t/t3507-cherry-pick-conflict.sh
@@ -11,6 +11,12 @@ test_description='test cherry-pick and revert with conflicts
. ./test-lib.sh
+pristine_detach () {
+ git checkout -f "$1^0" &&
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x
+}
+
test_expect_success setup '
echo unrelated >unrelated &&
@@ -18,18 +24,13 @@ test_expect_success setup '
test_commit initial foo a &&
test_commit base foo b &&
test_commit picked foo c &&
+ test_commit --signoff picked-signed foo d &&
git config advice.detachedhead false
'
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 &&
+ pristine_detach initial &&
head=$(git rev-parse HEAD) &&
test_must_fail git cherry-pick picked &&
@@ -38,14 +39,126 @@ test_expect_success 'failed cherry-pick does not advance HEAD' '
test "$head" = "$newhead"
'
-test_expect_success 'failed cherry-pick produces dirty index' '
+test_expect_success 'advice from failed cherry-pick' "
+ pristine_detach initial &&
- git checkout -f initial^0 &&
- git read-tree -u --reset HEAD &&
- git clean -d -f -f -q -x &&
+ picked=\$(git rev-parse --short picked) &&
+ cat <<-EOF >expected &&
+ error: could not apply \$picked... picked
+ hint: after resolving the conflicts, mark the corrected paths
+ hint: with 'git add <paths>' or 'git rm <paths>'
+ hint: and commit the result with 'git commit'
+ EOF
+ test_must_fail git cherry-pick picked 2>actual &&
+
+ test_i18ncmp expected actual
+"
+
+test_expect_success 'advice from failed cherry-pick --no-commit' "
+ pristine_detach initial &&
+
+ picked=\$(git rev-parse --short picked) &&
+ cat <<-EOF >expected &&
+ error: could not apply \$picked... picked
+ hint: after resolving the conflicts, mark the corrected paths
+ hint: with 'git add <paths>' or 'git rm <paths>'
+ EOF
+ test_must_fail git cherry-pick --no-commit picked 2>actual &&
+
+ test_i18ncmp expected actual
+"
+
+test_expect_success 'failed cherry-pick sets CHERRY_PICK_HEAD' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick picked &&
+ test_cmp_rev picked CHERRY_PICK_HEAD
+'
+
+test_expect_success 'successful cherry-pick does not set CHERRY_PICK_HEAD' '
+ pristine_detach initial &&
+ git cherry-pick base &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success 'cherry-pick --no-commit does not set CHERRY_PICK_HEAD' '
+ pristine_detach initial &&
+ git cherry-pick --no-commit base &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success 'cherry-pick w/dirty tree does not set CHERRY_PICK_HEAD' '
+ pristine_detach initial &&
+ echo foo > foo &&
+ test_must_fail git cherry-pick base &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success \
+ 'cherry-pick --strategy=resolve w/dirty tree does not set CHERRY_PICK_HEAD' '
+ pristine_detach initial &&
+ echo foo > foo &&
+ test_must_fail git cherry-pick --strategy=resolve base &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success 'GIT_CHERRY_PICK_HELP suppresses CHERRY_PICK_HEAD' '
+ pristine_detach initial &&
+ (
+ GIT_CHERRY_PICK_HELP="and then do something else" &&
+ export GIT_CHERRY_PICK_HELP &&
+ test_must_fail git cherry-pick picked
+ ) &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
- git update-index --refresh &&
- git diff-index --exit-code HEAD &&
+test_expect_success 'git reset clears CHERRY_PICK_HEAD' '
+ pristine_detach initial &&
+
+ test_must_fail git cherry-pick picked &&
+ git reset &&
+
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success 'failed commit does not clear CHERRY_PICK_HEAD' '
+ pristine_detach initial &&
+
+ test_must_fail git cherry-pick picked &&
+ test_must_fail git commit &&
+
+ test_cmp_rev picked CHERRY_PICK_HEAD
+'
+
+test_expect_success 'cancelled commit does not clear CHERRY_PICK_HEAD' '
+ pristine_detach initial &&
+
+ test_must_fail git cherry-pick picked &&
+ echo resolved >foo &&
+ git add foo &&
+ git update-index --refresh -q &&
+ test_must_fail git diff-index --exit-code HEAD &&
+ (
+ GIT_EDITOR=false &&
+ export GIT_EDITOR &&
+ test_must_fail git commit
+ ) &&
+
+ test_cmp_rev picked CHERRY_PICK_HEAD
+'
+
+test_expect_success 'successful commit clears CHERRY_PICK_HEAD' '
+ pristine_detach initial &&
+
+ test_must_fail git cherry-pick picked &&
+ echo resolved >foo &&
+ git add foo &&
+ git commit &&
+
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success 'failed cherry-pick produces dirty index' '
+ pristine_detach initial &&
test_must_fail git cherry-pick picked &&
@@ -54,9 +167,7 @@ test_expect_success 'failed cherry-pick produces dirty index' '
'
test_expect_success 'failed cherry-pick registers participants in index' '
-
- git read-tree -u --reset HEAD &&
- git clean -d -f -f -q -x &&
+ pristine_detach initial &&
{
git checkout base -- foo &&
git ls-files --stage foo &&
@@ -70,10 +181,7 @@ test_expect_success 'failed cherry-pick registers participants in index' '
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 &&
+ git read-tree -u --reset HEAD &&
test_must_fail git cherry-pick picked &&
git ls-files --stage --unmerged > actual &&
@@ -82,10 +190,7 @@ test_expect_success 'failed cherry-pick registers participants in index' '
'
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 &&
+ pristine_detach initial &&
cat <<-EOF > expected &&
<<<<<<< HEAD
a
@@ -94,9 +199,6 @@ test_expect_success 'failed cherry-pick describes conflict in work tree' '
>>>>>>> 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 &&
@@ -104,11 +206,8 @@ test_expect_success 'failed cherry-pick describes conflict in work tree' '
'
test_expect_success 'diff3 -m style' '
-
+ pristine_detach initial &&
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
@@ -119,9 +218,6 @@ test_expect_success 'diff3 -m style' '
>>>>>>> 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 &&
@@ -129,10 +225,8 @@ test_expect_success 'diff3 -m style' '
'
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 &&
+ pristine_detach initial &&
cat <<-EOF > expected &&
<<<<<<< HEAD
a
@@ -153,10 +247,7 @@ test_expect_success 'revert also handles conflicts sanely' '
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 &&
+ git read-tree -u --reset HEAD &&
head=$(git rev-parse HEAD) &&
test_must_fail git revert picked &&
@@ -171,11 +262,63 @@ test_expect_success 'revert also handles conflicts sanely' '
test_cmp expected actual
'
+test_expect_success 'failed revert sets REVERT_HEAD' '
+ pristine_detach initial &&
+ test_must_fail git revert picked &&
+ test_cmp_rev picked REVERT_HEAD
+'
+
+test_expect_success 'successful revert does not set REVERT_HEAD' '
+ pristine_detach base &&
+ git revert base &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+ test_must_fail git rev-parse --verify REVERT_HEAD
+'
+
+test_expect_success 'revert --no-commit sets REVERT_HEAD' '
+ pristine_detach base &&
+ git revert --no-commit base &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+ test_cmp_rev base REVERT_HEAD
+'
+
+test_expect_success 'revert w/dirty tree does not set REVERT_HEAD' '
+ pristine_detach base &&
+ echo foo > foo &&
+ test_must_fail git revert base &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+ test_must_fail git rev-parse --verify REVERT_HEAD
+'
+
+test_expect_success 'GIT_CHERRY_PICK_HELP does not suppress REVERT_HEAD' '
+ pristine_detach initial &&
+ (
+ GIT_CHERRY_PICK_HELP="and then do something else" &&
+ GIT_REVERT_HELP="and then do something else, again" &&
+ export GIT_CHERRY_PICK_HELP GIT_REVERT_HELP &&
+ test_must_fail git revert picked
+ ) &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+ test_cmp_rev picked REVERT_HEAD
+'
+
+test_expect_success 'git reset clears REVERT_HEAD' '
+ pristine_detach initial &&
+ test_must_fail git revert picked &&
+ git reset &&
+ test_must_fail git rev-parse --verify REVERT_HEAD
+'
+
+test_expect_success 'failed commit does not clear REVERT_HEAD' '
+ pristine_detach initial &&
+ test_must_fail git revert picked &&
+ test_must_fail git commit &&
+ test_cmp_rev picked REVERT_HEAD
+'
+
test_expect_success 'revert conflict, diff3 -m style' '
+ pristine_detach initial &&
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
@@ -186,13 +329,67 @@ test_expect_success 'revert conflict, diff3 -m style' '
>>>>>>> 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_expect_success 'failed cherry-pick does not forget -s' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick -s picked &&
+ test_i18ngrep -e "Signed-off-by" .git/MERGE_MSG
+'
+
+test_expect_success 'commit after failed cherry-pick does not add duplicated -s' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick -s picked-signed &&
+ git commit -a -s &&
+ test $(git show -s |grep -c "Signed-off-by") = 1
+'
+
+test_expect_success 'commit after failed cherry-pick adds -s at the right place' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick picked &&
+
+ git commit -a -s &&
+
+ # Do S-o-b and Conflicts appear in the right order?
+ cat <<-\EOF >expect &&
+ Signed-off-by: C O Mitter <committer@example.com>
+ # Conflicts:
+ EOF
+ grep -e "^# Conflicts:" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&
+ test_cmp expect actual &&
+
+ cat <<-\EOF >expected &&
+ picked
+
+ Signed-off-by: C O Mitter <committer@example.com>
+ EOF
+
+ git show -s --pretty=format:%B >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'commit --amend -s places the sign-off at the right place' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick picked &&
+
+ # emulate old-style conflicts block
+ mv .git/MERGE_MSG .git/MERGE_MSG+ &&
+ sed -e "/^# Conflicts:/,\$s/^# *//" <.git/MERGE_MSG+ >.git/MERGE_MSG &&
+
+ git commit -a &&
+ git commit --amend -s &&
+
+ # Do S-o-b and Conflicts appear in the right order?
+ cat <<-\EOF >expect &&
+ Signed-off-by: C O Mitter <committer@example.com>
+ Conflicts:
+ EOF
+ grep -e "^Conflicts:" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3508-cherry-pick-many-commits.sh b/t/t3508-cherry-pick-many-commits.sh
index f90ed3d..b457333 100755
--- a/t/t3508-cherry-pick-many-commits.sh
+++ b/t/t3508-cherry-pick-many-commits.sh
@@ -4,6 +4,14 @@ test_description='test cherry-picking many commits'
. ./test-lib.sh
+check_head_differs_from() {
+ ! test_cmp_rev HEAD "$1"
+}
+
+check_head_equals() {
+ test_cmp_rev HEAD "$1"
+}
+
test_expect_success setup '
echo first > file1 &&
git add file1 &&
@@ -29,7 +37,90 @@ test_expect_success 'cherry-pick first..fourth works' '
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)"
+ check_head_differs_from fourth
+'
+
+test_expect_success 'cherry-pick three one two works' '
+ git checkout -f first &&
+ test_commit one &&
+ test_commit two &&
+ test_commit three &&
+ git checkout -f master &&
+ git reset --hard first &&
+ git cherry-pick three one two &&
+ git diff --quiet three &&
+ git diff --quiet HEAD three &&
+ test "$(git log --reverse --format=%s first..)" = "three
+one
+two"
+'
+
+test_expect_success 'cherry-pick three one two: fails' '
+ git checkout -f master &&
+ git reset --hard first &&
+ test_must_fail git cherry-pick three one two:
+'
+
+test_expect_success 'output to keep user entertained during multi-pick' '
+ cat <<-\EOF >expected &&
+ [master OBJID] second
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:14:13 2005 -0700
+ 1 file changed, 1 insertion(+)
+ [master OBJID] third
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:15:13 2005 -0700
+ 1 file changed, 1 insertion(+)
+ [master OBJID] fourth
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:16:13 2005 -0700
+ 1 file changed, 1 insertion(+)
+ EOF
+
+ git checkout -f master &&
+ git reset --hard first &&
+ test_tick &&
+ git cherry-pick first..fourth >actual &&
+ sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy &&
+ test_line_count -ge 3 actual.fuzzy &&
+ test_i18ncmp expected actual.fuzzy
+'
+
+test_expect_success 'cherry-pick --strategy resolve first..fourth works' '
+ git checkout -f master &&
+ git reset --hard first &&
+ test_tick &&
+ git cherry-pick --strategy resolve first..fourth &&
+ git diff --quiet other &&
+ git diff --quiet HEAD other &&
+ check_head_differs_from fourth
+'
+
+test_expect_success 'output during multi-pick indicates merge strategy' '
+ cat <<-\EOF >expected &&
+ Trying simple merge.
+ [master OBJID] second
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:14:13 2005 -0700
+ 1 file changed, 1 insertion(+)
+ Trying simple merge.
+ [master OBJID] third
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:15:13 2005 -0700
+ 1 file changed, 1 insertion(+)
+ Trying simple merge.
+ [master OBJID] fourth
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:16:13 2005 -0700
+ 1 file changed, 1 insertion(+)
+ EOF
+
+ git checkout -f master &&
+ git reset --hard first &&
+ test_tick &&
+ git cherry-pick --strategy resolve first..fourth >actual &&
+ sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy &&
+ test_i18ncmp expected actual.fuzzy
'
test_expect_success 'cherry-pick --ff first..fourth works' '
@@ -39,7 +130,7 @@ test_expect_success 'cherry-pick --ff first..fourth works' '
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)"
+ check_head_equals fourth
'
test_expect_success 'cherry-pick -n first..fourth works' '
@@ -89,7 +180,7 @@ test_expect_success 'cherry-pick -3 fourth works' '
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)"
+ check_head_differs_from fourth
'
test_expect_success 'cherry-pick --stdin works' '
@@ -99,7 +190,7 @@ test_expect_success 'cherry-pick --stdin works' '
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)"
+ check_head_differs_from fourth
'
test_done
diff --git a/t/t3509-cherry-pick-merge-df.sh b/t/t3509-cherry-pick-merge-df.sh
new file mode 100755
index 0000000..1e5b394
--- /dev/null
+++ b/t/t3509-cherry-pick-merge-df.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+test_description='Test cherry-pick with directory/file conflicts'
+. ./test-lib.sh
+
+test_expect_success 'Initialize repository' '
+ mkdir a &&
+ >a/f &&
+ git add a &&
+ git commit -m a
+'
+
+test_expect_success 'Setup rename across paths each below D/F conflicts' '
+ mkdir b &&
+ test_ln_s_add ../a b/a &&
+ git commit -m b &&
+
+ git checkout -b branch &&
+ rm b/a &&
+ git mv a b/a &&
+ test_ln_s_add b/a a &&
+ git commit -m swap &&
+
+ >f1 &&
+ git add f1 &&
+ git commit -m f1
+'
+
+test_expect_success 'Cherry-pick succeeds with rename across D/F conflicts' '
+ git reset --hard &&
+ git checkout master^0 &&
+ git cherry-pick branch
+'
+
+test_expect_success 'Setup rename with file on one side matching directory name on other' '
+ git checkout --orphan nick-testcase &&
+ git rm -rf . &&
+
+ >empty &&
+ git add empty &&
+ git commit -m "Empty file" &&
+
+ git checkout -b simple &&
+ mv empty file &&
+ mkdir empty &&
+ mv file empty &&
+ git add empty/file &&
+ git commit -m "Empty file under empty dir" &&
+
+ echo content >newfile &&
+ git add newfile &&
+ git commit -m "New file"
+'
+
+test_expect_success 'Cherry-pick succeeds with was_a_dir/file -> was_a_dir (resolve)' '
+ git reset --hard &&
+ git checkout -q nick-testcase^0 &&
+ git cherry-pick --strategy=resolve simple
+'
+
+test_expect_success 'Cherry-pick succeeds with was_a_dir/file -> was_a_dir (recursive)' '
+ git reset --hard &&
+ git checkout -q nick-testcase^0 &&
+ git cherry-pick --strategy=recursive simple
+'
+
+test_expect_success 'Setup rename with file on one side matching different dirname on other' '
+ git reset --hard &&
+ git checkout --orphan mergeme &&
+ git rm -rf . &&
+
+ mkdir sub &&
+ mkdir othersub &&
+ echo content > sub/file &&
+ echo foo > othersub/whatever &&
+ git add -A &&
+ git commit -m "Common commit" &&
+
+ git rm -rf othersub &&
+ git mv sub/file othersub &&
+ git commit -m "Commit to merge" &&
+
+ git checkout -b newhead mergeme~1 &&
+ >independent-change &&
+ git add independent-change &&
+ git commit -m "Completely unrelated change"
+'
+
+test_expect_success 'Cherry-pick with rename to different D/F conflict succeeds (resolve)' '
+ git reset --hard &&
+ git checkout -q newhead^0 &&
+ git cherry-pick --strategy=resolve mergeme
+'
+
+test_expect_success 'Cherry-pick with rename to different D/F conflict succeeds (recursive)' '
+ git reset --hard &&
+ git checkout -q newhead^0 &&
+ git cherry-pick --strategy=recursive mergeme
+'
+
+test_done
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
new file mode 100755
index 0000000..7b7a89d
--- /dev/null
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -0,0 +1,514 @@
+#!/bin/sh
+
+test_description='Test cherry-pick continuation features
+
+ + conflicting: rewrites unrelated to conflicting
+ + yetanotherpick: rewrites foo to e
+ + anotherpick: rewrites foo to d
+ + picked: rewrites foo to c
+ + unrelatedpick: rewrites unrelated to reallyunrelated
+ + base: rewrites foo to b
+ + initial: writes foo as a, unrelated as unrelated
+
+'
+
+. ./test-lib.sh
+
+# Repeat first match 10 times
+_r10='\1\1\1\1\1\1\1\1\1\1'
+
+pristine_detach () {
+ git cherry-pick --quit &&
+ git checkout -f "$1^0" &&
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x
+}
+
+test_expect_success setup '
+ git config advice.detachedhead false &&
+ echo unrelated >unrelated &&
+ git add unrelated &&
+ test_commit initial foo a &&
+ test_commit base foo b &&
+ test_commit unrelatedpick unrelated reallyunrelated &&
+ test_commit picked foo c &&
+ test_commit anotherpick foo d &&
+ test_commit yetanotherpick foo e &&
+ pristine_detach initial &&
+ test_commit conflicting unrelated
+'
+
+test_expect_success 'cherry-pick persists data on failure' '
+ pristine_detach initial &&
+ test_expect_code 1 git cherry-pick -s base..anotherpick &&
+ test_path_is_dir .git/sequencer &&
+ test_path_is_file .git/sequencer/head &&
+ test_path_is_file .git/sequencer/todo &&
+ test_path_is_file .git/sequencer/opts
+'
+
+test_expect_success 'cherry-pick mid-cherry-pick-sequence' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick base..anotherpick &&
+ test_cmp_rev picked CHERRY_PICK_HEAD &&
+ # "oops, I forgot that these patches rely on the change from base"
+ git checkout HEAD foo &&
+ git cherry-pick base &&
+ git cherry-pick picked &&
+ git cherry-pick --continue &&
+ git diff --exit-code anotherpick
+'
+
+test_expect_success 'cherry-pick persists opts correctly' '
+ pristine_detach initial &&
+ test_expect_code 128 git cherry-pick -s -m 1 --strategy=recursive -X patience -X ours initial..anotherpick &&
+ test_path_is_dir .git/sequencer &&
+ test_path_is_file .git/sequencer/head &&
+ test_path_is_file .git/sequencer/todo &&
+ test_path_is_file .git/sequencer/opts &&
+ echo "true" >expect &&
+ git config --file=.git/sequencer/opts --get-all options.signoff >actual &&
+ test_cmp expect actual &&
+ echo "1" >expect &&
+ git config --file=.git/sequencer/opts --get-all options.mainline >actual &&
+ test_cmp expect actual &&
+ echo "recursive" >expect &&
+ git config --file=.git/sequencer/opts --get-all options.strategy >actual &&
+ test_cmp expect actual &&
+ cat >expect <<-\EOF &&
+ patience
+ ours
+ EOF
+ git config --file=.git/sequencer/opts --get-all options.strategy-option >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick cleans up sequencer state upon success' '
+ pristine_detach initial &&
+ git cherry-pick initial..picked &&
+ test_path_is_missing .git/sequencer
+'
+
+test_expect_success '--quit does not complain when no cherry-pick is in progress' '
+ pristine_detach initial &&
+ git cherry-pick --quit
+'
+
+test_expect_success '--abort requires cherry-pick in progress' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick --abort
+'
+
+test_expect_success '--quit cleans up sequencer state' '
+ pristine_detach initial &&
+ test_expect_code 1 git cherry-pick base..picked &&
+ git cherry-pick --quit &&
+ test_path_is_missing .git/sequencer
+'
+
+test_expect_success '--quit keeps HEAD and conflicted index intact' '
+ pristine_detach initial &&
+ cat >expect <<-\EOF &&
+ OBJID
+ :100644 100644 OBJID OBJID M unrelated
+ OBJID
+ :000000 100644 OBJID OBJID A foo
+ :000000 100644 OBJID OBJID A unrelated
+ EOF
+ test_expect_code 1 git cherry-pick base..picked &&
+ git cherry-pick --quit &&
+ test_path_is_missing .git/sequencer &&
+ test_must_fail git update-index --refresh &&
+ {
+ git rev-list HEAD |
+ git diff-tree --root --stdin |
+ sed "s/$_x40/OBJID/g"
+ } >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--abort to cancel multiple cherry-pick' '
+ pristine_detach initial &&
+ test_expect_code 1 git cherry-pick base..anotherpick &&
+ git cherry-pick --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev initial HEAD &&
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD
+'
+
+test_expect_success '--abort to cancel single cherry-pick' '
+ pristine_detach initial &&
+ test_expect_code 1 git cherry-pick picked &&
+ git cherry-pick --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev initial HEAD &&
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD
+'
+
+test_expect_success 'cherry-pick --abort to cancel multiple revert' '
+ pristine_detach anotherpick &&
+ test_expect_code 1 git revert base..picked &&
+ git cherry-pick --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev anotherpick HEAD &&
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD
+'
+
+test_expect_success 'revert --abort works, too' '
+ pristine_detach anotherpick &&
+ test_expect_code 1 git revert base..picked &&
+ git revert --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev anotherpick HEAD
+'
+
+test_expect_success '--abort to cancel single revert' '
+ pristine_detach anotherpick &&
+ test_expect_code 1 git revert picked &&
+ git revert --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev anotherpick HEAD &&
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD
+'
+
+test_expect_success '--abort keeps unrelated change, easy case' '
+ pristine_detach unrelatedpick &&
+ echo changed >expect &&
+ test_expect_code 1 git cherry-pick picked..yetanotherpick &&
+ echo changed >unrelated &&
+ git cherry-pick --abort &&
+ test_cmp expect unrelated
+'
+
+test_expect_success '--abort refuses to clobber unrelated change, harder case' '
+ pristine_detach initial &&
+ echo changed >expect &&
+ test_expect_code 1 git cherry-pick base..anotherpick &&
+ echo changed >unrelated &&
+ test_must_fail git cherry-pick --abort &&
+ test_cmp expect unrelated &&
+ git rev-list HEAD >log &&
+ test_line_count = 2 log &&
+ test_must_fail git update-index --refresh &&
+
+ git checkout unrelated &&
+ git cherry-pick --abort &&
+ test_cmp_rev initial HEAD
+'
+
+test_expect_success 'cherry-pick still writes sequencer state when one commit is left' '
+ pristine_detach initial &&
+ test_expect_code 1 git cherry-pick base..picked &&
+ test_path_is_dir .git/sequencer &&
+ echo "resolved" >foo &&
+ git add foo &&
+ git commit &&
+ {
+ git rev-list HEAD |
+ git diff-tree --root --stdin |
+ sed "s/$_x40/OBJID/g"
+ } >actual &&
+ cat >expect <<-\EOF &&
+ OBJID
+ :100644 100644 OBJID OBJID M foo
+ OBJID
+ :100644 100644 OBJID OBJID M unrelated
+ OBJID
+ :000000 100644 OBJID OBJID A foo
+ :000000 100644 OBJID OBJID A unrelated
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success '--abort after last commit in sequence' '
+ pristine_detach initial &&
+ test_expect_code 1 git cherry-pick base..picked &&
+ git cherry-pick --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev initial HEAD &&
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD
+'
+
+test_expect_success 'cherry-pick does not implicitly stomp an existing operation' '
+ pristine_detach initial &&
+ test_expect_code 1 git cherry-pick base..anotherpick &&
+ test-chmtime -v +0 .git/sequencer >expect &&
+ test_expect_code 128 git cherry-pick unrelatedpick &&
+ test-chmtime -v +0 .git/sequencer >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--continue complains when no cherry-pick is in progress' '
+ pristine_detach initial &&
+ test_expect_code 128 git cherry-pick --continue
+'
+
+test_expect_success '--continue complains when there are unresolved conflicts' '
+ pristine_detach initial &&
+ test_expect_code 1 git cherry-pick base..anotherpick &&
+ test_expect_code 128 git cherry-pick --continue
+'
+
+test_expect_success '--continue of single cherry-pick' '
+ pristine_detach initial &&
+ echo c >expect &&
+ test_must_fail git cherry-pick picked &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+
+ test_cmp expect foo &&
+ test_cmp_rev initial HEAD^ &&
+ git diff --exit-code HEAD &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success '--continue of single revert' '
+ pristine_detach initial &&
+ echo resolved >expect &&
+ echo "Revert \"picked\"" >expect.msg &&
+ test_must_fail git revert picked &&
+ echo resolved >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+
+ git diff --exit-code HEAD &&
+ test_cmp expect foo &&
+ test_cmp_rev initial HEAD^ &&
+ git diff-tree -s --pretty=tformat:%s HEAD >msg &&
+ test_cmp expect.msg msg &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+ test_must_fail git rev-parse --verify REVERT_HEAD
+'
+
+test_expect_success '--continue after resolving conflicts' '
+ pristine_detach initial &&
+ echo d >expect &&
+ cat >expect.log <<-\EOF &&
+ OBJID
+ :100644 100644 OBJID OBJID M foo
+ OBJID
+ :100644 100644 OBJID OBJID M foo
+ OBJID
+ :100644 100644 OBJID OBJID M unrelated
+ OBJID
+ :000000 100644 OBJID OBJID A foo
+ :000000 100644 OBJID OBJID A unrelated
+ EOF
+ test_must_fail git cherry-pick base..anotherpick &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+ {
+ git rev-list HEAD |
+ git diff-tree --root --stdin |
+ sed "s/$_x40/OBJID/g"
+ } >actual.log &&
+ test_cmp expect foo &&
+ test_cmp expect.log actual.log
+'
+
+test_expect_success '--continue after resolving conflicts and committing' '
+ pristine_detach initial &&
+ test_expect_code 1 git cherry-pick base..anotherpick &&
+ echo "c" >foo &&
+ git add foo &&
+ git commit &&
+ git cherry-pick --continue &&
+ test_path_is_missing .git/sequencer &&
+ {
+ git rev-list HEAD |
+ git diff-tree --root --stdin |
+ sed "s/$_x40/OBJID/g"
+ } >actual &&
+ cat >expect <<-\EOF &&
+ OBJID
+ :100644 100644 OBJID OBJID M foo
+ OBJID
+ :100644 100644 OBJID OBJID M foo
+ OBJID
+ :100644 100644 OBJID OBJID M unrelated
+ OBJID
+ :000000 100644 OBJID OBJID A foo
+ :000000 100644 OBJID OBJID A unrelated
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success '--continue asks for help after resolving patch to nil' '
+ pristine_detach conflicting &&
+ test_must_fail git cherry-pick initial..picked &&
+
+ test_cmp_rev unrelatedpick CHERRY_PICK_HEAD &&
+ git checkout HEAD -- unrelated &&
+ test_must_fail git cherry-pick --continue 2>msg &&
+ test_i18ngrep "The previous cherry-pick is now empty" msg
+'
+
+test_expect_success 'follow advice and skip nil patch' '
+ pristine_detach conflicting &&
+ test_must_fail git cherry-pick initial..picked &&
+
+ git checkout HEAD -- unrelated &&
+ test_must_fail git cherry-pick --continue &&
+ git reset &&
+ git cherry-pick --continue &&
+
+ git rev-list initial..HEAD >commits &&
+ test_line_count = 3 commits
+'
+
+test_expect_success '--continue respects opts' '
+ pristine_detach initial &&
+ test_expect_code 1 git cherry-pick -x base..anotherpick &&
+ echo "c" >foo &&
+ git add foo &&
+ git commit &&
+ git cherry-pick --continue &&
+ test_path_is_missing .git/sequencer &&
+ git cat-file commit HEAD >anotherpick_msg &&
+ git cat-file commit HEAD~1 >picked_msg &&
+ git cat-file commit HEAD~2 >unrelatedpick_msg &&
+ git cat-file commit HEAD~3 >initial_msg &&
+ test_must_fail grep "cherry picked from" initial_msg &&
+ grep "cherry picked from" unrelatedpick_msg &&
+ grep "cherry picked from" picked_msg &&
+ grep "cherry picked from" anotherpick_msg
+'
+
+test_expect_success '--continue of single-pick respects -x' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick -x picked &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+ test_path_is_missing .git/sequencer &&
+ git cat-file commit HEAD >msg &&
+ grep "cherry picked from" msg
+'
+
+test_expect_success '--continue respects -x in first commit in multi-pick' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick -x picked anotherpick &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+ test_path_is_missing .git/sequencer &&
+ git cat-file commit HEAD^ >msg &&
+ picked=$(git rev-parse --verify picked) &&
+ grep "cherry picked from.*$picked" msg
+'
+
+test_expect_failure '--signoff is automatically propagated to resolved conflict' '
+ pristine_detach initial &&
+ test_expect_code 1 git cherry-pick --signoff base..anotherpick &&
+ echo "c" >foo &&
+ git add foo &&
+ git commit &&
+ git cherry-pick --continue &&
+ test_path_is_missing .git/sequencer &&
+ git cat-file commit HEAD >anotherpick_msg &&
+ git cat-file commit HEAD~1 >picked_msg &&
+ git cat-file commit HEAD~2 >unrelatedpick_msg &&
+ git cat-file commit HEAD~3 >initial_msg &&
+ test_must_fail grep "Signed-off-by:" initial_msg &&
+ grep "Signed-off-by:" unrelatedpick_msg &&
+ test_must_fail grep "Signed-off-by:" picked_msg &&
+ grep "Signed-off-by:" anotherpick_msg
+'
+
+test_expect_failure '--signoff dropped for implicit commit of resolution, multi-pick case' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick -s picked anotherpick &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+
+ git diff --exit-code HEAD &&
+ test_cmp_rev initial HEAD^^ &&
+ git cat-file commit HEAD^ >msg &&
+ ! grep Signed-off-by: msg
+'
+
+test_expect_failure 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick -s picked &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+
+ git diff --exit-code HEAD &&
+ test_cmp_rev initial HEAD^ &&
+ git cat-file commit HEAD >msg &&
+ ! grep Signed-off-by: msg
+'
+
+test_expect_success 'malformed instruction sheet 1' '
+ pristine_detach initial &&
+ test_expect_code 1 git cherry-pick base..anotherpick &&
+ echo "resolved" >foo &&
+ git add foo &&
+ git commit &&
+ sed "s/pick /pick/" .git/sequencer/todo >new_sheet &&
+ cp new_sheet .git/sequencer/todo &&
+ test_expect_code 128 git cherry-pick --continue
+'
+
+test_expect_success 'malformed instruction sheet 2' '
+ pristine_detach initial &&
+ test_expect_code 1 git cherry-pick base..anotherpick &&
+ echo "resolved" >foo &&
+ git add foo &&
+ git commit &&
+ sed "s/pick/revert/" .git/sequencer/todo >new_sheet &&
+ cp new_sheet .git/sequencer/todo &&
+ test_expect_code 128 git cherry-pick --continue
+'
+
+test_expect_success 'empty commit set' '
+ pristine_detach initial &&
+ test_expect_code 128 git cherry-pick base..base
+'
+
+test_expect_success 'malformed instruction sheet 3' '
+ pristine_detach initial &&
+ test_expect_code 1 git cherry-pick base..anotherpick &&
+ echo "resolved" >foo &&
+ git add foo &&
+ git commit &&
+ sed "s/pick \([0-9a-f]*\)/pick $_r10/" .git/sequencer/todo >new_sheet &&
+ cp new_sheet .git/sequencer/todo &&
+ test_expect_code 128 git cherry-pick --continue
+'
+
+test_expect_success 'instruction sheet, fat-fingers version' '
+ pristine_detach initial &&
+ test_expect_code 1 git cherry-pick base..anotherpick &&
+ echo "c" >foo &&
+ git add foo &&
+ git commit &&
+ sed "s/pick \([0-9a-f]*\)/pick \1 /" .git/sequencer/todo >new_sheet &&
+ cp new_sheet .git/sequencer/todo &&
+ git cherry-pick --continue
+'
+
+test_expect_success 'commit descriptions in insn sheet are optional' '
+ pristine_detach initial &&
+ test_expect_code 1 git cherry-pick base..anotherpick &&
+ echo "c" >foo &&
+ git add foo &&
+ git commit &&
+ cut -d" " -f1,2 .git/sequencer/todo >new_sheet &&
+ cp new_sheet .git/sequencer/todo &&
+ git cherry-pick --continue &&
+ test_path_is_missing .git/sequencer &&
+ git rev-list HEAD >commits &&
+ test_line_count = 4 commits
+'
+
+test_done
diff --git a/t/t3511-cherry-pick-x.sh b/t/t3511-cherry-pick-x.sh
new file mode 100755
index 0000000..9cce5ae
--- /dev/null
+++ b/t/t3511-cherry-pick-x.sh
@@ -0,0 +1,247 @@
+#!/bin/sh
+
+test_description='Test cherry-pick -x and -s'
+
+. ./test-lib.sh
+
+pristine_detach () {
+ git cherry-pick --quit &&
+ git checkout -f "$1^0" &&
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x
+}
+
+mesg_one_line='base: commit message'
+
+mesg_no_footer="$mesg_one_line
+
+OneWordBodyThatsNotA-S-o-B"
+
+mesg_with_footer="$mesg_no_footer
+
+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+Signed-off-by: A.U. Thor <author@example.com>
+Signed-off-by: B.U. Thor <buthor@example.com>"
+
+mesg_broken_footer="$mesg_no_footer
+
+The signed-off-by string should begin with the words Signed-off-by followed
+by a colon and space, and then the signers name and email address. e.g.
+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+
+mesg_with_footer_sob="$mesg_with_footer
+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+
+mesg_with_cherry_footer="$mesg_with_footer_sob
+(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)
+Tested-by: C.U. Thor <cuthor@example.com>"
+
+mesg_unclean="$mesg_one_line
+
+
+leading empty lines
+
+
+consecutive empty lines
+
+# hash tag comment
+
+trailing empty lines
+
+
+"
+
+test_expect_success setup '
+ git config advice.detachedhead false &&
+ echo unrelated >unrelated &&
+ git add unrelated &&
+ test_commit initial foo a &&
+ test_commit "$mesg_one_line" foo b mesg-one-line &&
+ git reset --hard initial &&
+ test_commit "$mesg_no_footer" foo b mesg-no-footer &&
+ git reset --hard initial &&
+ test_commit "$mesg_broken_footer" foo b mesg-broken-footer &&
+ git reset --hard initial &&
+ test_commit "$mesg_with_footer" foo b mesg-with-footer &&
+ git reset --hard initial &&
+ test_commit "$mesg_with_footer_sob" foo b mesg-with-footer-sob &&
+ git reset --hard initial &&
+ test_commit "$mesg_with_cherry_footer" foo b mesg-with-cherry-footer &&
+ git reset --hard initial &&
+ test_config commit.cleanup verbatim &&
+ test_commit "$mesg_unclean" foo b mesg-unclean &&
+ test_unconfig commit.cleanup &&
+ pristine_detach initial &&
+ test_commit conflicting unrelated
+'
+
+test_expect_success 'cherry-pick -x inserts blank line after one line subject' '
+ pristine_detach initial &&
+ sha1=$(git rev-parse mesg-one-line^0) &&
+ git cherry-pick -x mesg-one-line &&
+ cat <<-EOF >expect &&
+ $mesg_one_line
+
+ (cherry picked from commit $sha1)
+ EOF
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -s inserts blank line after one line subject' '
+ pristine_detach initial &&
+ git cherry-pick -s mesg-one-line &&
+ cat <<-EOF >expect &&
+ $mesg_one_line
+
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+ EOF
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -s inserts blank line after non-conforming footer' '
+ pristine_detach initial &&
+ git cherry-pick -s mesg-broken-footer &&
+ cat <<-EOF >expect &&
+ $mesg_broken_footer
+
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+ EOF
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -x inserts blank line when conforming footer not found' '
+ pristine_detach initial &&
+ sha1=$(git rev-parse mesg-no-footer^0) &&
+ git cherry-pick -x mesg-no-footer &&
+ cat <<-EOF >expect &&
+ $mesg_no_footer
+
+ (cherry picked from commit $sha1)
+ EOF
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -s inserts blank line when conforming footer not found' '
+ pristine_detach initial &&
+ git cherry-pick -s mesg-no-footer &&
+ cat <<-EOF >expect &&
+ $mesg_no_footer
+
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+ EOF
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -x -s inserts blank line when conforming footer not found' '
+ pristine_detach initial &&
+ sha1=$(git rev-parse mesg-no-footer^0) &&
+ git cherry-pick -x -s mesg-no-footer &&
+ cat <<-EOF >expect &&
+ $mesg_no_footer
+
+ (cherry picked from commit $sha1)
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+ EOF
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -s adds sob when last sob doesnt match committer' '
+ pristine_detach initial &&
+ git cherry-pick -s mesg-with-footer &&
+ cat <<-EOF >expect &&
+ $mesg_with_footer
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+ EOF
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -x -s adds sob when last sob doesnt match committer' '
+ pristine_detach initial &&
+ sha1=$(git rev-parse mesg-with-footer^0) &&
+ git cherry-pick -x -s mesg-with-footer &&
+ cat <<-EOF >expect &&
+ $mesg_with_footer
+ (cherry picked from commit $sha1)
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+ EOF
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -s refrains from adding duplicate trailing sob' '
+ pristine_detach initial &&
+ git cherry-pick -s mesg-with-footer-sob &&
+ cat <<-EOF >expect &&
+ $mesg_with_footer_sob
+ EOF
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists for committer' '
+ pristine_detach initial &&
+ sha1=$(git rev-parse mesg-with-footer-sob^0) &&
+ git cherry-pick -x -s mesg-with-footer-sob &&
+ cat <<-EOF >expect &&
+ $mesg_with_footer_sob
+ (cherry picked from commit $sha1)
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+ EOF
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -x treats "(cherry picked from..." line as part of footer' '
+ pristine_detach initial &&
+ sha1=$(git rev-parse mesg-with-cherry-footer^0) &&
+ git cherry-pick -x mesg-with-cherry-footer &&
+ cat <<-EOF >expect &&
+ $mesg_with_cherry_footer
+ (cherry picked from commit $sha1)
+ EOF
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -s treats "(cherry picked from..." line as part of footer' '
+ pristine_detach initial &&
+ git cherry-pick -s mesg-with-cherry-footer &&
+ cat <<-EOF >expect &&
+ $mesg_with_cherry_footer
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+ EOF
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -x -s treats "(cherry picked from..." line as part of footer' '
+ pristine_detach initial &&
+ sha1=$(git rev-parse mesg-with-cherry-footer^0) &&
+ git cherry-pick -x -s mesg-with-cherry-footer &&
+ cat <<-EOF >expect &&
+ $mesg_with_cherry_footer
+ (cherry picked from commit $sha1)
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+ EOF
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick preserves commit message' '
+ pristine_detach initial &&
+ printf "$mesg_unclean" >expect &&
+ git log -1 --pretty=format:%B mesg-unclean >actual &&
+ test_cmp expect actual &&
+ git cherry-pick mesg-unclean &&
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh
new file mode 100755
index 0000000..6863b7b
--- /dev/null
+++ b/t/t3512-cherry-pick-submodule.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+test_description='cherry-pick can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+test_submodule_switch "git cherry-pick"
+
+test_done
diff --git a/t/t3513-revert-submodule.sh b/t/t3513-revert-submodule.sh
new file mode 100755
index 0000000..db93781
--- /dev/null
+++ b/t/t3513-revert-submodule.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='revert can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+# Create a revert that moves from HEAD (including any test modifications to
+# the work tree) to $1 by first checking out $1 and reverting it. Reverting
+# the revert is the transition we test for. We tar the current work tree
+# first so we can restore the work tree test setup after doing the checkout
+# and revert. We test here that the restored work tree content is identical
+# to that at the beginning. The last revert is then tested by the framework.
+git_revert () {
+ git status -su >expect &&
+ ls -1pR * >>expect &&
+ tar cf "$TRASH_DIRECTORY/tmp.tar" * &&
+ git checkout "$1" &&
+ git revert HEAD &&
+ rm -rf * &&
+ tar xf "$TRASH_DIRECTORY/tmp.tar" &&
+ git status -su >actual &&
+ ls -1pR * >>actual &&
+ test_cmp expect actual &&
+ git revert HEAD
+}
+
+KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+test_submodule_switch "git_revert"
+
+test_done
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index b514cbb..d046d98 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -14,7 +14,7 @@ test_expect_success \
git add -- foo bar baz 'space embedded' -q &&
git commit -m 'add normal files'"
-if touch -- 'tab embedded' 'newline
+if test_have_prereq !MINGW && touch -- 'tab embedded' 'newline
embedded' 2>/dev/null
then
test_set_prereq FUNNYNAMES
@@ -28,22 +28,6 @@ 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'
@@ -54,37 +38,37 @@ test_expect_success \
test_expect_success \
'Test that git rm --cached foo succeeds if the index matches the file' \
- 'echo content > foo
- git add foo
+ '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
+ '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
+ echo content >foo &&
+ git add foo &&
+ git commit -m foo --allow-empty &&
+ 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
+ 'echo content >foo &&
+ git add foo &&
+ git commit -m foo --allow-empty &&
+ echo "other content" >foo &&
+ git add foo &&
+ echo "yet another content" >foo &&
git rm --cached -f foo'
test_expect_success \
@@ -112,7 +96,7 @@ test_expect_success FUNNYNAMES \
"git rm -f 'space embedded' 'tab embedded' 'newline
embedded'"
-test_expect_success RO_DIR 'Test that "git rm -f" fails if its rm fails' '
+test_expect_success SANITY 'Test that "git rm -f" fails if its rm fails' '
chmod a-w . &&
test_must_fail git rm -f baz &&
chmod 775 .
@@ -131,7 +115,7 @@ test_expect_success '"rm" command printed' '
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 &&
+ test $(grep "^rm " rm-output | wc -l) = 1 &&
rm -f test-file rm-output &&
git commit -m "remove file from rm test"
'
@@ -141,7 +125,7 @@ test_expect_success '"rm" command suppressed with --quiet' '
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 &&
+ test $(wc -l < rm-output) = 0 &&
rm -f test-file rm-output &&
git commit -m "remove file from rm --quiet test"
'
@@ -186,7 +170,7 @@ 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 foo &&
test_must_fail git ls-files --error-unmatch baz
'
@@ -199,7 +183,7 @@ test_expect_success 'refuse to remove cached empty file with modifications' '
test_expect_success 'remove intent-to-add file without --force' '
echo content >intent-to-add &&
- git add -N intent-to-add
+ git add -N intent-to-add &&
git rm --cached intent-to-add
'
@@ -217,7 +201,7 @@ test_expect_success 'Recursive without -r fails' '
'
test_expect_success 'Recursive with -r but dirty' '
- echo qfwfq >>frotz/nitfol
+ echo qfwfq >>frotz/nitfol &&
test_must_fail git rm -r frotz &&
test -d frotz &&
test -f frotz/nitfol
@@ -256,19 +240,15 @@ test_expect_success 'refresh index before checking if it is up-to-date' '
test_expect_success 'choking "git rm" should not let it die with cruft' '
git reset -q --hard &&
- H=0000000000000000000000000000000000000000 &&
+ test_when_finished "rm -f .git/index.lock && git reset -q --hard" &&
i=0 &&
while test $i -lt 12000
do
- echo "100644 $H 0 some-file-$i"
+ echo "100644 1234567890123456789012345678901234567890 0 some-file-$i"
i=$(( $i + 1 ))
done | git update-index --index-info &&
- git rm -n "some-file-*" | :;
- test -f .git/index.lock
- status=$?
- rm -f .git/index.lock
- git reset -q --hard
- test "$status" != 0
+ git rm -n "some-file-*" | : &&
+ test_path_is_missing .git/index.lock
'
test_expect_success 'rm removes subdirectories recursively' '
@@ -279,4 +259,626 @@ test_expect_success 'rm removes subdirectories recursively' '
! test -d dir
'
+cat >expect <<EOF
+M .gitmodules
+D submod
+EOF
+
+cat >expect.modified <<EOF
+ M submod
+EOF
+
+cat >expect.cached <<EOF
+D submod
+EOF
+
+cat >expect.both_deleted<<EOF
+D .gitmodules
+D submod
+EOF
+
+test_expect_success 'rm removes empty submodules from work tree' '
+ mkdir submod &&
+ git update-index --add --cacheinfo 160000 $(git rev-parse HEAD) submod &&
+ git config -f .gitmodules submodule.sub.url ./. &&
+ git config -f .gitmodules submodule.sub.path submod &&
+ git submodule init &&
+ git add .gitmodules &&
+ git commit -m "add submodule" &&
+ git rm submod &&
+ test ! -e submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect actual &&
+ test_must_fail git config -f .gitmodules submodule.sub.url &&
+ test_must_fail git config -f .gitmodules submodule.sub.path
+'
+
+test_expect_success 'rm removes removed submodule from index and .gitmodules' '
+ git reset --hard &&
+ git submodule update &&
+ rm -rf submod &&
+ git rm submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect actual &&
+ test_must_fail git config -f .gitmodules submodule.sub.url &&
+ test_must_fail git config -f .gitmodules submodule.sub.path
+'
+
+test_expect_success 'rm removes work tree of unmodified submodules' '
+ git reset --hard &&
+ git submodule update &&
+ git rm submod &&
+ test ! -d submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect actual &&
+ test_must_fail git config -f .gitmodules submodule.sub.url &&
+ test_must_fail git config -f .gitmodules submodule.sub.path
+'
+
+test_expect_success 'rm removes a submodule with a trailing /' '
+ git reset --hard &&
+ git submodule update &&
+ git rm submod/ &&
+ test ! -d submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rm fails when given a file with a trailing /' '
+ test_must_fail git rm empty/
+'
+
+test_expect_success 'rm succeeds when given a directory with a trailing /' '
+ git rm -r frotz/
+'
+
+test_expect_success 'rm of a populated submodule with different HEAD fails unless forced' '
+ git reset --hard &&
+ git submodule update &&
+ (cd submod &&
+ git checkout HEAD^
+ ) &&
+ test_must_fail git rm submod &&
+ test -d submod &&
+ test -f submod/.git &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect.modified actual &&
+ git rm -f submod &&
+ test ! -d submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect actual &&
+ test_must_fail git config -f .gitmodules submodule.sub.url &&
+ test_must_fail git config -f .gitmodules submodule.sub.path
+'
+
+test_expect_success 'rm --cached leaves work tree of populated submodules and .gitmodules alone' '
+ git reset --hard &&
+ git submodule update &&
+ git rm --cached submod &&
+ test -d submod &&
+ test -f submod/.git &&
+ git status -s -uno >actual &&
+ test_cmp expect.cached actual &&
+ git config -f .gitmodules submodule.sub.url &&
+ git config -f .gitmodules submodule.sub.path
+'
+
+test_expect_success 'rm --dry-run does not touch the submodule or .gitmodules' '
+ git reset --hard &&
+ git submodule update &&
+ git rm -n submod &&
+ test -f submod/.git &&
+ git diff-index --exit-code HEAD
+'
+
+test_expect_success 'rm does not complain when no .gitmodules file is found' '
+ git reset --hard &&
+ git submodule update &&
+ git rm .gitmodules &&
+ git rm submod >actual 2>actual.err &&
+ ! test -s actual.err &&
+ ! test -d submod &&
+ ! test -f submod/.git &&
+ git status -s -uno >actual &&
+ test_cmp expect.both_deleted actual
+'
+
+test_expect_success 'rm will error out on a modified .gitmodules file unless staged' '
+ git reset --hard &&
+ git submodule update &&
+ git config -f .gitmodules foo.bar true &&
+ test_must_fail git rm submod >actual 2>actual.err &&
+ test -s actual.err &&
+ test -d submod &&
+ test -f submod/.git &&
+ git diff-files --quiet -- submod &&
+ git add .gitmodules &&
+ git rm submod >actual 2>actual.err &&
+ ! test -s actual.err &&
+ ! test -d submod &&
+ ! test -f submod/.git &&
+ git status -s -uno >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rm issues a warning when section is not found in .gitmodules' '
+ git reset --hard &&
+ git submodule update &&
+ git config -f .gitmodules --remove-section submodule.sub &&
+ git add .gitmodules &&
+ echo "warning: Could not find section in .gitmodules where path=submod" >expect.err &&
+ git rm submod >actual 2>actual.err &&
+ test_i18ncmp expect.err actual.err &&
+ ! test -d submod &&
+ ! test -f submod/.git &&
+ git status -s -uno >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rm of a populated submodule with modifications fails unless forced' '
+ git reset --hard &&
+ git submodule update &&
+ (cd submod &&
+ echo X >empty
+ ) &&
+ test_must_fail git rm submod &&
+ test -d submod &&
+ test -f submod/.git &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect.modified actual &&
+ git rm -f submod &&
+ test ! -d submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rm of a populated submodule with untracked files fails unless forced' '
+ git reset --hard &&
+ git submodule update &&
+ (cd submod &&
+ echo X >untracked
+ ) &&
+ test_must_fail git rm submod &&
+ test -d submod &&
+ test -f submod/.git &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect.modified actual &&
+ git rm -f submod &&
+ test ! -d submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup submodule conflict' '
+ git reset --hard &&
+ git submodule update &&
+ git checkout -b branch1 &&
+ echo 1 >nitfol &&
+ git add nitfol &&
+ git commit -m "added nitfol 1" &&
+ git checkout -b branch2 master &&
+ echo 2 >nitfol &&
+ git add nitfol &&
+ git commit -m "added nitfol 2" &&
+ git checkout -b conflict1 master &&
+ (cd submod &&
+ git fetch &&
+ git checkout branch1
+ ) &&
+ git add submod &&
+ git commit -m "submod 1" &&
+ git checkout -b conflict2 master &&
+ (cd submod &&
+ git checkout branch2
+ ) &&
+ git add submod &&
+ git commit -m "submod 2"
+'
+
+cat >expect.conflict <<EOF
+UU submod
+EOF
+
+test_expect_success 'rm removes work tree of unmodified conflicted submodule' '
+ git checkout conflict1 &&
+ git reset --hard &&
+ git submodule update &&
+ test_must_fail git merge conflict2 &&
+ git rm submod &&
+ test ! -d submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rm of a conflicted populated submodule with different HEAD fails unless forced' '
+ git checkout conflict1 &&
+ git reset --hard &&
+ git submodule update &&
+ (cd submod &&
+ git checkout HEAD^
+ ) &&
+ test_must_fail git merge conflict2 &&
+ test_must_fail git rm submod &&
+ test -d submod &&
+ test -f submod/.git &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect.conflict actual &&
+ git rm -f submod &&
+ test ! -d submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect actual &&
+ test_must_fail git config -f .gitmodules submodule.sub.url &&
+ test_must_fail git config -f .gitmodules submodule.sub.path
+'
+
+test_expect_success 'rm of a conflicted populated submodule with modifications fails unless forced' '
+ git checkout conflict1 &&
+ git reset --hard &&
+ git submodule update &&
+ (cd submod &&
+ echo X >empty
+ ) &&
+ test_must_fail git merge conflict2 &&
+ test_must_fail git rm submod &&
+ test -d submod &&
+ test -f submod/.git &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect.conflict actual &&
+ git rm -f submod &&
+ test ! -d submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect actual &&
+ test_must_fail git config -f .gitmodules submodule.sub.url &&
+ test_must_fail git config -f .gitmodules submodule.sub.path
+'
+
+test_expect_success 'rm of a conflicted populated submodule with untracked files fails unless forced' '
+ git checkout conflict1 &&
+ git reset --hard &&
+ git submodule update &&
+ (cd submod &&
+ echo X >untracked
+ ) &&
+ test_must_fail git merge conflict2 &&
+ test_must_fail git rm submod &&
+ test -d submod &&
+ test -f submod/.git &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect.conflict actual &&
+ git rm -f submod &&
+ test ! -d submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rm of a conflicted populated submodule with a .git directory fails even when forced' '
+ git checkout conflict1 &&
+ git reset --hard &&
+ git submodule update &&
+ (cd submod &&
+ rm .git &&
+ cp -R ../.git/modules/sub .git &&
+ GIT_WORK_TREE=. git config --unset core.worktree
+ ) &&
+ test_must_fail git merge conflict2 &&
+ test_must_fail git rm submod &&
+ test -d submod &&
+ test -d submod/.git &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect.conflict actual &&
+ test_must_fail git rm -f submod &&
+ test -d submod &&
+ test -d submod/.git &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect.conflict actual &&
+ git merge --abort &&
+ rm -rf submod
+'
+
+test_expect_success 'rm of a conflicted unpopulated submodule succeeds' '
+ git checkout conflict1 &&
+ git reset --hard &&
+ test_must_fail git merge conflict2 &&
+ git rm submod &&
+ test ! -d submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rm of a populated submodule with a .git directory fails even when forced' '
+ git checkout -f master &&
+ git reset --hard &&
+ git submodule update &&
+ (cd submod &&
+ rm .git &&
+ cp -R ../.git/modules/sub .git &&
+ GIT_WORK_TREE=. git config --unset core.worktree
+ ) &&
+ test_must_fail git rm submod &&
+ test -d submod &&
+ test -d submod/.git &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ ! test -s actual &&
+ test_must_fail git rm -f submod &&
+ test -d submod &&
+ test -d submod/.git &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ ! test -s actual &&
+ rm -rf submod
+'
+
+cat >expect.deepmodified <<EOF
+ M submod/subsubmod
+EOF
+
+test_expect_success 'setup subsubmodule' '
+ git reset --hard &&
+ git submodule update &&
+ (cd submod &&
+ git update-index --add --cacheinfo 160000 $(git rev-parse HEAD) subsubmod &&
+ git config -f .gitmodules submodule.sub.url ../. &&
+ git config -f .gitmodules submodule.sub.path subsubmod &&
+ git submodule init &&
+ git add .gitmodules &&
+ git commit -m "add subsubmodule" &&
+ git submodule update subsubmod
+ ) &&
+ git commit -a -m "added deep submodule"
+'
+
+test_expect_success 'rm recursively removes work tree of unmodified submodules' '
+ git rm submod &&
+ test ! -d submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rm of a populated nested submodule with different nested HEAD fails unless forced' '
+ git reset --hard &&
+ git submodule update --recursive &&
+ (cd submod/subsubmod &&
+ git checkout HEAD^
+ ) &&
+ test_must_fail git rm submod &&
+ test -d submod &&
+ test -f submod/.git &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect.modified actual &&
+ git rm -f submod &&
+ test ! -d submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rm of a populated nested submodule with nested modifications fails unless forced' '
+ git reset --hard &&
+ git submodule update --recursive &&
+ (cd submod/subsubmod &&
+ echo X >empty
+ ) &&
+ test_must_fail git rm submod &&
+ test -d submod &&
+ test -f submod/.git &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect.modified actual &&
+ git rm -f submod &&
+ test ! -d submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rm of a populated nested submodule with nested untracked files fails unless forced' '
+ git reset --hard &&
+ git submodule update --recursive &&
+ (cd submod/subsubmod &&
+ echo X >untracked
+ ) &&
+ test_must_fail git rm submod &&
+ test -d submod &&
+ test -f submod/.git &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect.modified actual &&
+ git rm -f submod &&
+ test ! -d submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rm of a populated nested submodule with a nested .git directory fails even when forced' '
+ git reset --hard &&
+ git submodule update --recursive &&
+ (cd submod/subsubmod &&
+ rm .git &&
+ cp -R ../../.git/modules/sub/modules/sub .git &&
+ GIT_WORK_TREE=. git config --unset core.worktree
+ ) &&
+ test_must_fail git rm submod &&
+ test -d submod &&
+ test -d submod/subsubmod/.git &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ ! test -s actual &&
+ test_must_fail git rm -f submod &&
+ test -d submod &&
+ test -d submod/subsubmod/.git &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ ! test -s actual &&
+ rm -rf submod
+'
+
+test_expect_success 'checking out a commit after submodule removal needs manual updates' '
+ git commit -m "submodule removal" submod &&
+ git checkout HEAD^ &&
+ git submodule update &&
+ git checkout -q HEAD^ 2>actual &&
+ git checkout -q master 2>actual &&
+ test_i18ngrep "^warning: unable to rmdir submod:" actual &&
+ git status -s submod >actual &&
+ echo "?? submod/" >expected &&
+ test_cmp expected actual &&
+ rm -rf submod &&
+ git status -s -uno --ignore-submodules=none > actual &&
+ ! test -s actual
+'
+
+test_expect_success 'rm of d/f when d has become a non-directory' '
+ rm -rf d &&
+ mkdir d &&
+ >d/f &&
+ git add d &&
+ rm -rf d &&
+ >d &&
+ git rm d/f &&
+ test_must_fail git rev-parse --verify :d/f &&
+ test_path_is_file d
+'
+
+test_expect_success SYMLINKS 'rm of d/f when d has become a dangling symlink' '
+ rm -rf d &&
+ mkdir d &&
+ >d/f &&
+ git add d &&
+ rm -rf d &&
+ ln -s nonexistent d &&
+ git rm d/f &&
+ test_must_fail git rev-parse --verify :d/f &&
+ test -h d &&
+ test_path_is_missing d
+'
+
+test_expect_success 'rm of file when it has become a directory' '
+ rm -rf d &&
+ >d &&
+ git add d &&
+ rm -f d &&
+ mkdir d &&
+ >d/f &&
+ test_must_fail git rm d &&
+ git rev-parse --verify :d &&
+ test_path_is_file d/f
+'
+
+test_expect_success SYMLINKS 'rm across a symlinked leading path (no index)' '
+ rm -rf d e &&
+ mkdir e &&
+ echo content >e/f &&
+ ln -s e d &&
+ git add -A e d &&
+ git commit -m "symlink d to e, e/f exists" &&
+ test_must_fail git rm d/f &&
+ git rev-parse --verify :d &&
+ git rev-parse --verify :e/f &&
+ test -h d &&
+ test_path_is_file e/f
+'
+
+test_expect_failure SYMLINKS 'rm across a symlinked leading path (w/ index)' '
+ rm -rf d e &&
+ mkdir d &&
+ echo content >d/f &&
+ git add -A e d &&
+ git commit -m "d/f exists" &&
+ mv d e &&
+ ln -s e d &&
+ test_must_fail git rm d/f &&
+ git rev-parse --verify :d/f &&
+ test -h d &&
+ test_path_is_file e/f
+'
+
+test_expect_success 'setup for testing rm messages' '
+ >bar.txt &&
+ >foo.txt &&
+ git add bar.txt foo.txt
+'
+
+test_expect_success 'rm files with different staged content' '
+ cat >expect <<-\EOF &&
+ error: the following files have staged content different from both the
+ file and the HEAD:
+ bar.txt
+ foo.txt
+ (use -f to force removal)
+ EOF
+ echo content1 >foo.txt &&
+ echo content1 >bar.txt &&
+ test_must_fail git rm foo.txt bar.txt 2>actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'rm files with different staged content without hints' '
+ cat >expect <<-\EOF &&
+ error: the following files have staged content different from both the
+ file and the HEAD:
+ bar.txt
+ foo.txt
+ EOF
+ echo content2 >foo.txt &&
+ echo content2 >bar.txt &&
+ test_must_fail git -c advice.rmhints=false rm foo.txt bar.txt 2>actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'rm file with local modification' '
+ cat >expect <<-\EOF &&
+ error: the following file has local modifications:
+ foo.txt
+ (use --cached to keep the file, or -f to force removal)
+ EOF
+ git commit -m "testing rm 3" &&
+ echo content3 >foo.txt &&
+ test_must_fail git rm foo.txt 2>actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'rm file with local modification without hints' '
+ cat >expect <<-\EOF &&
+ error: the following file has local modifications:
+ bar.txt
+ EOF
+ echo content4 >bar.txt &&
+ test_must_fail git -c advice.rmhints=false rm bar.txt 2>actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'rm file with changes in the index' '
+ cat >expect <<-\EOF &&
+ error: the following file has changes staged in the index:
+ foo.txt
+ (use --cached to keep the file, or -f to force removal)
+ EOF
+ git reset --hard &&
+ echo content5 >foo.txt &&
+ git add foo.txt &&
+ test_must_fail git rm foo.txt 2>actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'rm file with changes in the index without hints' '
+ cat >expect <<-\EOF &&
+ error: the following file has changes staged in the index:
+ foo.txt
+ EOF
+ test_must_fail git -c advice.rmhints=false rm foo.txt 2>actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'rm files with two different errors' '
+ cat >expect <<-\EOF &&
+ error: the following file has staged content different from both the
+ file and the HEAD:
+ foo1.txt
+ (use -f to force removal)
+ error: the following file has changes staged in the index:
+ bar1.txt
+ (use --cached to keep the file, or -f to force removal)
+ EOF
+ echo content >foo1.txt &&
+ git add foo1.txt &&
+ echo content6 >foo1.txt &&
+ echo content6 >bar1.txt &&
+ git add bar1.txt &&
+ test_must_fail git rm bar1.txt foo1.txt 2>actual &&
+ test_i18ncmp expect actual
+'
+
test_done
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index d03495d..4865304 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -25,16 +25,15 @@ test_expect_success \
echo foo >xfoo1 &&
chmod 755 xfoo1 &&
git add xfoo1 &&
- case "`git ls-files --stage xfoo1`" in
+ 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' '
+test_expect_success '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
+ test_ln_s_add foo xfoo1 &&
+ case "$(git ls-files --stage xfoo1)" in
120000" "*xfoo1) echo pass;;
*) echo fail; git ls-files --stage xfoo1; (exit 1);;
esac
@@ -46,27 +45,25 @@ test_expect_success \
echo foo >xfoo2 &&
chmod 755 xfoo2 &&
git update-index --add xfoo2 &&
- case "`git ls-files --stage xfoo2`" in
+ 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' '
+test_expect_success 'git add: filemode=0 should not get confused by symlink' '
rm -f xfoo2 &&
- ln -s foo xfoo2 &&
- git update-index --add xfoo2 &&
- case "`git ls-files --stage xfoo2`" in
+ test_ln_s_add foo xfoo2 &&
+ case "$(git ls-files --stage xfoo2)" in
120000" "*xfoo2) echo pass;;
*) echo fail; git ls-files --stage xfoo2; (exit 1);;
esac
'
-test_expect_success SYMLINKS \
+test_expect_success \
'git update-index --add: Test that executable bit is not used...' \
'git config core.filemode 0 &&
- ln -s xfoo2 xfoo3 &&
- git update-index --add xfoo3 &&
- case "`git ls-files --stage xfoo3`" in
+ test_ln_s_add xfoo2 xfoo3 && # runs git update-index --add
+ case "$(git ls-files --stage xfoo3)" in
120000" "*xfoo3) echo pass;;
*) echo fail; git ls-files --stage xfoo3; (exit 1);;
esac'
@@ -94,6 +91,13 @@ test_expect_success 'error out when attempting to add ignored ones without -f' '
! (git ls-files | grep "\\.ig")
'
+test_expect_success 'error out when attempting to add ignored ones but add others' '
+ touch a.if &&
+ test_must_fail git add a.?? &&
+ ! (git ls-files | grep "\\.ig") &&
+ (git ls-files | grep a.if)
+'
+
test_expect_success 'add ignored ones with -f' '
git add -f a.?? &&
git ls-files --error-unmatch a.ig
@@ -169,17 +173,32 @@ test_expect_success 'git add with filemode=0, symlinks=0 prefers stage 2 over st
test_expect_success 'git add --refresh' '
>foo && git add foo && git commit -a -m "commit all" &&
- test -z "`git diff-index HEAD -- foo`" &&
+ test -z "$(git diff-index HEAD -- foo)" &&
git read-tree HEAD &&
- case "`git diff-index HEAD -- foo`" in
+ 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 -z "$(git diff-index HEAD -- foo)"
'
-test_expect_success POSIXPERM 'git add should fail atomically upon an unreadable file' '
+test_expect_success 'git add --refresh with pathspec' '
+ git reset --hard &&
+ echo >foo && echo >bar && echo >baz &&
+ git add foo bar baz && H=$(git rev-parse :foo) && git rm -f foo &&
+ echo "100644 $H 3 foo" | git update-index --index-info &&
+ test-chmtime -60 bar baz &&
+ >expect &&
+ git add --refresh bar >actual &&
+ test_cmp expect actual &&
+
+ git diff-files --name-only >actual &&
+ ! grep bar actual&&
+ grep baz actual
+'
+
+test_expect_success POSIXPERM,SANITY 'git add should fail atomically upon an unreadable file' '
git reset --hard &&
date >foo1 &&
date >foo2 &&
@@ -190,7 +209,7 @@ test_expect_success POSIXPERM 'git add should fail atomically upon an unreadable
rm -f foo2
-test_expect_success POSIXPERM 'git add --ignore-errors' '
+test_expect_success POSIXPERM,SANITY 'git add --ignore-errors' '
git reset --hard &&
date >foo1 &&
date >foo2 &&
@@ -201,7 +220,7 @@ test_expect_success POSIXPERM 'git add --ignore-errors' '
rm -f foo2
-test_expect_success POSIXPERM 'git add (add.ignore-errors)' '
+test_expect_success POSIXPERM,SANITY 'git add (add.ignore-errors)' '
git config add.ignore-errors 1 &&
git reset --hard &&
date >foo1 &&
@@ -212,7 +231,7 @@ test_expect_success POSIXPERM 'git add (add.ignore-errors)' '
'
rm -f foo2
-test_expect_success POSIXPERM 'git add (add.ignore-errors = false)' '
+test_expect_success POSIXPERM,SANITY 'git add (add.ignore-errors = false)' '
git config add.ignore-errors 0 &&
git reset --hard &&
date >foo1 &&
@@ -223,7 +242,7 @@ test_expect_success POSIXPERM 'git add (add.ignore-errors = false)' '
'
rm -f foo2
-test_expect_success POSIXPERM '--no-ignore-errors overrides config' '
+test_expect_success POSIXPERM,SANITY '--no-ignore-errors overrides config' '
git config add.ignore-errors 1 &&
git reset --hard &&
date >foo1 &&
@@ -260,6 +279,25 @@ test_expect_success '"add non-existent" should fail' '
! (git ls-files | grep "non-existent")
'
+test_expect_success 'git add -A on empty repo does not error out' '
+ rm -fr empty &&
+ git init empty &&
+ (
+ cd empty &&
+ git add -A . &&
+ git add -A
+ )
+'
+
+test_expect_success '"git add ." in empty repo' '
+ rm -fr empty &&
+ git init empty &&
+ (
+ cd empty &&
+ git add .
+ )
+'
+
test_expect_success 'git add --dry-run of existing changed file' "
echo new >>track-this &&
git add --dry-run track-this >actual 2>&1 &&
@@ -268,21 +306,60 @@ test_expect_success 'git add --dry-run of existing changed file' "
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
+ test_must_fail git add --dry-run track-this ignored-file >actual 2>&1
"
-cat >expect <<EOF
+test_expect_success 'git add --dry-run of an existing file output' "
+ echo \"fatal: pathspec 'ignored-file' did not match any files\" >expect &&
+ test_i18ncmp expect 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 2>&1 &&
- test_cmp expect actual
+ test_must_fail git add --dry-run --ignore-missing track-this ignored-file >actual.out 2>actual.err
+'
+
+test_expect_success 'git add --dry-run --ignore-missing of non-existing file output' '
+ test_i18ncmp expect.out actual.out &&
+ test_i18ncmp expect.err actual.err
+'
+
+test_expect_success 'git add --chmod=+x stages a non-executable file with +x' '
+ echo foo >foo1 &&
+ git add --chmod=+x foo1 &&
+ case "$(git ls-files --stage foo1)" in
+ 100755" "*foo1) echo pass;;
+ *) echo fail; git ls-files --stage foo1; (exit 1);;
+ esac
+'
+
+test_expect_success 'git add --chmod=-x stages an executable file with -x' '
+ echo foo >xfoo1 &&
+ chmod 755 xfoo1 &&
+ git add --chmod=-x 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 POSIXPERM,SYMLINKS 'git add --chmod=+x with symlinks' '
+ git config core.filemode 1 &&
+ git config core.symlinks 1 &&
+ echo foo >foo2 &&
+ git add --chmod=+x foo2 &&
+ case "$(git ls-files --stage foo2)" in
+ 100755" "*foo2) echo pass;;
+ *) echo fail; git ls-files --stage foo2; (exit 1);;
+ esac
'
test_done
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 7ad8465..deae948 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -3,8 +3,9 @@
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'
+if ! test_have_prereq PERL
+then
+ skip_all='skipping add -i tests, perl not available'
test_done
fi
@@ -18,6 +19,8 @@ test_expect_success 'status works (initial)' '
git add -i </dev/null >output &&
grep "+1/-0 *+2/-0 file" output
'
+
+test_expect_success 'setup expected' '
cat >expected <<EOF
new file mode 100644
index 0000000..d95f3ad
@@ -26,6 +29,8 @@ index 0000000..d95f3ad
@@ -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 &&
@@ -51,6 +56,8 @@ test_expect_success 'status works (commit)' '
git add -i </dev/null >output &&
grep "+1/-0 *+2/-0 file" output
'
+
+test_expect_success 'setup expected' '
cat >expected <<EOF
index 180b47c..b6f2c08 100644
--- a/file
@@ -59,6 +66,8 @@ index 180b47c..b6f2c08 100644
baseline
+content
EOF
+'
+
test_expect_success 'diff works (commit)' '
(echo d; echo 1) | git add -i >output &&
sed -ne "/^index/,/content/p" <output >diff &&
@@ -71,48 +80,64 @@ test_expect_success 'revert works (commit)' '
grep "unchanged *+3/-0 file" output
'
+
+test_expect_success 'setup expected' '
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 'setup fake editor' '
+ >fake_editor.sh &&
+ 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
'
+test_expect_success 'setup patch' '
cat >patch <<EOF
@@ -1,1 +1,4 @@
this
+patch
--doesn't
+-does not
apply
EOF
-echo "#!$SHELL_PATH" >fake_editor.sh
-cat >>fake_editor.sh <<\EOF
+'
+
+test_expect_success 'setup fake editor' '
+ 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"
+ 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
'
+test_expect_success 'setup patch' '
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
'
+test_expect_success 'setup patch' '
cat >patch <<EOF
@@ -1,0 +1,0 @@
baseline
@@ -120,6 +145,9 @@ cat >patch <<EOF
+newcontent
+lines
EOF
+'
+
+test_expect_success 'setup expected' '
cat >expected <<EOF
diff --git a/file b/file
index b5dd6c9..f910ae9 100644
@@ -132,6 +160,8 @@ index b5dd6c9..f910ae9 100644
+more
lines
EOF
+'
+
test_expect_success 'real edit works' '
(echo e; echo n; echo d) | git add -p &&
git diff >output &&
@@ -152,13 +182,6 @@ test_expect_success 'skip files similarly as commit -a' '
'
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 &&
@@ -197,6 +220,7 @@ test_expect_success 'setup again' '
'
# Write the patch file with a new line at the top and bottom
+test_expect_success 'setup patch' '
cat >patch <<EOF
index 180b47c..b6f2c08 100644
--- a/file
@@ -207,7 +231,10 @@ index 180b47c..b6f2c08 100644
content
+lastline
EOF
+'
+
# Expected output, similar to the patch but w/ diff at the top
+test_expect_success 'setup expected' '
cat >expected <<EOF
diff --git a/file b/file
index b6f2c08..61b9053 100755
@@ -219,6 +246,8 @@ index b6f2c08..61b9053 100755
content
+lastline
EOF
+'
+
# Test splitting the first patch, then adding both
test_expect_success 'add first line works' '
git commit -am "clear local changes" &&
@@ -228,6 +257,7 @@ test_expect_success 'add first line works' '
test_cmp expected diff
'
+test_expect_success 'setup expected' '
cat >expected <<EOF
diff --git a/non-empty b/non-empty
deleted file mode 100644
@@ -237,6 +267,8 @@ index d95f3ad..0000000
@@ -1 +0,0 @@
-content
EOF
+'
+
test_expect_success 'deleting a non-empty file' '
git reset --hard &&
echo content >non-empty &&
@@ -248,11 +280,13 @@ test_expect_success 'deleting a non-empty file' '
test_cmp expected diff
'
+test_expect_success 'setup expected' '
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 &&
@@ -265,4 +299,85 @@ test_expect_success 'deleting an empty file' '
test_cmp expected diff
'
+test_expect_success 'split hunk setup' '
+ git reset --hard &&
+ for i in 10 20 30 40 50 60
+ do
+ echo $i
+ done >test &&
+ git add test &&
+ test_tick &&
+ git commit -m test &&
+
+ for i in 10 15 20 21 22 23 24 30 40 50 60
+ do
+ echo $i
+ done >test
+'
+
+test_expect_success 'split hunk "add -p (edit)"' '
+ # Split, say Edit and do nothing. Then:
+ #
+ # 1. Broken version results in a patch that does not apply and
+ # only takes [y/n] (edit again) so the first q is discarded
+ # and then n attempts to discard the edit. Repeat q enough
+ # times to get out.
+ #
+ # 2. Correct version applies the (not)edited version, and asks
+ # about the next hunk, against which we say q and program
+ # exits.
+ printf "%s\n" s e q n q q |
+ EDITOR=: git add -p &&
+ git diff >actual &&
+ ! grep "^+15" actual
+'
+
+test_expect_failure 'split hunk "add -p (no, yes, edit)"' '
+ cat >test <<-\EOF &&
+ 5
+ 10
+ 20
+ 21
+ 30
+ 31
+ 40
+ 50
+ 60
+ EOF
+ git reset &&
+ # test sequence is s(plit), n(o), y(es), e(dit)
+ # q n q q is there to make sure we exit at the end.
+ printf "%s\n" s n y e q n q q |
+ EDITOR=: git add -p 2>error &&
+ test_must_be_empty error &&
+ git diff >actual &&
+ ! grep "^+31" actual
+'
+
+test_expect_success 'patch mode ignores unmerged entries' '
+ git reset --hard &&
+ test_commit conflict &&
+ test_commit non-conflict &&
+ git checkout -b side &&
+ test_commit side conflict.t &&
+ git checkout master &&
+ test_commit master conflict.t &&
+ test_must_fail git merge side &&
+ echo changed >non-conflict.t &&
+ echo y | git add -p >output &&
+ ! grep a/conflict.t output &&
+ cat >expected <<-\EOF &&
+ * Unmerged path conflict.t
+ diff --git a/non-conflict.t b/non-conflict.t
+ index f766221..5ea2ed4 100644
+ --- a/non-conflict.t
+ +++ b/non-conflict.t
+ @@ -1 +1 @@
+ -non-conflict
+ +changed
+ EOF
+ git diff --cached >diff &&
+ test_cmp expected diff
+'
+
test_done
diff --git a/t/t3702-add-edit.sh b/t/t3702-add-edit.sh
index 4ee47cc..3cb74ca 100755
--- a/t/t3702-add-edit.sh
+++ b/t/t3702-add-edit.sh
@@ -118,4 +118,11 @@ test_expect_success 'add -e' '
'
+test_expect_success 'add -e notices editor failure' '
+ git reset --hard &&
+ echo change >>file &&
+ test_must_fail env GIT_EDITOR=false git add -e &&
+ test_expect_code 1 git diff --exit-code
+'
+
test_done
diff --git a/t/t3703-add-magic-pathspec.sh b/t/t3703-add-magic-pathspec.sh
new file mode 100755
index 0000000..3ef525a
--- /dev/null
+++ b/t/t3703-add-magic-pathspec.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+test_description='magic pathspec tests using git-add'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ mkdir sub anothersub &&
+ : >sub/foo &&
+ : >anothersub/foo
+'
+
+test_expect_success 'add :/' "
+ cat >expected <<-EOF &&
+ add 'anothersub/foo'
+ add 'expected'
+ add 'sub/actual'
+ add 'sub/foo'
+ EOF
+ (cd sub && git add -n :/ >actual) &&
+ test_cmp expected sub/actual
+"
+
+cat >expected <<EOF
+add 'anothersub/foo'
+EOF
+
+test_expect_success 'add :/anothersub' '
+ (cd sub && git add -n :/anothersub >actual) &&
+ test_cmp expected sub/actual
+'
+
+test_expect_success 'add :/non-existent' '
+ (cd sub && test_must_fail git add -n :/non-existent)
+'
+
+cat >expected <<EOF
+add 'sub/foo'
+EOF
+
+if test_have_prereq !MINGW && mkdir ":" 2>/dev/null
+then
+ test_set_prereq COLON_DIR
+fi
+
+test_expect_success COLON_DIR 'a file with the same (long) magic name exists' '
+ : >":(icase)ha" &&
+ test_must_fail git add -n ":(icase)ha" &&
+ git add -n "./:(icase)ha"
+'
+
+test_expect_success COLON_DIR 'a file with the same (short) magic name exists' '
+ : >":/bar" &&
+ test_must_fail git add -n :/bar &&
+ git add -n "./:/bar"
+'
+
+test_done
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index 256c4c9..4bf1dbe 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -34,6 +34,48 @@ test_expect_success 'no encoding header for base case' '
test z = "z$E"
'
+test_expect_success 'UTF-16 refused because of NULs' '
+ echo UTF-16 >F &&
+ test_must_fail git commit -a -F "$TEST_DIRECTORY"/t3900/UTF-16.txt
+'
+
+test_expect_success 'UTF-8 invalid characters refused' '
+ test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+ echo "UTF-8 characters" >F &&
+ printf "Commit message\n\nInvalid surrogate:\355\240\200\n" \
+ >"$HOME/invalid" &&
+ git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
+ grep "did not conform" "$HOME"/stderr
+'
+
+test_expect_success 'UTF-8 overlong sequences rejected' '
+ test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+ rm -f "$HOME/stderr" "$HOME/invalid" &&
+ echo "UTF-8 overlong" >F &&
+ printf "\340\202\251ommit message\n\nThis is not a space:\300\240\n" \
+ >"$HOME/invalid" &&
+ git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
+ grep "did not conform" "$HOME"/stderr
+'
+
+test_expect_success 'UTF-8 non-characters refused' '
+ test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+ echo "UTF-8 non-character 1" >F &&
+ printf "Commit message\n\nNon-character:\364\217\277\276\n" \
+ >"$HOME/invalid" &&
+ git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
+ grep "did not conform" "$HOME"/stderr
+'
+
+test_expect_success 'UTF-8 non-characters refused' '
+ test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+ echo "UTF-8 non-character 2." >F &&
+ printf "Commit message\n\nNon-character:\357\267\220\n" \
+ >"$HOME/invalid" &&
+ git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
+ grep "did not conform" "$HOME"/stderr
+'
+
for H in ISO8859-1 eucJP ISO-2022-JP
do
test_expect_success "$H setup" '
@@ -133,4 +175,33 @@ do
'
done
+test_commit_autosquash_flags () {
+ H=$1
+ flag=$2
+ test_expect_success "commit --$flag with $H encoding" '
+ git config i18n.commitencoding $H &&
+ git checkout -b $H-$flag C0 &&
+ echo $H >>F &&
+ git commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt &&
+ test_tick &&
+ echo intermediate stuff >>G &&
+ git add G &&
+ git commit -a -m "intermediate commit" &&
+ test_tick &&
+ echo $H $flag >>F &&
+ git commit -a --$flag HEAD~1 &&
+ E=$(git cat-file commit '$H-$flag' |
+ sed -ne "s/^encoding //p") &&
+ test "z$E" = "z$H" &&
+ git config --unset-all i18n.commitencoding &&
+ git rebase --autosquash -i HEAD^^^ &&
+ git log --oneline >actual &&
+ test_line_count = 3 actual
+ '
+}
+
+test_commit_autosquash_flags eucJP fixup
+
+test_commit_autosquash_flags ISO-2022-JP squash
+
test_done
diff --git a/t/t3900/UTF-16.txt b/t/t3900/UTF-16.txt
new file mode 100644
index 0000000..2257f05
--- /dev/null
+++ b/t/t3900/UTF-16.txt
Binary files differ
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 31a5770..509084e 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -9,7 +9,7 @@ test_description='i18n settings and format-patch | am pipe'
check_encoding () {
# Make sure characters are not corrupted
- cnt="$1" header="$2" i=1 j=0 bad=0
+ cnt="$1" header="$2" i=1 j=0
while test "$i" -le $cnt
do
git format-patch --encoding=UTF-8 --stdout HEAD~$i..HEAD~$j |
@@ -20,14 +20,10 @@ check_encoding () {
grep "^encoding ISO8859-1" ;;
*)
grep "^encoding ISO8859-1"; test "$?" != 0 ;;
- esac || {
- bad=1
- break
- }
+ esac || return 1
j=$i
i=$(($i+1))
done
- (exit $bad)
}
test_expect_success setup '
@@ -54,10 +50,13 @@ test_expect_success setup '
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 &&
+ if test_have_prereq !MINGW
+ then
+ # 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
+ fi &&
test_tick &&
echo Yet another >theirs &&
git add theirs &&
@@ -119,7 +118,7 @@ test_expect_success 'rebase (U/L)' '
check_encoding 2
'
-test_expect_success 'rebase (L/L)' '
+test_expect_success !MINGW '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 &&
@@ -131,7 +130,7 @@ test_expect_success 'rebase (L/L)' '
check_encoding 2 8859
'
-test_expect_success 'rebase (L/U)' '
+test_expect_success !MINGW 'rebase (L/U)' '
# This is pathological -- use UTF-8 as intermediate form
# to get ISO-8859-1 results.
git config i18n.commitencoding ISO8859-1 &&
@@ -159,7 +158,7 @@ test_expect_success 'cherry-pick(U/U)' '
check_encoding 3
'
-test_expect_success 'cherry-pick(L/L)' '
+test_expect_success !MINGW 'cherry-pick(L/L)' '
# Both the commitencoding and logoutputencoding is set to ISO-8859-1
git config i18n.commitencoding ISO8859-1 &&
@@ -189,7 +188,7 @@ test_expect_success 'cherry-pick(U/L)' '
check_encoding 3
'
-test_expect_success 'cherry-pick(L/U)' '
+test_expect_success !MINGW 'cherry-pick(L/U)' '
# Again, the commitencoding is set to ISO-8859-1 but
# logoutputencoding is set to UTF-8.
@@ -252,4 +251,66 @@ test_expect_success 'rebase --merge (L/U)' '
check_encoding 2 8859
'
+test_expect_success 'am (U/U)' '
+ # Apply UTF-8 patches with UTF-8 commitencoding
+ git config i18n.commitencoding UTF-8 &&
+ . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+
+ git reset --hard master &&
+ git am out-u1 out-u2 &&
+
+ check_encoding 2
+'
+
+test_expect_success !MINGW 'am (L/L)' '
+ # Apply ISO-8859-1 patches with ISO-8859-1 commitencoding
+ git config i18n.commitencoding ISO8859-1 &&
+ . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+
+ git reset --hard master &&
+ git am out-l1 out-l2 &&
+
+ check_encoding 2 8859
+'
+
+test_expect_success 'am (U/L)' '
+ # Apply ISO-8859-1 patches with UTF-8 commitencoding
+ git config i18n.commitencoding UTF-8 &&
+ . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+ git reset --hard master &&
+
+ # am specifies --utf8 by default.
+ git am out-l1 out-l2 &&
+
+ check_encoding 2
+'
+
+test_expect_success 'am --no-utf8 (U/L)' '
+ # Apply ISO-8859-1 patches with UTF-8 commitencoding
+ git config i18n.commitencoding UTF-8 &&
+ . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+
+ git reset --hard master &&
+ git am --no-utf8 out-l1 out-l2 2>err &&
+
+ # commit-tree will warn that the commit message does not contain valid UTF-8
+ # as mailinfo did not convert it
+ grep "did not conform" err &&
+
+ check_encoding 2
+'
+
+test_expect_success !MINGW 'am (L/U)' '
+ # Apply UTF-8 patches with ISO-8859-1 commitencoding
+ git config i18n.commitencoding ISO8859-1 &&
+ . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+
+ git reset --hard master &&
+ # mailinfo will re-code the commit message to the charset specified by
+ # i18n.commitencoding
+ git am out-u1 out-u2 &&
+
+ check_encoding 2 8859
+'
+
test_done
diff --git a/t/t3902-quoted.sh b/t/t3902-quoted.sh
index 147e634..f528008 100755
--- a/t/t3902-quoted.sh
+++ b/t/t3902-quoted.sh
@@ -10,16 +10,16 @@ test_description='quoted output'
FN='濱野'
GN='ç´”'
HT=' '
-LF='
-'
DQ='"'
+test_have_prereq MINGW ||
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.'
+if ! test -f "Name and an${HT}HT"
+then
+ # FAT/NTFS does not allow tabs in filenames
+ skip_all='Your filesystem does not allow tabs in filenames'
test_done
-}
+fi
for_each_name () {
for name in \
@@ -31,21 +31,22 @@ for_each_name () {
done
}
-test_expect_success setup '
+test_expect_success 'setup' '
mkdir "$FN" &&
- for_each_name "echo initial >\"\$name\""
+ for_each_name "echo initial >\"\$name\"" &&
git add . &&
git commit -q -m Initial &&
for_each_name "echo second >\"\$name\"" &&
- git commit -a -m Second
+ git commit -a -m Second &&
for_each_name "echo modified >\"\$name\""
'
-cat >expect.quoted <<\EOF
+test_expect_success 'setup expected files' '
+cat >expect.quoted <<\EOF &&
Name
"Name and a\nLF"
"Name and an\tHT"
@@ -72,6 +73,7 @@ With SP in it
濱野/file
濱野純
EOF
+'
test_expect_success 'check fully quoted output from ls-files' '
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 62e208a..2142c1f 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -10,6 +10,8 @@ test_description='Test git stash'
test_expect_success 'stash some dirty working directory' '
echo 1 > file &&
git add file &&
+ echo unrelated >other-file &&
+ git add other-file &&
test_tick &&
git commit -m initial &&
echo 2 > file &&
@@ -37,14 +39,30 @@ test_expect_success 'parents of stash' '
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 'applying bogus stash does nothing' '
+ test_must_fail git stash apply stash@{1} &&
+ echo 1 >expect &&
+ test_cmp expect file
+'
+
+test_expect_success 'apply does not need clean working directory' '
+ echo 4 >other-file &&
+ git stash apply &&
+ echo 3 >expect &&
+ test_cmp expect file
+'
+
+test_expect_success 'apply does not clobber working directory changes' '
+ git reset --hard &&
+ echo 4 >file &&
+ test_must_fail git stash apply &&
+ echo 4 >expect &&
+ test_cmp expect file
'
test_expect_success 'apply stashed changes' '
+ git reset --hard &&
+ echo 5 >other-file &&
git add other-file &&
test_tick &&
git commit -m other-file &&
@@ -69,9 +87,14 @@ test_expect_success 'apply stashed changes (including index)' '
test_expect_success 'unstashing in a subdirectory' '
git reset --hard HEAD &&
mkdir subdir &&
- cd subdir &&
- git stash apply &&
- cd ..
+ (
+ cd subdir &&
+ git stash apply
+ )
+'
+
+test_expect_success 'stash drop complains of extra options' '
+ test_must_fail git stash drop --foo
'
test_expect_success 'drop top stash' '
@@ -156,7 +179,7 @@ EOF
test_expect_success 'stash branch' '
echo foo > file &&
- git commit file -m first
+ git commit file -m first &&
echo bar > file &&
echo bar2 > file2 &&
git add file2 &&
@@ -181,17 +204,17 @@ test_expect_success 'apply -q is quiet' '
echo foo > file &&
git stash &&
git stash apply -q > output.out 2>&1 &&
- test ! -s output.out
+ test_must_be_empty output.out
'
test_expect_success 'save -q is quiet' '
git stash save --quiet > output.out 2>&1 &&
- test ! -s output.out
+ test_must_be_empty output.out
'
test_expect_success 'pop -q is quiet' '
git stash pop -q > output.out 2>&1 &&
- test ! -s output.out
+ test_must_be_empty output.out
'
test_expect_success 'pop -q --index works and is quiet' '
@@ -200,13 +223,13 @@ test_expect_success 'pop -q --index works and is quiet' '
git stash save --quiet &&
git stash pop -q --index > output.out 2>&1 &&
test foo = "$(git show :file)" &&
- test ! -s output.out
+ test_must_be_empty output.out
'
test_expect_success 'drop -q is quiet' '
git stash &&
git stash drop -q > output.out 2>&1 &&
- test ! -s output.out
+ test_must_be_empty output.out
'
test_expect_success 'stash -k' '
@@ -217,6 +240,14 @@ test_expect_success 'stash -k' '
test bar,bar4 = $(cat file),$(cat file2)
'
+test_expect_success 'stash --no-keep-index' '
+ echo bar33 > file &&
+ echo bar44 > file2 &&
+ git add file2 &&
+ git stash --no-keep-index &&
+ test bar,bar2 = $(cat file),$(cat file2)
+'
+
test_expect_success 'stash --invalid-option' '
echo bar5 > file &&
echo bar6 > file2 &&
@@ -254,7 +285,7 @@ test_expect_success 'stash rm and ignore' '
echo file >.gitignore &&
git stash save "rm and ignore" &&
test bar = "$(cat file)" &&
- test file = "$(cat .gitignore)"
+ test file = "$(cat .gitignore)" &&
git stash apply &&
! test -r file &&
test file = "$(cat .gitignore)"
@@ -267,7 +298,7 @@ test_expect_success 'stash rm and ignore (stage .gitignore)' '
git add .gitignore &&
git stash save "rm and ignore (stage .gitignore)" &&
test bar = "$(cat file)" &&
- ! test -r .gitignore
+ ! test -r .gitignore &&
git stash apply &&
! test -r file &&
test file = "$(cat .gitignore)"
@@ -309,41 +340,58 @@ test_expect_success SYMLINKS 'stash file to symlink (full stage)' '
# This test creates a commit with a symlink used for the following tests
-test_expect_success SYMLINKS 'stash symlink to file' '
+test_expect_success 'stash symlink to file' '
git reset --hard &&
- ln -s file filelink &&
- git add filelink &&
+ test_ln_s_add file filelink &&
git commit -m "Add symlink" &&
rm filelink &&
cp file filelink &&
- git stash save "symlink to file" &&
+ git stash save "symlink to file"
+'
+
+test_expect_success SYMLINKS 'this must have re-created the symlink' '
test -h filelink &&
- case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+ case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac
+'
+
+test_expect_success 'unstash must re-create the file' '
git stash apply &&
! test -h filelink &&
test bar = "$(cat file)"
'
-test_expect_success SYMLINKS 'stash symlink to file (stage rm)' '
+test_expect_success 'stash symlink to file (stage rm)' '
git reset --hard &&
git rm filelink &&
cp file filelink &&
- git stash save "symlink to file (stage rm)" &&
+ git stash save "symlink to file (stage rm)"
+'
+
+test_expect_success SYMLINKS 'this must have re-created the symlink' '
test -h filelink &&
- case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+ case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac
+'
+
+test_expect_success 'unstash must re-create the file' '
git stash apply &&
! test -h filelink &&
test bar = "$(cat file)"
'
-test_expect_success SYMLINKS 'stash symlink to file (full stage)' '
+test_expect_success 'stash symlink to file (full stage)' '
git reset --hard &&
rm filelink &&
cp file filelink &&
git add filelink &&
- git stash save "symlink to file (full stage)" &&
+ git stash save "symlink to file (full stage)"
+'
+
+test_expect_success SYMLINKS 'this must have re-created the symlink' '
test -h filelink &&
- case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+ case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac
+'
+
+test_expect_success 'unstash must re-create the file' '
git stash apply &&
! test -h filelink &&
test bar = "$(cat file)"
@@ -378,4 +426,309 @@ test_expect_failure 'stash file to directory' '
test foo = "$(cat file/file)"
'
+test_expect_success 'stash branch - no stashes on stack, stash-like argument' '
+ git stash clear &&
+ test_when_finished "git reset --hard HEAD" &&
+ git reset --hard &&
+ echo foo >> file &&
+ STASH_ID=$(git stash create) &&
+ git reset --hard &&
+ git stash branch stash-branch ${STASH_ID} &&
+ test_when_finished "git reset --hard HEAD && git checkout master && git branch -D stash-branch" &&
+ test $(git ls-files --modified | wc -l) -eq 1
+'
+
+test_expect_success 'stash branch - stashes on stack, stash-like argument' '
+ git stash clear &&
+ test_when_finished "git reset --hard HEAD" &&
+ git reset --hard &&
+ echo foo >> file &&
+ git stash &&
+ test_when_finished "git stash drop" &&
+ echo bar >> file &&
+ STASH_ID=$(git stash create) &&
+ git reset --hard &&
+ git stash branch stash-branch ${STASH_ID} &&
+ test_when_finished "git reset --hard HEAD && git checkout master && git branch -D stash-branch" &&
+ test $(git ls-files --modified | wc -l) -eq 1
+'
+
+test_expect_success 'stash show format defaults to --stat' '
+ git stash clear &&
+ test_when_finished "git reset --hard HEAD" &&
+ git reset --hard &&
+ echo foo >> file &&
+ git stash &&
+ test_when_finished "git stash drop" &&
+ echo bar >> file &&
+ STASH_ID=$(git stash create) &&
+ git reset --hard &&
+ cat >expected <<-EOF &&
+ file | 1 +
+ 1 file changed, 1 insertion(+)
+ EOF
+ git stash show ${STASH_ID} >actual &&
+ test_i18ncmp expected actual
+'
+
+test_expect_success 'stash show - stashes on stack, stash-like argument' '
+ git stash clear &&
+ test_when_finished "git reset --hard HEAD" &&
+ git reset --hard &&
+ echo foo >> file &&
+ git stash &&
+ test_when_finished "git stash drop" &&
+ echo bar >> file &&
+ STASH_ID=$(git stash create) &&
+ git reset --hard &&
+ echo "1 0 file" >expected &&
+ git stash show --numstat ${STASH_ID} >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'stash show -p - stashes on stack, stash-like argument' '
+ git stash clear &&
+ test_when_finished "git reset --hard HEAD" &&
+ git reset --hard &&
+ echo foo >> file &&
+ git stash &&
+ test_when_finished "git stash drop" &&
+ echo bar >> file &&
+ STASH_ID=$(git stash create) &&
+ git reset --hard &&
+ cat >expected <<-EOF &&
+ diff --git a/file b/file
+ index 7601807..935fbd3 100644
+ --- a/file
+ +++ b/file
+ @@ -1 +1,2 @@
+ baz
+ +bar
+ EOF
+ git stash show -p ${STASH_ID} >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'stash show - no stashes on stack, stash-like argument' '
+ git stash clear &&
+ test_when_finished "git reset --hard HEAD" &&
+ git reset --hard &&
+ echo foo >> file &&
+ STASH_ID=$(git stash create) &&
+ git reset --hard &&
+ echo "1 0 file" >expected &&
+ git stash show --numstat ${STASH_ID} >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'stash show -p - no stashes on stack, stash-like argument' '
+ git stash clear &&
+ test_when_finished "git reset --hard HEAD" &&
+ git reset --hard &&
+ echo foo >> file &&
+ STASH_ID=$(git stash create) &&
+ git reset --hard &&
+ cat >expected <<-EOF &&
+ diff --git a/file b/file
+ index 7601807..71b52c4 100644
+ --- a/file
+ +++ b/file
+ @@ -1 +1,2 @@
+ baz
+ +foo
+ EOF
+ git stash show -p ${STASH_ID} >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'stash drop - fail early if specified stash is not a stash reference' '
+ git stash clear &&
+ test_when_finished "git reset --hard HEAD && git stash clear" &&
+ git reset --hard &&
+ echo foo > file &&
+ git stash &&
+ echo bar > file &&
+ git stash &&
+ test_must_fail git stash drop $(git rev-parse stash@{0}) &&
+ git stash pop &&
+ test bar = "$(cat file)" &&
+ git reset --hard HEAD
+'
+
+test_expect_success 'stash pop - fail early if specified stash is not a stash reference' '
+ git stash clear &&
+ test_when_finished "git reset --hard HEAD && git stash clear" &&
+ git reset --hard &&
+ echo foo > file &&
+ git stash &&
+ echo bar > file &&
+ git stash &&
+ test_must_fail git stash pop $(git rev-parse stash@{0}) &&
+ git stash pop &&
+ test bar = "$(cat file)" &&
+ git reset --hard HEAD
+'
+
+test_expect_success 'ref with non-existent reflog' '
+ git stash clear &&
+ echo bar5 > file &&
+ echo bar6 > file2 &&
+ git add file2 &&
+ git stash &&
+ test_must_fail git rev-parse --quiet --verify does-not-exist &&
+ test_must_fail git stash drop does-not-exist &&
+ test_must_fail git stash drop does-not-exist@{0} &&
+ test_must_fail git stash pop does-not-exist &&
+ test_must_fail git stash pop does-not-exist@{0} &&
+ test_must_fail git stash apply does-not-exist &&
+ test_must_fail git stash apply does-not-exist@{0} &&
+ test_must_fail git stash show does-not-exist &&
+ test_must_fail git stash show does-not-exist@{0} &&
+ test_must_fail git stash branch tmp does-not-exist &&
+ test_must_fail git stash branch tmp does-not-exist@{0} &&
+ git stash drop
+'
+
+test_expect_success 'invalid ref of the form stash@{n}, n >= N' '
+ git stash clear &&
+ test_must_fail git stash drop stash@{0} &&
+ echo bar5 > file &&
+ echo bar6 > file2 &&
+ git add file2 &&
+ git stash &&
+ test_must_fail git stash drop stash@{1} &&
+ test_must_fail git stash pop stash@{1} &&
+ test_must_fail git stash apply stash@{1} &&
+ test_must_fail git stash show stash@{1} &&
+ test_must_fail git stash branch tmp stash@{1} &&
+ git stash drop
+'
+
+test_expect_success 'stash branch should not drop the stash if the branch exists' '
+ git stash clear &&
+ echo foo >file &&
+ git add file &&
+ git commit -m initial &&
+ echo bar >file &&
+ git stash &&
+ test_must_fail git stash branch master stash@{0} &&
+ git rev-parse stash@{0} --
+'
+
+test_expect_success 'stash apply shows status same as git status (relative to current directory)' '
+ git stash clear &&
+ echo 1 >subdir/subfile1 &&
+ echo 2 >subdir/subfile2 &&
+ git add subdir/subfile1 &&
+ git commit -m subdir &&
+ (
+ cd subdir &&
+ echo x >subfile1 &&
+ echo x >../file &&
+ git status >../expect &&
+ git stash &&
+ sane_unset GIT_MERGE_VERBOSITY &&
+ git stash apply
+ ) |
+ sed -e 1,2d >actual && # drop "Saved..." and "HEAD is now..."
+ test_i18ncmp expect actual
+'
+
+cat > expect << EOF
+diff --git a/HEAD b/HEAD
+new file mode 100644
+index 0000000..fe0cbee
+--- /dev/null
++++ b/HEAD
+@@ -0,0 +1 @@
++file-not-a-ref
+EOF
+
+test_expect_success 'stash where working directory contains "HEAD" file' '
+ git stash clear &&
+ git reset --hard &&
+ echo file-not-a-ref > HEAD &&
+ git add HEAD &&
+ test_tick &&
+ git stash &&
+ git diff-files --quiet &&
+ git diff-index --cached --quiet HEAD &&
+ test "$(git rev-parse stash^)" = "$(git rev-parse HEAD)" &&
+ git diff stash^..stash > output &&
+ test_cmp output expect
+'
+
+test_expect_success 'store called with invalid commit' '
+ test_must_fail git stash store foo
+'
+
+test_expect_success 'store updates stash ref and reflog' '
+ git stash clear &&
+ git reset --hard &&
+ echo quux >bazzy &&
+ git add bazzy &&
+ STASH_ID=$(git stash create) &&
+ git reset --hard &&
+ ! grep quux bazzy &&
+ git stash store -m quuxery $STASH_ID &&
+ test $(cat .git/refs/stash) = $STASH_ID &&
+ git reflog --format=%H stash| grep $STASH_ID &&
+ git stash pop &&
+ grep quux bazzy
+'
+
+test_expect_success 'handle stash specification with spaces' '
+ git stash clear &&
+ echo pig >file &&
+ git stash &&
+ stamp=$(git log -g --format="%cd" -1 refs/stash) &&
+ test_tick &&
+ echo cow >file &&
+ git stash &&
+ git stash apply "stash@{$stamp}" &&
+ grep pig file
+'
+
+test_expect_success 'setup stash with index and worktree changes' '
+ git stash clear &&
+ git reset --hard &&
+ echo index >file &&
+ git add file &&
+ echo working >file &&
+ git stash
+'
+
+test_expect_success 'stash list implies --first-parent -m' '
+ cat >expect <<-EOF &&
+ stash@{0}
+
+ diff --git a/file b/file
+ index 257cc56..d26b33d 100644
+ --- a/file
+ +++ b/file
+ @@ -1 +1 @@
+ -foo
+ +working
+ EOF
+ git stash list --format=%gd -p >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stash list --cc shows combined diff' '
+ cat >expect <<-\EOF &&
+ stash@{0}
+
+ diff --cc file
+ index 257cc56,9015a7a..d26b33d
+ --- a/file
+ +++ b/file
+ @@@ -1,1 -1,1 +1,1 @@@
+ - foo
+ -index
+ ++working
+ EOF
+ git stash list --format=%gd -p --cc >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh
index f37e3bc..38e7300 100755
--- a/t/t3904-stash-patch.sh
+++ b/t/t3904-stash-patch.sh
@@ -1,13 +1,20 @@
#!/bin/sh
-test_description='git checkout --patch'
+test_description='stash -p'
. ./lib-patch-mode.sh
+if ! test_have_prereq PERL
+then
+ skip_all='skipping stash -p tests, perl not available'
+ test_done
+fi
+
test_expect_success 'setup' '
mkdir dir &&
echo parent > dir/foo &&
echo dummy > bar &&
- git add bar dir/foo &&
+ echo committed > HEAD &&
+ git add bar dir/foo HEAD &&
git commit -m initial &&
test_tick &&
test_commit second dir/foo head &&
@@ -17,39 +24,84 @@ test_expect_success 'setup' '
save_head
'
-# note: bar sorts before dir, so the first 'n' is always to skip 'bar'
+# note: order of files with unstaged changes: HEAD bar dir/foo
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
+ set_state HEAD HEADfile_work HEADfile_index &&
+ set_state dir/foo work index &&
+ (echo n; echo n; echo n) | test_must_fail git stash save -p &&
+ verify_state HEAD HEADfile_work HEADfile_index &&
+ verify_saved_state bar &&
+ verify_state dir/foo work index
'
test_expect_success 'git stash -p' '
- (echo n; echo y) | git stash save -p &&
- verify_state dir/foo head index &&
+ (echo y; echo n; echo y) | git stash save -p &&
+ verify_state HEAD committed HEADfile_index &&
verify_saved_state bar &&
+ verify_state dir/foo head index &&
git reset --hard &&
git stash apply &&
- verify_state dir/foo work head &&
- verify_state bar dummy dummy
+ verify_state HEAD HEADfile_work committed &&
+ verify_state bar dummy dummy &&
+ verify_state dir/foo work head
'
test_expect_success 'git stash -p --no-keep-index' '
+ set_state HEAD HEADfile_work HEADfile_index &&
+ set_state bar bar_work bar_index &&
set_state dir/foo work index &&
+ (echo y; echo n; echo y) | git stash save -p --no-keep-index &&
+ verify_state HEAD committed committed &&
+ verify_state bar bar_work dummy &&
+ verify_state dir/foo head head &&
+ git reset --hard &&
+ git stash apply --index &&
+ verify_state HEAD HEADfile_work HEADfile_index &&
+ verify_state bar dummy bar_index &&
+ verify_state dir/foo work index
+'
+
+test_expect_success 'git stash --no-keep-index -p' '
+ set_state HEAD HEADfile_work HEADfile_index &&
set_state bar bar_work bar_index &&
- (echo n; echo y) | git stash save -p --no-keep-index &&
+ set_state dir/foo work index &&
+ (echo y; echo n; echo y) | git stash save --no-keep-index -p &&
+ verify_state HEAD committed committed &&
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
+ verify_state HEAD HEADfile_work HEADfile_index &&
+ verify_state bar dummy bar_index &&
+ verify_state dir/foo work index
'
test_expect_success 'none of this moved HEAD' '
verify_saved_head
'
+test_expect_failure 'stash -p with split hunk' '
+ git reset --hard &&
+ cat >test <<-\EOF &&
+ aaa
+ bbb
+ ccc
+ EOF
+ git add test &&
+ git commit -m "initial" &&
+ cat >test <<-\EOF &&
+ aaa
+ added line 1
+ bbb
+ added line 2
+ ccc
+ EOF
+ printf "%s\n" s n y q |
+ test_might_fail git stash -p 2>error &&
+ ! test_must_be_empty error &&
+ grep "added line 1" test &&
+ ! grep "added line 2" test
+'
+
test_done
diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh
new file mode 100755
index 0000000..f372fc8
--- /dev/null
+++ b/t/t3905-stash-include-untracked.sh
@@ -0,0 +1,188 @@
+#!/bin/sh
+#
+# Copyright (c) 2011 David Caldwell
+#
+
+test_description='Test git stash --include-untracked'
+
+. ./test-lib.sh
+
+test_expect_success 'stash save --include-untracked 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 &&
+ echo 1 > file2 &&
+ echo 1 > HEAD &&
+ mkdir untracked &&
+ echo untracked >untracked/untracked &&
+ git stash --include-untracked &&
+ git diff-files --quiet &&
+ git diff-index --cached --quiet HEAD
+'
+
+cat > expect <<EOF
+?? actual
+?? expect
+EOF
+
+test_expect_success 'stash save --include-untracked cleaned the untracked files' '
+ git status --porcelain >actual &&
+ test_cmp expect actual
+'
+
+cat > expect.diff <<EOF
+diff --git a/HEAD b/HEAD
+new file mode 100644
+index 0000000..d00491f
+--- /dev/null
++++ b/HEAD
+@@ -0,0 +1 @@
++1
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..d00491f
+--- /dev/null
++++ b/file2
+@@ -0,0 +1 @@
++1
+diff --git a/untracked/untracked b/untracked/untracked
+new file mode 100644
+index 0000000..5a72eb2
+--- /dev/null
++++ b/untracked/untracked
+@@ -0,0 +1 @@
++untracked
+EOF
+cat > expect.lstree <<EOF
+HEAD
+file2
+untracked
+EOF
+
+test_expect_success 'stash save --include-untracked stashed the untracked files' '
+ test_path_is_missing file2 &&
+ test_path_is_missing untracked &&
+ test_path_is_missing HEAD &&
+ git diff HEAD stash^3 -- HEAD file2 untracked >actual &&
+ test_cmp expect.diff actual &&
+ git ls-tree --name-only stash^3: >actual &&
+ test_cmp expect.lstree actual
+'
+test_expect_success 'stash save --patch --include-untracked fails' '
+ test_must_fail git stash --patch --include-untracked
+'
+
+test_expect_success 'stash save --patch --all fails' '
+ test_must_fail git stash --patch --all
+'
+
+git clean --force --quiet
+
+cat > expect <<EOF
+ M file
+?? HEAD
+?? actual
+?? expect
+?? file2
+?? untracked/
+EOF
+
+test_expect_success 'stash pop after save --include-untracked leaves files untracked again' '
+ git stash pop &&
+ git status --porcelain >actual &&
+ test_cmp expect actual &&
+ test "1" = "$(cat file2)" &&
+ test untracked = "$(cat untracked/untracked)"
+'
+
+git clean --force --quiet -d
+
+test_expect_success 'stash save -u dirty index' '
+ echo 4 > file3 &&
+ git add file3 &&
+ test_tick &&
+ git stash -u
+'
+
+cat > expect <<EOF
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..b8626c4
+--- /dev/null
++++ b/file3
+@@ -0,0 +1 @@
++4
+EOF
+
+test_expect_success 'stash save --include-untracked dirty index got stashed' '
+ git stash pop --index &&
+ git diff --cached >actual &&
+ test_cmp expect actual
+'
+
+git reset > /dev/null
+
+# Must direct output somewhere where it won't be considered an untracked file
+test_expect_success 'stash save --include-untracked -q is quiet' '
+ echo 1 > file5 &&
+ git stash save --include-untracked --quiet > .git/stash-output.out 2>&1 &&
+ test_line_count = 0 .git/stash-output.out &&
+ rm -f .git/stash-output.out
+'
+
+test_expect_success 'stash save --include-untracked removed files' '
+ rm -f file &&
+ git stash save --include-untracked &&
+ echo 1 > expect &&
+ test_cmp file expect
+'
+
+rm -f expect
+
+test_expect_success 'stash save --include-untracked removed files got stashed' '
+ git stash pop &&
+ test_path_is_missing file
+'
+
+cat > .gitignore <<EOF
+.gitignore
+ignored
+ignored.d/
+EOF
+
+test_expect_success 'stash save --include-untracked respects .gitignore' '
+ echo ignored > ignored &&
+ mkdir ignored.d &&
+ echo ignored >ignored.d/untracked &&
+ git stash -u &&
+ test -s ignored &&
+ test -s ignored.d/untracked &&
+ test -s .gitignore
+'
+
+test_expect_success 'stash save -u can stash with only untracked files different' '
+ echo 4 > file4 &&
+ git stash -u &&
+ test_path_is_missing file4
+'
+
+test_expect_success 'stash save --all does not respect .gitignore' '
+ git stash -a &&
+ test_path_is_missing ignored &&
+ test_path_is_missing ignored.d &&
+ test_path_is_missing .gitignore
+'
+
+test_expect_success 'stash save --all is stash poppable' '
+ git stash pop &&
+ test -s ignored &&
+ test -s ignored.d/untracked &&
+ test -s .gitignore
+'
+
+test_done
diff --git a/t/t3906-stash-submodule.sh b/t/t3906-stash-submodule.sh
new file mode 100755
index 0000000..d7219d6
--- /dev/null
+++ b/t/t3906-stash-submodule.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='stash apply can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+git_stash () {
+ git status -su >expect &&
+ ls -1pR * >>expect &&
+ git read-tree -u -m "$1" &&
+ git stash &&
+ git status -su >actual &&
+ ls -1pR * >>actual &&
+ test_cmp expect actual &&
+ git stash apply
+}
+
+KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES=1
+KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+test_submodule_switch "git_stash"
+
+test_done
diff --git a/t/t3910-mac-os-precompose.sh b/t/t3910-mac-os-precompose.sh
new file mode 100755
index 0000000..26dd5b7
--- /dev/null
+++ b/t/t3910-mac-os-precompose.sh
@@ -0,0 +1,205 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Torsten Bögershausen
+#
+
+test_description='utf-8 decomposed (nfd) converted to precomposed (nfc)'
+
+. ./test-lib.sh
+
+if ! test_have_prereq UTF8_NFD_TO_NFC
+then
+ skip_all="filesystem does not corrupt utf-8"
+ test_done
+fi
+
+# create utf-8 variables
+Adiarnfc=$(printf '\303\204')
+Adiarnfd=$(printf 'A\314\210')
+
+Odiarnfc=$(printf '\303\226')
+Odiarnfd=$(printf 'O\314\210')
+AEligatu=$(printf '\303\206')
+Invalidu=$(printf '\303\377')
+
+
+#Create a string with 255 bytes (decomposed)
+Alongd=$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd #21 Byte
+Alongd=$Alongd$Alongd$Alongd #63 Byte
+Alongd=$Alongd$Alongd$Alongd$Alongd$Adiarnfd #255 Byte
+
+#Create a string with 254 bytes (precomposed)
+Alongc=$AEligatu$AEligatu$AEligatu$AEligatu$AEligatu #10 Byte
+Alongc=$Alongc$Alongc$Alongc$Alongc$Alongc #50 Byte
+Alongc=$Alongc$Alongc$Alongc$Alongc$Alongc #250 Byte
+Alongc=$Alongc$AEligatu$AEligatu #254 Byte
+
+test_expect_success "detect if nfd needed" '
+ precomposeunicode=$(git config core.precomposeunicode) &&
+ test "$precomposeunicode" = true &&
+ git config core.precomposeunicode true
+'
+test_expect_success "setup" '
+ >x &&
+ git add x &&
+ git commit -m "1st commit" &&
+ git rm x &&
+ git commit -m "rm x"
+'
+test_expect_success "setup case mac" '
+ git checkout -b mac_os
+'
+# This will test nfd2nfc in git diff
+test_expect_success "git diff f.Adiar" '
+ touch f.$Adiarnfc &&
+ git add f.$Adiarnfc &&
+ echo f.Adiarnfc >f.$Adiarnfc &&
+ git diff f.$Adiarnfd >expect &&
+ git diff f.$Adiarnfc >actual &&
+ test_cmp expect actual &&
+ git reset HEAD f.Adiarnfc &&
+ rm f.$Adiarnfc expect actual
+'
+# This will test nfd2nfc in git diff-files
+test_expect_success "git diff-files f.Adiar" '
+ touch f.$Adiarnfc &&
+ git add f.$Adiarnfc &&
+ echo f.Adiarnfc >f.$Adiarnfc &&
+ git diff-files f.$Adiarnfd >expect &&
+ git diff-files f.$Adiarnfc >actual &&
+ test_cmp expect actual &&
+ git reset HEAD f.Adiarnfc &&
+ rm f.$Adiarnfc expect actual
+'
+# This will test nfd2nfc in git diff-index
+test_expect_success "git diff-index f.Adiar" '
+ touch f.$Adiarnfc &&
+ git add f.$Adiarnfc &&
+ echo f.Adiarnfc >f.$Adiarnfc &&
+ git diff-index HEAD f.$Adiarnfd >expect &&
+ git diff-index HEAD f.$Adiarnfc >actual &&
+ test_cmp expect actual &&
+ git reset HEAD f.Adiarnfc &&
+ rm f.$Adiarnfc expect actual
+'
+# This will test nfd2nfc in readdir()
+test_expect_success "add file Adiarnfc" '
+ echo f.Adiarnfc >f.$Adiarnfc &&
+ git add f.$Adiarnfc &&
+ git commit -m "add f.$Adiarnfc"
+'
+# This will test nfd2nfc in git diff-tree
+test_expect_success "git diff-tree f.Adiar" '
+ echo f.Adiarnfc >>f.$Adiarnfc &&
+ git diff-tree HEAD f.$Adiarnfd >expect &&
+ git diff-tree HEAD f.$Adiarnfc >actual &&
+ test_cmp expect actual &&
+ git checkout f.$Adiarnfc &&
+ rm expect actual
+'
+# This will test nfd2nfc in git stage()
+test_expect_success "stage file d.Adiarnfd/f.Adiarnfd" '
+ mkdir d.$Adiarnfd &&
+ echo d.$Adiarnfd/f.$Adiarnfd >d.$Adiarnfd/f.$Adiarnfd &&
+ git stage d.$Adiarnfd/f.$Adiarnfd &&
+ git commit -m "add d.$Adiarnfd/f.$Adiarnfd"
+'
+test_expect_success "add link Adiarnfc" '
+ ln -s d.$Adiarnfd/f.$Adiarnfd l.$Adiarnfc &&
+ git add l.$Adiarnfc &&
+ git commit -m "add l.Adiarnfc"
+'
+# This will test git log
+test_expect_success "git log f.Adiar" '
+ git log f.$Adiarnfc > f.Adiarnfc.log &&
+ git log f.$Adiarnfd > f.Adiarnfd.log &&
+ test -s f.Adiarnfc.log &&
+ test -s f.Adiarnfd.log &&
+ test_cmp f.Adiarnfc.log f.Adiarnfd.log &&
+ rm f.Adiarnfc.log f.Adiarnfd.log
+'
+# This will test git ls-files
+test_expect_success "git lsfiles f.Adiar" '
+ git ls-files f.$Adiarnfc > f.Adiarnfc.log &&
+ git ls-files f.$Adiarnfd > f.Adiarnfd.log &&
+ test -s f.Adiarnfc.log &&
+ test -s f.Adiarnfd.log &&
+ test_cmp f.Adiarnfc.log f.Adiarnfd.log &&
+ rm f.Adiarnfc.log f.Adiarnfd.log
+'
+# This will test git mv
+test_expect_success "git mv" '
+ git mv f.$Adiarnfd f.$Odiarnfc &&
+ git mv d.$Adiarnfd d.$Odiarnfc &&
+ git mv l.$Adiarnfd l.$Odiarnfc &&
+ git commit -m "mv Adiarnfd Odiarnfc"
+'
+# Files can be checked out as nfc
+# And the link has been corrected from nfd to nfc
+test_expect_success "git checkout nfc" '
+ rm f.$Odiarnfc &&
+ git checkout f.$Odiarnfc
+'
+# Make it possible to checkout files with their NFD names
+test_expect_success "git checkout file nfd" '
+ rm -f f.* &&
+ git checkout f.$Odiarnfd
+'
+# Make it possible to checkout links with their NFD names
+test_expect_success "git checkout link nfd" '
+ rm l.* &&
+ git checkout l.$Odiarnfd
+'
+test_expect_success "setup case mac2" '
+ git checkout master &&
+ git reset --hard &&
+ git checkout -b mac_os_2
+'
+# This will test nfd2nfc in git commit
+test_expect_success "commit file d2.Adiarnfd/f.Adiarnfd" '
+ mkdir d2.$Adiarnfd &&
+ echo d2.$Adiarnfd/f.$Adiarnfd >d2.$Adiarnfd/f.$Adiarnfd &&
+ git add d2.$Adiarnfd/f.$Adiarnfd &&
+ git commit -m "add d2.$Adiarnfd/f.$Adiarnfd" -- d2.$Adiarnfd/f.$Adiarnfd
+'
+test_expect_success "setup for long decomposed filename" '
+ git checkout master &&
+ git reset --hard &&
+ git checkout -b mac_os_long_nfd_fn
+'
+test_expect_success "Add long decomposed filename" '
+ echo longd >$Alongd &&
+ git add * &&
+ git commit -m "Long filename"
+'
+test_expect_success "setup for long precomposed filename" '
+ git checkout master &&
+ git reset --hard &&
+ git checkout -b mac_os_long_nfc_fn
+'
+test_expect_success "Add long precomposed filename" '
+ echo longc >$Alongc &&
+ git add * &&
+ git commit -m "Long filename"
+'
+
+test_expect_failure 'handle existing decomposed filenames' '
+ echo content >"verbatim.$Adiarnfd" &&
+ git -c core.precomposeunicode=false add "verbatim.$Adiarnfd" &&
+ git commit -m "existing decomposed file" &&
+ >expect &&
+ git ls-files --exclude-standard -o "verbatim*" >untracked &&
+ test_cmp expect untracked
+'
+
+# Test if the global core.precomposeunicode stops autosensing
+# Must be the last test case
+test_expect_success "respect git config --global core.precomposeunicode" '
+ git config --global core.precomposeunicode true &&
+ rm -rf .git &&
+ git init &&
+ precomposeunicode=$(git config core.precomposeunicode) &&
+ test "$precomposeunicode" = "true"
+'
+
+test_done
diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh
index 6ddd469..8de36b7 100755
--- a/t/t4000-diff-format.sh
+++ b/t/t4000-diff-format.sh
@@ -15,17 +15,17 @@ line 3'
cat path0 >path1
chmod +x path1
-test_expect_success \
- 'update-index --add two files with and without +x.' \
- 'git update-index --add path0 path1'
+test_expect_success 'update-index --add two files with and without +x.' '
+ git update-index --add path0 path1
+'
mv path0 path0-
sed -e 's/line/Line/' <path0- >path0
chmod +x path0
rm -f path1
-test_expect_success \
- 'git diff-files -p after editing work tree.' \
- 'git diff-files -p >current'
+test_expect_success 'git diff-files -p after editing work tree.' '
+ git diff-files -p >actual
+'
# that's as far as it comes
if [ "$(git config --get core.filemode)" = false ]
@@ -55,8 +55,38 @@ deleted file mode 100755
-line 3
EOF
-test_expect_success \
- 'validate git diff-files -p output.' \
- 'compare_diff_patch current expected'
+test_expect_success 'validate git diff-files -p output.' '
+ compare_diff_patch expected actual
+'
+
+test_expect_success 'git diff-files -s after editing work tree' '
+ git diff-files -s >actual 2>err &&
+ test_must_be_empty actual &&
+ test_must_be_empty err
+'
+
+test_expect_success 'git diff-files --no-patch as synonym for -s' '
+ git diff-files --no-patch >actual 2>err &&
+ test_must_be_empty actual &&
+ test_must_be_empty err
+'
+
+test_expect_success 'git diff-files --no-patch --patch shows the patch' '
+ git diff-files --no-patch --patch >actual &&
+ compare_diff_patch expected actual
+'
+
+test_expect_success 'git diff-files --no-patch --patch-with-raw shows the patch and raw data' '
+ git diff-files --no-patch --patch-with-raw >actual &&
+ grep -q "^:100644 100755 .* 0000000000000000000000000000000000000000 M path0\$" actual &&
+ tail -n +4 actual >actual-patch &&
+ compare_diff_patch expected actual-patch
+'
+
+test_expect_success 'git diff-files --patch --no-patch does not show the patch' '
+ git diff-files --patch --no-patch >actual 2>err &&
+ test_must_be_empty actual &&
+ test_must_be_empty err
+'
test_done
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index 71bac83..0d1fa45 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -9,21 +9,84 @@ 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 'setup' '
+ cat >path0 <<-\EOF &&
+ 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
+ EOF
+ 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
+ cat >no-rename <<-\EOF
+ diff --git a/path0 b/path0
+ deleted file mode 100644
+ index fdbec44..0000000
+ --- a/path0
+ +++ /dev/null
+ @@ -1,15 +0,0 @@
+ -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
+ diff --git a/path1 b/path1
+ new file mode 100644
+ index 0000000..752c50e
+ --- /dev/null
+ +++ b/path1
+ @@ -0,0 +1,15 @@
+ +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
+ EOF
'
test_expect_success \
@@ -43,27 +106,27 @@ test_expect_success \
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 'test diff.renames=true' '
+ git -c diff.renames=true diff --cached $tree >current &&
+ compare_diff_patch current expected
+'
+
+test_expect_success 'test diff.renames=false' '
+ git -c diff.renames=false diff --cached $tree >current &&
+ compare_diff_patch current no-rename
+'
+
+test_expect_success 'test diff.renames unset' '
+ git diff --cached $tree >current &&
+ compare_diff_patch current expected
+'
+
test_expect_success 'favour same basenames over different ones' '
cp path1 another-path &&
git add another-path &&
@@ -71,10 +134,100 @@ test_expect_success 'favour same basenames over different ones' '
git rm path1 &&
mkdir subdir &&
git mv another-path subdir/path1 &&
- git status | grep "renamed: .*path1 -> subdir/path1"'
+ git status | test_i18ngrep "renamed: .*path1 -> subdir/path1"'
-test_expect_success 'favour same basenames even with minor differences' '
+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"'
+ git status | test_i18ngrep "renamed: .*path1 -> subdir/path1"'
+
+test_expect_success 'two files with same basename and same content' '
+ git reset --hard &&
+ mkdir -p dir/A dir/B &&
+ cp path1 dir/A/file &&
+ cp path1 dir/B/file &&
+ git add dir &&
+ git commit -m 2 &&
+ git mv dir other-dir &&
+ git status | test_i18ngrep "renamed: .*dir/A/file -> other-dir/A/file"
+'
+
+test_expect_success 'setup for many rename source candidates' '
+ git reset --hard &&
+ 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
+ echo "$i$j" >"path$i$j"
+ done
+ done &&
+ git add "path??" &&
+ test_tick &&
+ git commit -m "hundred" &&
+ (cat path1; echo new) >new-path &&
+ echo old >>path1 &&
+ git add new-path path1 &&
+ git diff -l 4 -C -C --cached --name-status >actual 2>actual.err &&
+ sed -e "s/^\([CM]\)[0-9]* /\1 /" actual >actual.munged &&
+ cat >expect <<-EOF &&
+ C path1 new-path
+ M path1
+ EOF
+ test_cmp expect actual.munged &&
+ grep warning actual.err
+'
+
+test_expect_success 'rename pretty print with nothing in common' '
+ mkdir -p a/b/ &&
+ : >a/b/c &&
+ git add a/b/c &&
+ git commit -m "create a/b/c" &&
+ mkdir -p c/b/ &&
+ git mv a/b/c c/b/a &&
+ git commit -m "a/b/c -> c/b/a" &&
+ git diff -M --summary HEAD^ HEAD >output &&
+ test_i18ngrep " a/b/c => c/b/a " output &&
+ git diff -M --stat HEAD^ HEAD >output &&
+ test_i18ngrep " a/b/c => c/b/a " output
+'
+
+test_expect_success 'rename pretty print with common prefix' '
+ mkdir -p c/d &&
+ git mv c/b/a c/d/e &&
+ git commit -m "c/b/a -> c/d/e" &&
+ git diff -M --summary HEAD^ HEAD >output &&
+ test_i18ngrep " c/{b/a => d/e} " output &&
+ git diff -M --stat HEAD^ HEAD >output &&
+ test_i18ngrep " c/{b/a => d/e} " output
+'
+
+test_expect_success 'rename pretty print with common suffix' '
+ mkdir d &&
+ git mv c/d/e d/e &&
+ git commit -m "c/d/e -> d/e" &&
+ git diff -M --summary HEAD^ HEAD >output &&
+ test_i18ngrep " {c/d => d}/e " output &&
+ git diff -M --stat HEAD^ HEAD >output &&
+ test_i18ngrep " {c/d => d}/e " output
+'
+
+test_expect_success 'rename pretty print with common prefix and suffix' '
+ mkdir d/f &&
+ git mv d/e d/f/e &&
+ git commit -m "d/e -> d/f/e" &&
+ git diff -M --summary HEAD^ HEAD >output &&
+ test_i18ngrep " d/{ => f}/e " output &&
+ git diff -M --stat HEAD^ HEAD >output &&
+ test_i18ngrep " d/{ => f}/e " output
+'
+
+test_expect_success 'rename pretty print common prefix and suffix overlap' '
+ mkdir d/f/f &&
+ git mv d/f/e d/f/f/e &&
+ git commit -m "d/f/e d/f/f/e" &&
+ git diff -M --summary HEAD^ HEAD >output &&
+ test_i18ngrep " d/f/{ => f}/e " output &&
+ git diff -M --stat HEAD^ HEAD >output &&
+ test_i18ngrep " d/f/{ => f}/e " output
+'
test_done
diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh
index 73441a5..a5e8b83 100755
--- a/t/t4002-diff-basic.sh
+++ b/t/t4002-diff-basic.sh
@@ -126,15 +126,12 @@ cat >.test-recursive-AB <<\EOF
: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 /' &&
+ -e '/^:000000 /d;s/'$_x40'\( [MCRNDU][0-9]*\) /'$_z40'\1 /' &&
test_cmp "$1" .test-tmp
}
@@ -205,8 +202,8 @@ test_expect_success \
'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 read-tree --reset $tree_O &&
+ test_must_fail git update-index --refresh -q &&
git diff-files >.test-a &&
cmp_diff_files_output .test-a .test-recursive-OA'
@@ -215,8 +212,8 @@ test_expect_success \
'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 read-tree --reset $tree_O &&
+ test_must_fail git update-index --refresh -q &&
git diff-files >.test-a &&
cmp_diff_files_output .test-a .test-recursive-OB'
@@ -225,8 +222,8 @@ test_expect_success \
'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 read-tree --reset $tree_A &&
+ test_must_fail git update-index --refresh -q &&
git diff-files >.test-a &&
cmp_diff_files_output .test-a .test-recursive-AB'
diff --git a/t/t4003-diff-rename-1.sh b/t/t4003-diff-rename-1.sh
index c6130c4..df2accb 100755
--- a/t/t4003-diff-rename-1.sh
+++ b/t/t4003-diff-rename-1.sh
@@ -11,7 +11,7 @@ test_description='More rename detection
test_expect_success \
'prepare reference tree' \
- 'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
+ 'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
echo frotz >rezrov &&
git update-index --add COPYING rezrov &&
tree=$(git write-tree) &&
@@ -29,7 +29,7 @@ test_expect_success \
# 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
+GIT_DIFF_OPTS=--unified=0 git diff-index -C -p $tree >current
cat >expected <<\EOF
diff --git a/COPYING b/COPYING.1
copy from COPYING
@@ -99,7 +99,7 @@ test_expect_success \
test_expect_success \
'prepare work tree once again' \
- 'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
+ 'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
git update-index --add --remove COPYING COPYING.1'
# tree has COPYING and rezrov. work tree has COPYING and COPYING.1,
diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh
index 1a09e8d..6e562c8 100755
--- a/t/t4004-diff-rename-symlink.sh
+++ b/t/t4004-diff-rename-symlink.sh
@@ -12,13 +12,7 @@ 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 \
+test_expect_success SYMLINKS \
'prepare reference tree' \
'echo xyzzy | tr -d '\\\\'012 >yomin &&
ln -s xyzzy frotz &&
@@ -26,7 +20,7 @@ test_expect_success \
tree=$(git write-tree) &&
echo $tree'
-test_expect_success \
+test_expect_success SYMLINKS \
'prepare work tree' \
'mv frotz rezrov &&
rm -f yomin &&
@@ -40,8 +34,9 @@ test_expect_success \
# 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
+test_expect_success SYMLINKS 'setup diff output' "
+ GIT_DIFF_OPTS=--unified=0 git diff-index -C -p $tree >current &&
+ cat >expected <<\EOF
diff --git a/bozbar b/bozbar
new file mode 120000
--- /dev/null
@@ -65,8 +60,9 @@ deleted file mode 100644
-xyzzy
\ No newline at end of file
EOF
+"
-test_expect_success \
+test_expect_success SYMLINKS \
'validate diff output' \
'compare_diff_patch current expected'
diff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh
index 1ba359d..135addb 100755
--- a/t/t4005-diff-rename-2.sh
+++ b/t/t4005-diff-rename-2.sh
@@ -11,7 +11,7 @@ test_description='Same rename detection as t4003 but testing diff-raw.
test_expect_success \
'prepare reference tree' \
- 'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
+ 'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
echo frotz >rezrov &&
git update-index --add COPYING rezrov &&
tree=$(git write-tree) &&
@@ -29,7 +29,7 @@ test_expect_success \
# and COPYING.2 are based on COPYING, and do not say anything about
# rezrov.
-git diff-index -M $tree >current
+git diff-index -C $tree >current
cat >expected <<\EOF
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 COPYING COPYING.1
@@ -66,12 +66,12 @@ test_expect_success \
# 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
+# anything about rezrov or 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 &&
+ 'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
git update-index --add --remove COPYING COPYING.1'
git diff-index -C --find-copies-harder $tree >current
diff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh
index ff8c2f7..76f643b 100755
--- a/t/t4006-diff-mode.sh
+++ b/t/t4006-diff-mode.sh
@@ -8,23 +8,52 @@ test_description='Test mode change diffs.
'
. ./test-lib.sh
-test_expect_success \
- 'setup' \
- 'echo frotz >rezrov &&
- git update-index --add rezrov &&
- tree=`git write-tree` &&
- echo $tree'
-
-test_expect_success \
- 'chmod' \
- 'test_chmod +x rezrov &&
- git diff-index $tree >current'
-
-sed -e 's/\(:100644 100755\) \('"$_x40"'\) \2 /\1 X X /' <current >check
-echo ":100644 100755 X X M rezrov" >expected
-
-test_expect_success \
- 'verify' \
- 'test_cmp expected check'
+sed_script='s/\(:100644 100755\) \('"$_x40"'\) \2 /\1 X X /'
+
+test_expect_success 'setup' '
+ echo frotz >rezrov &&
+ git update-index --add rezrov &&
+ tree=$(git write-tree) &&
+ echo $tree
+'
+
+test_expect_success 'chmod' '
+ test_chmod +x rezrov &&
+ git diff-index $tree >current &&
+ sed -e "$sed_script" <current >check &&
+ echo ":100644 100755 X X M rezrov" >expected &&
+ test_cmp expected check
+'
+
+test_expect_success 'prepare binary file' '
+ git commit -m rezrov &&
+ printf "\00\01\02\03\04\05\06" >binbin &&
+ git add binbin &&
+ git commit -m binbin
+'
+
+# test_expect_success '--stat output after text chmod' '
+# test_chmod -x rezrov &&
+# echo " 0 files changed" >expect &&
+# git diff HEAD --stat >actual &&
+# test_i18ncmp expect actual
+# '
+#
+# test_expect_success '--shortstat output after text chmod' '
+# git diff HEAD --shortstat >actual &&
+# test_i18ncmp expect actual
+# '
+#
+# test_expect_success '--stat output after binary chmod' '
+# test_chmod +x binbin &&
+# echo " 0 files changed" >expect &&
+# git diff HEAD --stat >actual &&
+# test_i18ncmp expect actual
+# '
+#
+# test_expect_success '--shortstat output after binary chmod' '
+# git diff HEAD --shortstat >actual &&
+# test_i18ncmp expect actual
+# '
test_done
diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh
index 11502b7..dae327f 100755
--- a/t/t4007-rename-3.sh
+++ b/t/t4007-rename-3.sh
@@ -11,7 +11,7 @@ test_description='Rename interaction with pathspec.
test_expect_success 'prepare reference tree' '
mkdir path0 path1 &&
- cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
+ cp "$TEST_DIRECTORY"/diff-lib/COPYING path0/COPYING &&
git update-index --add path0/COPYING &&
tree=$(git write-tree) &&
echo $tree
diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh
index e19ca65..9dd1bc5 100755
--- a/t/t4008-diff-break-rewrite.sh
+++ b/t/t4008-diff-break-rewrite.sh
@@ -10,179 +10,145 @@ 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.
+original file1 and creation of completely rewritten file1. The latter
+two are then merged back into a single "complete rewrite".
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.
+With -B, this should be detected as two complete rewrites.
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_expect_success setup '
+ cat "$TEST_DIRECTORY"/diff-lib/README >file0 &&
+ cat "$TEST_DIRECTORY"/diff-lib/COPYING >file1 &&
+ git update-index --add file0 file1 &&
+ git tag reference $(git write-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 (#1)' '
+ git diff-index -B --cached reference >current &&
+ cat >expect <<-\EOF &&
+ :100644 000000 548142c327a6790ff8821d67c2ee1eff7a656b52 0000000000000000000000000000000000000000 D file0
+ :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 2fbedd0b5d4b8126e4750c3bee305e8ff79f80ec M100 file1
+ EOF
+ compare_diff_raw expect current
+'
+
+test_expect_success 'run diff with -B and -M (#2)' '
+ git diff-index -B -M reference >current &&
+ cat >expect <<-\EOF &&
+ :100644 100644 548142c327a6790ff8821d67c2ee1eff7a656b52 2fbedd0b5d4b8126e4750c3bee305e8ff79f80ec R100 file0 file1
+ EOF
+ compare_diff_raw expect current
+'
+
+test_expect_success 'swap file0 and file1' '
+ rm -f file0 file1 &&
+ git read-tree -m reference &&
+ 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 (#3)' '
+ git diff-index -B reference >current &&
+ cat >expect <<-\EOF &&
+ :100644 100644 548142c327a6790ff8821d67c2ee1eff7a656b52 6ff87c4664981e4397625791c8ea3bbb5f2279a3 M100 file0
+ :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 548142c327a6790ff8821d67c2ee1eff7a656b52 M100 file1
+ EOF
+ compare_diff_raw expect current
+'
+
+test_expect_success 'run diff with -B and -M (#4)' '
+ git diff-index -B -M reference >current &&
+ cat >expect <<-\EOF &&
+ :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100 file1 file0
+ :100644 100644 548142c327a6790ff8821d67c2ee1eff7a656b52 548142c327a6790ff8821d67c2ee1eff7a656b52 R100 file0 file1
+ EOF
+ compare_diff_raw expect current
+'
+
+test_expect_success 'make file0 into something completely different' '
+ rm -f file0 &&
+ test_ln_s_add frotz file0 &&
+ git update-index file1
+'
+
+test_expect_success 'run diff with -B (#5)' '
+ git diff-index -B reference >current &&
+ cat >expect <<-\EOF &&
+ :100644 120000 548142c327a6790ff8821d67c2ee1eff7a656b52 67be421f88824578857624f7b3dc75e99a8a1481 T file0
+ :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 548142c327a6790ff8821d67c2ee1eff7a656b52 M100 file1
+ EOF
+ compare_diff_raw expect current
+'
+
+test_expect_success 'run diff with -B -M (#6)' '
+ git diff-index -B -M reference >current &&
+
+ # file0 changed from regular to symlink. file1 is the same as the preimage
+ # of file0. Because the change does not make file0 disappear, file1 is
+ # denoted as a copy of file0
+ cat >expect <<-\EOF &&
+ :100644 120000 548142c327a6790ff8821d67c2ee1eff7a656b52 67be421f88824578857624f7b3dc75e99a8a1481 T file0
+ :100644 100644 548142c327a6790ff8821d67c2ee1eff7a656b52 548142c327a6790ff8821d67c2ee1eff7a656b52 C file0 file1
+ EOF
+ compare_diff_raw expect current
+'
+
+test_expect_success 'run diff with -M (#7)' '
+ git diff-index -M reference >current &&
+
+ # This should not mistake file0 as the copy source of new file1
+ # due to type differences.
+ cat >expect <<-\EOF &&
+ :100644 120000 548142c327a6790ff8821d67c2ee1eff7a656b52 67be421f88824578857624f7b3dc75e99a8a1481 T file0
+ :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 548142c327a6790ff8821d67c2ee1eff7a656b52 M file1
+ EOF
+ compare_diff_raw expect current
+'
+
+test_expect_success 'file1 edited to look like file0 and file0 rename-edited to file2' '
+ rm -f file0 file1 &&
+ git read-tree -m reference &&
+ 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 (#8)' '
+ git diff-index -B reference >current &&
+ cat >expect <<-\EOF &&
+ :100644 000000 548142c327a6790ff8821d67c2ee1eff7a656b52 0000000000000000000000000000000000000000 D file0
+ :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 2fbedd0b5d4b8126e4750c3bee305e8ff79f80ec M100 file1
+ :000000 100644 0000000000000000000000000000000000000000 69a939f651686f56322566e2fd76715947a24162 A file2
+ EOF
+ compare_diff_raw expect current
+'
+
+test_expect_success 'run diff with -B -C (#9)' '
+ git diff-index -B -C reference >current &&
+ cat >expect <<-\EOF &&
+ :100644 100644 548142c327a6790ff8821d67c2ee1eff7a656b52 2fbedd0b5d4b8126e4750c3bee305e8ff79f80ec C095 file0 file1
+ :100644 100644 548142c327a6790ff8821d67c2ee1eff7a656b52 69a939f651686f56322566e2fd76715947a24162 R095 file0 file2
+ EOF
+ compare_diff_raw expect current
+'
test_done
diff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh
index de3f174..3641fd8 100755
--- a/t/t4009-diff-rename-4.sh
+++ b/t/t4009-diff-rename-4.sh
@@ -11,7 +11,7 @@ test_description='Same rename detection as t4003 but testing diff-raw -z.
test_expect_success \
'prepare reference tree' \
- 'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
+ 'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
echo frotz >rezrov &&
git update-index --add COPYING rezrov &&
tree=$(git write-tree) &&
@@ -29,7 +29,7 @@ test_expect_success \
# and COPYING.2 are based on COPYING, and do not say anything about
# rezrov.
-git diff-index -z -M $tree >current
+git diff-index -z -C $tree >current
cat >expected <<\EOF
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234
@@ -73,12 +73,12 @@ test_expect_success \
# 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
+# anything about rezrov or 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 &&
+ 'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
git update-index --add --remove COPYING COPYING.1'
git diff-index -z -C --find-copies-harder $tree >current
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
index 94df7ae..43c488b 100755
--- a/t/t4010-diff-pathspec.sh
+++ b/t/t4010-diff-pathspec.sh
@@ -18,7 +18,7 @@ test_expect_success \
mkdir path1 &&
echo rezrov >path1/file1 &&
git update-index --add file0 path1/file1 &&
- tree=`git write-tree` &&
+ tree=$(git write-tree) &&
echo "$tree" &&
echo nitfol >file0 &&
echo yomin >path1/file1 &&
@@ -48,7 +48,15 @@ test_expect_success \
compare_diff_raw current expected'
cat >expected <<\EOF
-:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M file0
+:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M path1/file1
+EOF
+test_expect_success \
+ '"*file1" should show path1/file1' \
+ 'git diff-index --cached $tree -- "*file1" >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+:100644 100644 8e4020bb5a8d8c873b25de15933e75cc0fc275df dca6b92303befc93086aa025d90a5facd7eb2812 M file0
EOF
test_expect_success \
'limit to file0 should show file0' \
@@ -70,4 +78,72 @@ test_expect_success 'diff-tree pathspec' '
test_cmp expected current
'
+EMPTY_TREE=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+
+test_expect_success 'diff-tree with wildcard shows dir also matches' '
+ git diff-tree --name-only $EMPTY_TREE $tree -- "f*" >result &&
+ echo file0 >expected &&
+ test_cmp expected result
+'
+
+test_expect_success 'diff-tree -r with wildcard' '
+ git diff-tree -r --name-only $EMPTY_TREE $tree -- "*file1" >result &&
+ echo path1/file1 >expected &&
+ test_cmp expected result
+'
+
+test_expect_success 'diff-tree with wildcard shows dir also matches' '
+ git diff-tree --name-only $tree $tree2 -- "path1/f*" >result &&
+ echo path1 >expected &&
+ test_cmp expected result
+'
+
+test_expect_success 'diff-tree -r with wildcard from beginning' '
+ git diff-tree -r --name-only $tree $tree2 -- "path1/*file1" >result &&
+ echo path1/file1 >expected &&
+ test_cmp expected result
+'
+
+test_expect_success 'diff-tree -r with wildcard' '
+ git diff-tree -r --name-only $tree $tree2 -- "path1/f*" >result &&
+ echo path1/file1 >expected &&
+ test_cmp expected result
+'
+
+test_expect_success 'setup submodules' '
+ test_tick &&
+ git init submod &&
+ ( cd submod && test_commit first; ) &&
+ git add submod &&
+ git commit -m first &&
+ ( cd submod && test_commit second; ) &&
+ git add submod &&
+ git commit -m second
+'
+
+test_expect_success 'diff-tree ignores trailing slash on submodule path' '
+ git diff --name-only HEAD^ HEAD submod >expect &&
+ git diff --name-only HEAD^ HEAD submod/ >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'diff multiple wildcard pathspecs' '
+ mkdir path2 &&
+ echo rezrov >path2/file1 &&
+ git update-index --add path2/file1 &&
+ tree3=$(git write-tree) &&
+ git diff --name-only $tree $tree3 -- "path2*1" "path1*1" >actual &&
+ cat <<-\EOF >expect &&
+ path1/file1
+ path2/file1
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'diff-cache ignores trailing slash on submodule path' '
+ git diff --name-only HEAD^ submod >expect &&
+ git diff --name-only HEAD^ submod/ >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index 918a21a..13e7f62 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -9,89 +9,149 @@ 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
+test_expect_success 'diff new symlink and file' '
+ 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
+ diff --git a/nitfol b/nitfol
+ new file mode 100644
+ index 0000000..7c465af
+ --- /dev/null
+ +++ b/nitfol
+ @@ -0,0 +1 @@
+ +xyzzy
+ EOF
-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
+ # the empty tree
+ git update-index &&
+ tree=$(git write-tree) &&
-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_ln_s_add xyzzy frotz &&
+ echo xyzzy >nitfol &&
+ git update-index --add nitfol &&
+ GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree >current &&
+ compare_diff_patch expected current
+'
-test_expect_success \
- 'diff unchanged symlink' \
- 'tree=$(git write-tree) &&
- git update-index frotz &&
- test -z "$(git diff-index --name-only $tree)"'
+test_expect_success 'diff unchanged symlink and file' '
+ tree=$(git write-tree) &&
+ git update-index frotz nitfol &&
+ test -z "$(git diff-index --name-only $tree)"
+'
-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 and file' '
+ 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
+ diff --git a/nitfol b/nitfol
+ deleted file mode 100644
+ index 7c465af..0000000
+ --- a/nitfol
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -xyzzy
+ EOF
+ mv frotz frotz2 &&
+ mv nitfol nitfol2 &&
+ git diff-index -M -p $tree >current &&
+ compare_diff_patch expected current
+'
-test_expect_success \
- 'diff removed symlink' \
- 'mv frotz frotz2 &&
- git diff-index -M -p $tree > current &&
- compare_diff_patch current expected'
+test_expect_success 'diff identical, but newly created symlink and file' '
+ >expected &&
+ rm -f frotz nitfol &&
+ echo xyzzy >nitfol &&
+ test-chmtime +10 nitfol &&
+ if test_have_prereq SYMLINKS
+ then
+ ln -s xyzzy frotz
+ else
+ printf xyzzy >frotz
+ # the symlink property propagates from the index
+ fi &&
+ git diff-index -M -p $tree >current &&
+ compare_diff_patch expected current &&
-cat > expected << EOF
-diff --git a/frotz b/frotz
-EOF
+ >expected &&
+ git diff-index -M -p -w $tree >current &&
+ compare_diff_patch expected current
+'
-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'
+test_expect_success 'diff different symlink and file' '
+ 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
+ diff --git a/nitfol b/nitfol
+ index 7c465af..df1db54 100644
+ --- a/nitfol
+ +++ b/nitfol
+ @@ -1 +1 @@
+ -xyzzy
+ +yxyyz
+ EOF
+ rm -f frotz &&
+ if test_have_prereq SYMLINKS
+ then
+ ln -s yxyyz frotz
+ else
+ printf yxyyz >frotz
+ # the symlink property propagates from the index
+ fi &&
+ echo yxyyz >nitfol &&
+ git diff-index -M -p $tree >current &&
+ compare_diff_patch expected current
+'
-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 SYMLINKS '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 &&
+ ! test -s output.err
+'
+
+test_expect_success SYMLINKS 'setup symlinks with attributes' '
+ echo "*.bin diff=bin" >>.gitattributes &&
+ echo content >file.bin &&
+ ln -s file.bin link.bin &&
+ git add -N file.bin link.bin
+'
-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 SYMLINKS 'symlinks do not respect userdiff config by path' '
+ cat >expect <<-\EOF &&
+ diff --git a/file.bin b/file.bin
+ index e69de29..d95f3ad 100644
+ Binary files a/file.bin and b/file.bin differ
+ diff --git a/link.bin b/link.bin
+ index e69de29..dce41ec 120000
+ --- a/link.bin
+ +++ b/link.bin
+ @@ -0,0 +1 @@
+ +file.bin
+ \ No newline at end of file
+ EOF
+ git config diff.bin.binary true &&
+ git diff file.bin link.bin >actual &&
+ test_cmp expect actual
+'
-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
index bc46563..643d729 100755
--- a/t/t4012-diff-binary.sh
+++ b/t/t4012-diff-binary.sh
@@ -8,13 +8,21 @@ test_description='Binary diff and apply
. ./test-lib.sh
-test_expect_success 'prepare repository' \
- 'echo AIT >a && echo BIT >b && echo CIT >c && echo DIT >d &&
- git update-index --add a b c d &&
- echo git >a &&
- cat "$TEST_DIRECTORY"/test4012.png >b &&
- echo git >c &&
- cat b b >d'
+cat >expect.binary-numstat <<\EOF
+1 1 a
+- - b
+1 1 c
+- - d
+EOF
+
+test_expect_success 'prepare repository' '
+ echo AIT >a && echo BIT >b && echo CIT >c && echo DIT >d &&
+ git update-index --add a b c d &&
+ echo git >a &&
+ cat "$TEST_DIRECTORY"/test-binary-1.png >b &&
+ echo git >c &&
+ cat b b >d
+'
cat > expected <<\EOF
a | 2 +-
@@ -23,63 +31,74 @@ cat > expected <<\EOF
d | Bin
4 files changed, 2 insertions(+), 2 deletions(-)
EOF
-test_expect_success 'diff without --binary' \
- 'git diff | git apply --stat --summary >current &&
- test_cmp expected current'
+test_expect_success 'apply --stat output for binary file change' '
+ git diff >diff &&
+ git apply --stat --summary <diff >current &&
+ test_i18ncmp expected current
+'
+
+test_expect_success 'diff --shortstat output for binary file change' '
+ tail -n 1 expected >expect &&
+ git diff --shortstat >current &&
+ test_i18ncmp expect current
+'
-test_expect_success 'diff with --binary' \
- 'git diff --binary | git apply --stat --summary >current &&
- test_cmp expected current'
+test_expect_success 'diff --shortstat output for binary file change only' '
+ echo " 1 file changed, 0 insertions(+), 0 deletions(-)" >expected &&
+ git diff --shortstat -- b >current &&
+ test_i18ncmp expected current
+'
+
+test_expect_success 'apply --numstat notices binary file change' '
+ git diff >diff &&
+ git apply --numstat <diff >current &&
+ test_cmp expect.binary-numstat current
+'
+
+test_expect_success 'apply --numstat understands diff --binary format' '
+ git diff --binary >diff &&
+ git apply --numstat <diff >current &&
+ test_cmp expect.binary-numstat current
+'
# apply needs to be able to skip the binary material correctly
# in order to report the line number of a corrupt patch.
-test_expect_success 'apply detecting corrupt patch correctly' \
- 'git diff | sed -e 's/-CIT/xCIT/' >broken &&
- if git apply --stat --summary broken 2>detected
- then
- echo unhappy - should have detected an error
- (exit 1)
- else
- echo happy
- fi &&
- detected=`cat detected` &&
- detected=`expr "$detected" : "fatal.*at line \\([0-9]*\\)\$"` &&
- detected=`sed -ne "${detected}p" broken` &&
- test "$detected" = xCIT'
-
-test_expect_success 'apply detecting corrupt patch correctly' \
- 'git diff --binary | sed -e 's/-CIT/xCIT/' >broken &&
- if git apply --stat --summary broken 2>detected
- then
- echo unhappy - should have detected an error
- (exit 1)
- else
- echo happy
- fi &&
- detected=`cat detected` &&
- detected=`expr "$detected" : "fatal.*at line \\([0-9]*\\)\$"` &&
- detected=`sed -ne "${detected}p" broken` &&
- test "$detected" = xCIT'
+test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '
+ git diff >output &&
+ sed -e "s/-CIT/xCIT/" <output >broken &&
+ test_must_fail git apply --stat --summary broken 2>detected &&
+ detected=$(cat detected) &&
+ detected=$(expr "$detected" : "fatal.*at line \\([0-9]*\\)\$") &&
+ detected=$(sed -ne "${detected}p" broken) &&
+ test "$detected" = xCIT
+'
+
+test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '
+ git diff --binary | sed -e "s/-CIT/xCIT/" >broken &&
+ test_must_fail git apply --stat --summary broken 2>detected &&
+ detected=$(cat detected) &&
+ detected=$(expr "$detected" : "fatal.*at line \\([0-9]*\\)\$") &&
+ detected=$(sed -ne "${detected}p" broken) &&
+ test "$detected" = xCIT
+'
test_expect_success 'initial commit' 'git commit -a -m initial'
# Try removal (b), modification (d), and creation (e).
-test_expect_success 'diff-index with --binary' \
- 'echo AIT >a && mv b e && echo CIT >c && cat e >d &&
- git update-index --add --remove a b c d e &&
- tree0=`git write-tree` &&
- git diff --cached --binary >current &&
- git apply --stat --summary current'
-
-test_expect_success 'apply binary patch' \
- 'git reset --hard &&
- git apply --binary --index <current &&
- tree1=`git write-tree` &&
- test "$tree1" = "$tree0"'
-
-nul_to_q() {
- perl -pe 'y/\000/Q/'
-}
+test_expect_success 'diff-index with --binary' '
+ echo AIT >a && mv b e && echo CIT >c && cat e >d &&
+ git update-index --add --remove a b c d e &&
+ tree0=$(git write-tree) &&
+ git diff --cached --binary >current &&
+ git apply --stat --summary current
+'
+
+test_expect_success 'apply binary patch' '
+ git reset --hard &&
+ git apply --binary --index <current &&
+ tree1=$(git write-tree) &&
+ test "$tree1" = "$tree0"
+'
test_expect_success 'diff --no-index with binary creation' '
echo Q | q_to_nul >binary &&
@@ -94,4 +113,23 @@ test_expect_success 'diff --no-index with binary creation' '
test_cmp expected actual
'
+cat >expect <<EOF
+ binfile | Bin 0 -> 1026 bytes
+ textfile | 10000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+
+test_expect_success 'diff --stat with binary files and big change count' '
+ printf "\01\00%1024d" 1 >binfile &&
+ git add binfile &&
+ i=0 &&
+ while test $i -lt 10000; do
+ echo $i &&
+ i=$(($i + 1))
+ done >textfile &&
+ git add textfile &&
+ git diff --cached --stat binfile textfile >output &&
+ grep " | " output >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index dae6358..94ef500 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -80,28 +80,43 @@ test_expect_success setup '
git config log.showroot false &&
git commit --amend &&
+
+ GIT_AUTHOR_DATE="2006-06-26 00:06:00 +0000" &&
+ GIT_COMMITTER_DATE="2006-06-26 00:06:00 +0000" &&
+ export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+ git checkout -b rearrange initial &&
+ for i in B A; do echo $i; done >dir/sub &&
+ git add dir/sub &&
+ git commit -m "Rearranged lines in dir/sub" &&
+ git checkout master &&
+
+ git config diff.renames false &&
+
git show-branch
'
: <<\EOF
! [initial] Initial
* [master] Merge branch 'side'
- ! [side] Side
----
- - [master] Merge branch 'side'
- *+ [side] Side
- * [master^] Second
-+*+ [initial] Initial
+ ! [rearrange] Rearranged lines in dir/sub
+ ! [side] Side
+----
+ + [rearrange] Rearranged lines in dir/sub
+ - [master] Merge branch 'side'
+ * + [side] Side
+ * [master^] Third
+ * [master~2] Second
++*++ [initial] Initial
EOF
-V=`git version | sed -e 's/^git version //' -e 's/\./\\./g'`
+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`
+ test=$(echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g')
+ pfx=$(printf "%04d" $test_count)
expect="$TEST_DIRECTORY/t4013/diff.$test"
actual="$pfx-diff.$test"
@@ -115,7 +130,12 @@ do
} >"$actual" &&
if test -f "$expect"
then
- test_cmp "$expect" "$actual" &&
+ case $cmd in
+ *format-patch* | *-stat*)
+ test_i18ncmp "$expect" "$actual";;
+ *)
+ test_cmp "$expect" "$actual";;
+ esac &&
rm -f "$actual"
else
# this is to help developing new tests.
@@ -208,7 +228,14 @@ log -p --first-parent master
log -m -p --first-parent master
log -m -p master
log -SF master
+log -S F master
log -SF -p master
+log -SF master --max-count=0
+log -SF master --max-count=1
+log -SF master --max-count=2
+log -GF master
+log -GF -p master
+log -GF -p --pickaxe-all master
log --decorate --all
log --decorate=full --all
@@ -280,6 +307,33 @@ diff --no-index --name-status -- dir2 dir
diff --no-index dir dir3
diff master master^ side
diff --dirstat master~1 master~2
+diff --dirstat initial rearrange
+diff --dirstat-by-file initial rearrange
EOF
+test_expect_success 'log -S requires an argument' '
+ test_must_fail git log -S
+'
+
+test_expect_success 'diff --cached on unborn branch' '
+ echo ref: refs/heads/unborn >.git/HEAD &&
+ git diff --cached >result &&
+ test_cmp "$TEST_DIRECTORY/t4013/diff.diff_--cached" result
+'
+
+test_expect_success 'diff --cached -- file on unborn branch' '
+ git diff --cached -- file0 >result &&
+ test_cmp "$TEST_DIRECTORY/t4013/diff.diff_--cached_--_file0" result
+'
+
+test_expect_success 'diff-tree --stdin with log formatting' '
+ cat >expect <<-\EOF &&
+ Side
+ Third
+ Second
+ EOF
+ git rev-list master | git diff-tree --stdin --format=%s -s >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master
index 3a9f78a..9951e36 100644
--- a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master
@@ -1,8 +1,8 @@
$ git diff-tree --cc --patch-with-stat --summary master
59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub | 2 ++
- file0 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+)
diff --cc dir/sub
index cead32e,7289e35..992913c
diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side
index a61ad8c..cec33fa 100644
--- a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side
+++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side
@@ -1,9 +1,9 @@
$ git diff-tree --cc --patch-with-stat --summary side
c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master
index 49f23b9..db3c0a7 100644
--- a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master
+++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master
@@ -1,8 +1,8 @@
$ git diff-tree --cc --patch-with-stat master
59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub | 2 ++
- file0 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+)
diff --cc dir/sub
index cead32e,7289e35..992913c
diff --git a/t/t4013/diff.diff-tree_--cc_--stat_--summary_master b/t/t4013/diff.diff-tree_--cc_--stat_--summary_master
index cc6eb3b..d019867 100644
--- a/t/t4013/diff.diff-tree_--cc_--stat_--summary_master
+++ b/t/t4013/diff.diff-tree_--cc_--stat_--summary_master
@@ -1,6 +1,6 @@
$ git diff-tree --cc --stat --summary master
59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub | 2 ++
- file0 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+)
$
diff --git a/t/t4013/diff.diff-tree_--cc_--stat_--summary_side b/t/t4013/diff.diff-tree_--cc_--stat_--summary_side
index 50362be..12b2eee 100644
--- a/t/t4013/diff.diff-tree_--cc_--stat_--summary_side
+++ b/t/t4013/diff.diff-tree_--cc_--stat_--summary_side
@@ -1,8 +1,8 @@
$ git diff-tree --cc --stat --summary side
c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
$
diff --git a/t/t4013/diff.diff-tree_--cc_--stat_master b/t/t4013/diff.diff-tree_--cc_--stat_master
index fae7f33..40b9179 100644
--- a/t/t4013/diff.diff-tree_--cc_--stat_master
+++ b/t/t4013/diff.diff-tree_--cc_--stat_master
@@ -1,6 +1,6 @@
$ git diff-tree --cc --stat master
59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub | 2 ++
- file0 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+)
$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-stat_initial
index d5c333a..817ed06 100644
--- a/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-stat_initial
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-stat_initial
@@ -1,9 +1,9 @@
$ 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(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+)
diff --git a/dir/sub b/dir/sub
new file mode 100644
diff --git a/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side b/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side
index 4d30e7e..fe3f6b7 100644
--- a/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side
+++ b/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side
@@ -5,10 +5,10 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
diff --git a/dir/sub b/dir/sub
index 35d242b..7289e35 100644
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial
index 7dfa6af..06eb77e 100644
--- a/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial
@@ -5,10 +5,10 @@ Date: Mon Jun 26 00:00:00 2006 +0000
Initial
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+)
diff --git a/dir/sub b/dir/sub
new file mode 100644
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial b/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial
index 43bfce2..680eab5 100644
--- a/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial
@@ -5,10 +5,10 @@ Date: Mon Jun 26 00:00:00 2006 +0000
Initial
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+)
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
index 9154aa4..9722d1b 100644
--- a/t/t4013/diff.diff-tree_--pretty_--root_--stat_initial
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--stat_initial
@@ -5,8 +5,8 @@ Date: Mon Jun 26 00:00:00 2006 +0000
Initial
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+)
$
diff --git a/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial
index 1562b62..ad69ffe 100644
--- a/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial
+++ b/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial
@@ -1,9 +1,9 @@
$ git diff-tree --root --patch-with-stat initial
444ac553ac7612cc88969031b02b3767fb8a353a
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+)
diff --git a/dir/sub b/dir/sub
new file mode 100644
diff --git a/t/t4013/diff.diff-tree_-c_--stat_--summary_master b/t/t4013/diff.diff-tree_-c_--stat_--summary_master
index ac9f641..81c3021 100644
--- a/t/t4013/diff.diff-tree_-c_--stat_--summary_master
+++ b/t/t4013/diff.diff-tree_-c_--stat_--summary_master
@@ -1,6 +1,6 @@
$ git diff-tree -c --stat --summary master
59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub | 2 ++
- file0 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+)
$
diff --git a/t/t4013/diff.diff-tree_-c_--stat_--summary_side b/t/t4013/diff.diff-tree_-c_--stat_--summary_side
index 2afcca1..e8dc12b 100644
--- a/t/t4013/diff.diff-tree_-c_--stat_--summary_side
+++ b/t/t4013/diff.diff-tree_-c_--stat_--summary_side
@@ -1,8 +1,8 @@
$ git diff-tree -c --stat --summary side
c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
$
diff --git a/t/t4013/diff.diff-tree_-c_--stat_master b/t/t4013/diff.diff-tree_-c_--stat_master
index c2fe6a9..89d59b1 100644
--- a/t/t4013/diff.diff-tree_-c_--stat_master
+++ b/t/t4013/diff.diff-tree_-c_--stat_master
@@ -1,6 +1,6 @@
$ git diff-tree -c --stat master
59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub | 2 ++
- file0 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+)
$
diff --git a/t/t4013/diff.diff_--cached b/t/t4013/diff.diff_--cached
new file mode 100644
index 0000000..ff16e83
--- /dev/null
+++ b/t/t4013/diff.diff_--cached
@@ -0,0 +1,38 @@
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..992913c
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,8 @@
++A
++B
++C
++D
++E
++F
++1
++2
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..10a8a9f
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,9 @@
++1
++2
++3
++4
++5
++6
++A
++B
++C
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
diff --git a/t/t4013/diff.diff_--cached_--_file0 b/t/t4013/diff.diff_--cached_--_file0
new file mode 100644
index 0000000..b9bb858
--- /dev/null
+++ b/t/t4013/diff.diff_--cached_--_file0
@@ -0,0 +1,15 @@
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..10a8a9f
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,9 @@
++1
++2
++3
++4
++5
++6
++A
++B
++C
diff --git a/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange b/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange
new file mode 100644
index 0000000..e48e33f
--- /dev/null
+++ b/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange
@@ -0,0 +1,3 @@
+$ git diff --dirstat-by-file initial rearrange
+ 100.0% dir/
+$
diff --git a/t/t4013/diff.diff_--dirstat_initial_rearrange b/t/t4013/diff.diff_--dirstat_initial_rearrange
new file mode 100644
index 0000000..5fb02c1
--- /dev/null
+++ b/t/t4013/diff.diff_--dirstat_initial_rearrange
@@ -0,0 +1,3 @@
+$ git diff --dirstat initial rearrange
+ 100.0% dir/
+$
diff --git a/t/t4013/diff.diff_--patch-with-stat_-r_initial..side b/t/t4013/diff.diff_--patch-with-stat_-r_initial..side
index 9ed317a..be8d1ea 100644
--- a/t/t4013/diff.diff_--patch-with-stat_-r_initial..side
+++ b/t/t4013/diff.diff_--patch-with-stat_-r_initial..side
@@ -1,8 +1,8 @@
$ git diff --patch-with-stat -r initial..side
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
diff --git a/dir/sub b/dir/sub
index 35d242b..7289e35 100644
diff --git a/t/t4013/diff.diff_--patch-with-stat_initial..side b/t/t4013/diff.diff_--patch-with-stat_initial..side
index 8b50629..5424e6d 100644
--- a/t/t4013/diff.diff_--patch-with-stat_initial..side
+++ b/t/t4013/diff.diff_--patch-with-stat_initial..side
@@ -1,8 +1,8 @@
$ git diff --patch-with-stat initial..side
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
diff --git a/dir/sub b/dir/sub
index 35d242b..7289e35 100644
diff --git a/t/t4013/diff.diff_--stat_initial..side b/t/t4013/diff.diff_--stat_initial..side
index 0517b5d..b7741e2 100644
--- a/t/t4013/diff.diff_--stat_initial..side
+++ b/t/t4013/diff.diff_--stat_initial..side
@@ -1,6 +1,6 @@
$ git diff --stat initial..side
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
$
diff --git a/t/t4013/diff.diff_-r_--stat_initial..side b/t/t4013/diff.diff_-r_--stat_initial..side
index 245220d..5d514f5 100644
--- a/t/t4013/diff.diff_-r_--stat_initial..side
+++ b/t/t4013/diff.diff_-r_--stat_initial..side
@@ -1,6 +1,6 @@
$ git diff -r --stat initial..side
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
$
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side
index 52116d3..547ca06 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side
+++ b/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side
@@ -12,10 +12,10 @@ 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(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master b/t/t4013/diff.format-patch_--attach_--stdout_initial..master
index ce49bd6..52fedc1 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master
@@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -73,9 +73,9 @@ 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(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
@@ -121,10 +121,10 @@ 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(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
index 5f1b238..1c3cde2 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
@@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -73,9 +73,9 @@ 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(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_initial..side
index 4a2364a..4717bd8 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..side
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..side
@@ -12,10 +12,10 @@ 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(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master
index 43b81eb..02c4db7 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master
+++ b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master
@@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -73,9 +73,9 @@ 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(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
@@ -121,10 +121,10 @@ 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(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
index ca3f60b..c7677c5 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
+++ b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
@@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -73,9 +73,9 @@ 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(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
@@ -121,10 +121,10 @@ 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(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_initial..master
index 08f2301..5b3e34e 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master
@@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -73,9 +73,9 @@ 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(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
@@ -121,10 +121,10 @@ 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(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^
index 07f1230..d13f8a8 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^
@@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -73,9 +73,9 @@ 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(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
index 29e00ab..caec553 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
@@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..side b/t/t4013/diff.format-patch_--inline_--stdout_initial..side
index 67633d4..d3a6762 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..side
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..side
@@ -12,10 +12,10 @@ 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(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^
index 1f0f9ad..244d964 100644
--- a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^
+++ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^
@@ -1,7 +1,7 @@
$ 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
+Date: Mon, 26 Jun 2006 00:06:00 +0000
Subject: [DIFFERENT_PREFIX 0/2] *** SUBJECT HERE ***
*** BLURB HERE ***
@@ -10,10 +10,10 @@ A U Thor (2):
Second
Third
- dir/sub | 4 ++++
- file0 | 3 +++
- file1 | 3 +++
- file2 | 3 ---
+ dir/sub | 4 ++++
+ file0 | 3 +++
+ file1 | 3 +++
+ file2 | 3 ---
4 files changed, 10 insertions(+), 3 deletions(-)
create mode 100644 file1
delete mode 100644 file2
@@ -28,9 +28,9 @@ Subject: [DIFFERENT_PREFIX 1/2] Second
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -73,9 +73,9 @@ 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(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master b/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master
index f7752eb..bfc287a 100644
--- a/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master
+++ b/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master
@@ -6,9 +6,9 @@ Subject: [PATCH] Second
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -51,9 +51,9 @@ Date: Mon, 26 Jun 2006 00:02:00 +0000
Subject: [PATCH] Third
---
- dir/sub | 2 ++
- file1 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
diff --git a/dir/sub b/dir/sub
@@ -85,10 +85,10 @@ 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(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.format-patch_--stdout_--numbered_initial..master b/t/t4013/diff.format-patch_--stdout_--numbered_initial..master
index 8e67dbf..568f6f5 100644
--- a/t/t4013/diff.format-patch_--stdout_--numbered_initial..master
+++ b/t/t4013/diff.format-patch_--stdout_--numbered_initial..master
@@ -6,9 +6,9 @@ Subject: [PATCH 1/3] Second
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -51,9 +51,9 @@ 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(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
diff --git a/dir/sub b/dir/sub
@@ -85,10 +85,10 @@ 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(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.format-patch_--stdout_initial..master b/t/t4013/diff.format-patch_--stdout_initial..master
index 7b89978..5f0352f 100644
--- a/t/t4013/diff.format-patch_--stdout_initial..master
+++ b/t/t4013/diff.format-patch_--stdout_initial..master
@@ -6,9 +6,9 @@ Subject: [PATCH 1/3] Second
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -51,9 +51,9 @@ 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(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
diff --git a/dir/sub b/dir/sub
@@ -85,10 +85,10 @@ 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(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.format-patch_--stdout_initial..master^ b/t/t4013/diff.format-patch_--stdout_initial..master^
index b7f9725..2ae454d 100644
--- a/t/t4013/diff.format-patch_--stdout_initial..master^
+++ b/t/t4013/diff.format-patch_--stdout_initial..master^
@@ -6,9 +6,9 @@ Subject: [PATCH 1/2] Second
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -51,9 +51,9 @@ 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(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.format-patch_--stdout_initial..side b/t/t4013/diff.format-patch_--stdout_initial..side
index e765088..a7d52fb 100644
--- a/t/t4013/diff.format-patch_--stdout_initial..side
+++ b/t/t4013/diff.format-patch_--stdout_initial..side
@@ -5,10 +5,10 @@ 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(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.log_--decorate=full_--all b/t/t4013/diff.log_--decorate=full_--all
index d155e0b..b345b2e 100644
--- a/t/t4013/diff.log_--decorate=full_--all
+++ b/t/t4013/diff.log_--decorate=full_--all
@@ -1,5 +1,11 @@
$ git log --decorate=full --all
-commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, refs/heads/master)
+commit cd4e72fd96faed3f0ba949dc42967430374e2290 (refs/heads/rearrange)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Rearranged lines in dir/sub
+
+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
diff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all
index fd7c3e6..3aa16a9 100644
--- a/t/t4013/diff.log_--decorate_--all
+++ b/t/t4013/diff.log_--decorate_--all
@@ -1,5 +1,11 @@
$ git log --decorate --all
-commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, master)
+commit cd4e72fd96faed3f0ba949dc42967430374e2290 (rearrange)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Rearranged lines in dir/sub
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> master)
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
diff --git a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
index bd7f5c0..a18f147 100644
--- a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
+++ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
@@ -12,8 +12,8 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
diff --git a/dir/sub b/dir/sub
index 35d242b..7289e35 100644
@@ -31,8 +31,8 @@ Date: Mon Jun 26 00:02:00 2006 +0000
Third
---
- dir/sub | 2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
diff --git a/dir/sub b/dir/sub
index 8422d40..cead32e 100644
@@ -53,8 +53,8 @@ Date: Mon Jun 26 00:01:00 2006 +0000
This is the second commit.
---
- dir/sub | 2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
diff --git a/dir/sub b/dir/sub
index 35d242b..8422d40 100644
diff --git a/t/t4013/diff.log_--patch-with-stat_master b/t/t4013/diff.log_--patch-with-stat_master
index 14595a6..ae425c4 100644
--- a/t/t4013/diff.log_--patch-with-stat_master
+++ b/t/t4013/diff.log_--patch-with-stat_master
@@ -12,10 +12,10 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
diff --git a/dir/sub b/dir/sub
index 35d242b..7289e35 100644
@@ -54,9 +54,9 @@ Date: Mon Jun 26 00:02:00 2006 +0000
Third
---
- dir/sub | 2 ++
- file1 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
diff --git a/dir/sub b/dir/sub
index 8422d40..cead32e 100644
@@ -86,9 +86,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.log_--patch-with-stat_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_
index 5a4e727..d5207ca 100644
--- a/t/t4013/diff.log_--patch-with-stat_master_--_dir_
+++ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_
@@ -12,8 +12,8 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
diff --git a/dir/sub b/dir/sub
index 35d242b..7289e35 100644
@@ -31,8 +31,8 @@ Date: Mon Jun 26 00:02:00 2006 +0000
Third
---
- dir/sub | 2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
diff --git a/dir/sub b/dir/sub
index 8422d40..cead32e 100644
@@ -53,8 +53,8 @@ Date: Mon Jun 26 00:01:00 2006 +0000
This is the second commit.
---
- dir/sub | 2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
diff --git a/dir/sub b/dir/sub
index 35d242b..8422d40 100644
diff --git a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
index df0aaa9..0fc1e8c 100644
--- a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
@@ -6,9 +6,9 @@ Date: Mon Jun 26 00:04:00 2006 +0000
Merge branch 'side'
- dir/sub | 2 ++
- file0 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+)
diff --cc dir/sub
index cead32e,7289e35..992913c
@@ -44,10 +44,10 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/dir/sub b/dir/sub
@@ -87,9 +87,9 @@ Date: Mon Jun 26 00:02:00 2006 +0000
Third
---
- dir/sub | 2 ++
- file1 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
diff --git a/dir/sub b/dir/sub
@@ -120,9 +120,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -162,10 +162,10 @@ Date: Mon Jun 26 00:00:00 2006 +0000
Initial
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+)
create mode 100644 dir/sub
create mode 100644 file0
create mode 100644 file2
diff --git a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
index c11b5f2c..dffc09d 100644
--- a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
@@ -12,10 +12,10 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/dir/sub b/dir/sub
@@ -55,9 +55,9 @@ Date: Mon Jun 26 00:02:00 2006 +0000
Third
---
- dir/sub | 2 ++
- file1 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
diff --git a/dir/sub b/dir/sub
@@ -88,9 +88,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -130,10 +130,10 @@ Date: Mon Jun 26 00:00:00 2006 +0000
Initial
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+)
create mode 100644 dir/sub
create mode 100644 file0
create mode 100644 file2
diff --git a/t/t4013/diff.log_--root_--patch-with-stat_master b/t/t4013/diff.log_--root_--patch-with-stat_master
index 5f0c98f..55aa980 100644
--- a/t/t4013/diff.log_--root_--patch-with-stat_master
+++ b/t/t4013/diff.log_--root_--patch-with-stat_master
@@ -12,10 +12,10 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
diff --git a/dir/sub b/dir/sub
index 35d242b..7289e35 100644
@@ -54,9 +54,9 @@ Date: Mon Jun 26 00:02:00 2006 +0000
Third
---
- dir/sub | 2 ++
- file1 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
diff --git a/dir/sub b/dir/sub
index 8422d40..cead32e 100644
@@ -86,9 +86,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/dir/sub b/dir/sub
@@ -127,10 +127,10 @@ Date: Mon Jun 26 00:00:00 2006 +0000
Initial
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+)
diff --git a/dir/sub b/dir/sub
new file mode 100644
diff --git a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
index e62c368..019d85f 100644
--- a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
@@ -6,9 +6,9 @@ Date: Mon Jun 26 00:04:00 2006 +0000
Merge branch 'side'
- dir/sub | 2 ++
- file0 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+)
diff --combined dir/sub
index cead32e,7289e35..992913c
@@ -44,10 +44,10 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/dir/sub b/dir/sub
@@ -87,9 +87,9 @@ Date: Mon Jun 26 00:02:00 2006 +0000
Third
---
- dir/sub | 2 ++
- file1 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
diff --git a/dir/sub b/dir/sub
@@ -120,9 +120,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -162,10 +162,10 @@ Date: Mon Jun 26 00:00:00 2006 +0000
Initial
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+)
create mode 100644 dir/sub
create mode 100644 file0
create mode 100644 file2
diff --git a/t/t4013/diff.log_-GF_-p_--pickaxe-all_master b/t/t4013/diff.log_-GF_-p_--pickaxe-all_master
new file mode 100644
index 0000000..d36f880
--- /dev/null
+++ b/t/t4013/diff.log_-GF_-p_--pickaxe-all_master
@@ -0,0 +1,27 @@
+$ git log -GF -p --pickaxe-all 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/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+$
diff --git a/t/t4013/diff.log_-GF_-p_master b/t/t4013/diff.log_-GF_-p_master
new file mode 100644
index 0000000..9d93f2c
--- /dev/null
+++ b/t/t4013/diff.log_-GF_-p_master
@@ -0,0 +1,18 @@
+$ git log -GF -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_-GF_master b/t/t4013/diff.log_-GF_master
new file mode 100644
index 0000000..4c6708d
--- /dev/null
+++ b/t/t4013/diff.log_-GF_master
@@ -0,0 +1,7 @@
+$ git log -GF 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_-SF_master_--max-count=0 b/t/t4013/diff.log_-SF_master_--max-count=0
new file mode 100644
index 0000000..c1fc6c8
--- /dev/null
+++ b/t/t4013/diff.log_-SF_master_--max-count=0
@@ -0,0 +1,2 @@
+$ git log -SF master --max-count=0
+$
diff --git a/t/t4013/diff.log_-SF_master_--max-count=1 b/t/t4013/diff.log_-SF_master_--max-count=1
new file mode 100644
index 0000000..c981a03
--- /dev/null
+++ b/t/t4013/diff.log_-SF_master_--max-count=1
@@ -0,0 +1,7 @@
+$ git log -SF master --max-count=1
+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_-SF_master_--max-count=2 b/t/t4013/diff.log_-SF_master_--max-count=2
new file mode 100644
index 0000000..a6c55fd
--- /dev/null
+++ b/t/t4013/diff.log_-SF_master_--max-count=2
@@ -0,0 +1,7 @@
+$ git log -SF master --max-count=2
+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_-S_F_master b/t/t4013/diff.log_-S_F_master
new file mode 100644
index 0000000..978d2b4
--- /dev/null
+++ b/t/t4013/diff.log_-S_F_master
@@ -0,0 +1,7 @@
+$ git log -S F 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.show_--patch-with-stat_--summary_side b/t/t4013/diff.show_--patch-with-stat_--summary_side
index 377f2b7..95a474e 100644
--- a/t/t4013/diff.show_--patch-with-stat_--summary_side
+++ b/t/t4013/diff.show_--patch-with-stat_--summary_side
@@ -5,10 +5,10 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.show_--patch-with-stat_side b/t/t4013/diff.show_--patch-with-stat_side
index fb14c53..974e99b 100644
--- a/t/t4013/diff.show_--patch-with-stat_side
+++ b/t/t4013/diff.show_--patch-with-stat_side
@@ -5,10 +5,10 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
diff --git a/dir/sub b/dir/sub
index 35d242b..7289e35 100644
diff --git a/t/t4013/diff.show_--stat_--summary_side b/t/t4013/diff.show_--stat_--summary_side
index 5bd5977..a71492f 100644
--- a/t/t4013/diff.show_--stat_--summary_side
+++ b/t/t4013/diff.show_--stat_--summary_side
@@ -5,9 +5,9 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
$
diff --git a/t/t4013/diff.show_--stat_side b/t/t4013/diff.show_--stat_side
index 3b22327..9be7124 100644
--- a/t/t4013/diff.show_--stat_side
+++ b/t/t4013/diff.show_--stat_side
@@ -5,8 +5,8 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
$
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_
index 6a467cc..c8b6af2 100644
--- a/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_
@@ -5,8 +5,8 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
diff --git a/dir/sub b/dir/sub
index 35d242b..7289e35 100644
@@ -24,8 +24,8 @@ Date: Mon Jun 26 00:02:00 2006 +0000
Third
---
- dir/sub | 2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
diff --git a/dir/sub b/dir/sub
index 8422d40..cead32e 100644
@@ -46,8 +46,8 @@ Date: Mon Jun 26 00:01:00 2006 +0000
This is the second commit.
---
- dir/sub | 2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
diff --git a/dir/sub b/dir/sub
index 35d242b..8422d40 100644
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_master b/t/t4013/diff.whatchanged_--patch-with-stat_master
index 1e1bbe1..1ac431b 100644
--- a/t/t4013/diff.whatchanged_--patch-with-stat_master
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_master
@@ -5,10 +5,10 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
diff --git a/dir/sub b/dir/sub
index 35d242b..7289e35 100644
@@ -47,9 +47,9 @@ Date: Mon Jun 26 00:02:00 2006 +0000
Third
---
- dir/sub | 2 ++
- file1 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
diff --git a/dir/sub b/dir/sub
index 8422d40..cead32e 100644
@@ -79,9 +79,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_ b/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_
index 13789f1..b30c285 100644
--- a/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_
@@ -5,8 +5,8 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
diff --git a/dir/sub b/dir/sub
index 35d242b..7289e35 100644
@@ -24,8 +24,8 @@ Date: Mon Jun 26 00:02:00 2006 +0000
Third
---
- dir/sub | 2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
diff --git a/dir/sub b/dir/sub
index 8422d40..cead32e 100644
@@ -46,8 +46,8 @@ Date: Mon Jun 26 00:01:00 2006 +0000
This is the second commit.
---
- dir/sub | 2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
diff --git a/dir/sub b/dir/sub
index 35d242b..8422d40 100644
diff --git a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
index e96ff1f..30aae78 100644
--- a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
@@ -6,9 +6,9 @@ Date: Mon Jun 26 00:04:00 2006 +0000
Merge branch 'side'
- dir/sub | 2 ++
- file0 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+)
diff --cc dir/sub
index cead32e,7289e35..992913c
@@ -44,10 +44,10 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/dir/sub b/dir/sub
@@ -87,9 +87,9 @@ Date: Mon Jun 26 00:02:00 2006 +0000
Third
---
- dir/sub | 2 ++
- file1 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
diff --git a/dir/sub b/dir/sub
@@ -120,9 +120,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -162,10 +162,10 @@ Date: Mon Jun 26 00:00:00 2006 +0000
Initial
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+)
create mode 100644 dir/sub
create mode 100644 file0
create mode 100644 file2
diff --git a/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master
index 0291153..db90e51 100644
--- a/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master
@@ -5,10 +5,10 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/dir/sub b/dir/sub
@@ -48,9 +48,9 @@ Date: Mon Jun 26 00:02:00 2006 +0000
Third
---
- dir/sub | 2 ++
- file1 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
diff --git a/dir/sub b/dir/sub
@@ -81,9 +81,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -123,10 +123,10 @@ Date: Mon Jun 26 00:00:00 2006 +0000
Initial
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+)
create mode 100644 dir/sub
create mode 100644 file0
create mode 100644 file2
diff --git a/t/t4013/diff.whatchanged_--root_--patch-with-stat_master b/t/t4013/diff.whatchanged_--root_--patch-with-stat_master
index 9b0349c..9a6cc92 100644
--- a/t/t4013/diff.whatchanged_--root_--patch-with-stat_master
+++ b/t/t4013/diff.whatchanged_--root_--patch-with-stat_master
@@ -5,10 +5,10 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
diff --git a/dir/sub b/dir/sub
index 35d242b..7289e35 100644
@@ -47,9 +47,9 @@ Date: Mon Jun 26 00:02:00 2006 +0000
Third
---
- dir/sub | 2 ++
- file1 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
diff --git a/dir/sub b/dir/sub
index 8422d40..cead32e 100644
@@ -79,9 +79,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/dir/sub b/dir/sub
@@ -120,10 +120,10 @@ Date: Mon Jun 26 00:00:00 2006 +0000
Initial
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+)
diff --git a/dir/sub b/dir/sub
new file mode 100644
diff --git a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
index c0aff68..d1d32bd 100644
--- a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
@@ -6,9 +6,9 @@ Date: Mon Jun 26 00:04:00 2006 +0000
Merge branch 'side'
- dir/sub | 2 ++
- file0 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+)
diff --combined dir/sub
index cead32e,7289e35..992913c
@@ -44,10 +44,10 @@ Date: Mon Jun 26 00:03:00 2006 +0000
Side
---
- dir/sub | 2 ++
- file0 | 3 +++
- file3 | 4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
create mode 100644 file3
diff --git a/dir/sub b/dir/sub
@@ -87,9 +87,9 @@ Date: Mon Jun 26 00:02:00 2006 +0000
Third
---
- dir/sub | 2 ++
- file1 | 3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
create mode 100644 file1
diff --git a/dir/sub b/dir/sub
@@ -120,9 +120,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000
This is the second commit.
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 ---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
3 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 file2
@@ -162,10 +162,10 @@ Date: Mon Jun 26 00:00:00 2006 +0000
Initial
---
- dir/sub | 2 ++
- file0 | 3 +++
- file2 | 3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+)
create mode 100644 dir/sub
create mode 100644 file0
create mode 100644 file2
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index f87434b..805dc90 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -6,30 +6,36 @@
test_description='various format-patch tests'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.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 &&
+ test_tick &&
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 &&
+ test_tick &&
git commit -m "Side changes #1" &&
for i in D E F; do echo "$i"; done >>file &&
git update-index file &&
+ test_tick &&
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 &&
+ test_tick &&
git commit -m "Side changes #3 with \\n backslash-n in it." &&
git checkout master &&
git diff-tree -p C2 | git apply --index &&
+ test_tick &&
git commit -m "Master accepts moral equivalent of #2"
'
@@ -37,7 +43,7 @@ test_expect_success setup '
test_expect_success "format-patch --ignore-if-in-upstream" '
git format-patch --stdout master..side >patch0 &&
- cnt=`grep "^From " patch0 | wc -l` &&
+ cnt=$(grep "^From " patch0 | wc -l) &&
test $cnt = 3
'
@@ -46,16 +52,40 @@ 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` &&
+ cnt=$(grep "^From " patch1 | wc -l) &&
test $cnt = 2
'
+test_expect_success "format-patch --ignore-if-in-upstream handles tags" '
+ git tag -a v1 -m tag side &&
+ git tag -a v2 -m tag master &&
+ git format-patch --stdout --ignore-if-in-upstream v2..v1 >patch1 &&
+ cnt=$(grep "^From " patch1 | wc -l) &&
+ test $cnt = 2
+'
+
+test_expect_success "format-patch doesn't consider merge commits" '
+
+ git checkout -b slave master &&
+ echo "Another line" >>file &&
+ test_tick &&
+ git commit -am "Slave change #1" &&
+ echo "Yet another line" >>file &&
+ test_tick &&
+ git commit -am "Slave change #2" &&
+ git checkout -b merger master &&
+ test_tick &&
+ git merge --no-ff slave &&
+ cnt=$(git format-patch -3 --stdout | grep "^From " | wc -l) &&
+ test $cnt = 3
+'
+
test_expect_success "format-patch result applies" '
git checkout -b rebuild-0 master &&
git am -3 patch0 &&
- cnt=`git rev-list master.. | wc -l` &&
+ cnt=$(git rev-list master.. | wc -l) &&
test $cnt = 2
'
@@ -63,7 +93,7 @@ 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` &&
+ cnt=$(git rev-list master.. | wc -l) &&
test $cnt = 2
'
@@ -88,82 +118,125 @@ test_expect_success 'replay did not screw up the log message' '
test_expect_success 'extra headers' '
- git config format.headers "To: R. E. Cipient <rcipient@example.com>
+ git config format.headers "To: R E Cipient <rcipient@example.com>
" &&
- git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>
+ git config --add format.headers "Cc: S E Cipient <scipient@example.com>
" &&
git format-patch --stdout master..side > patch2 &&
sed -e "/^\$/q" patch2 > hdrs2 &&
- grep "^To: R. E. Cipient <rcipient@example.com>\$" hdrs2 &&
- grep "^Cc: S. E. Cipient <scipient@example.com>\$" hdrs2
+ grep "^To: R E Cipient <rcipient@example.com>\$" hdrs2 &&
+ grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs2
'
test_expect_success 'extra headers without newlines' '
- git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
- git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>" &&
+ git config --replace-all format.headers "To: R E Cipient <rcipient@example.com>" &&
+ git config --add format.headers "Cc: S E Cipient <scipient@example.com>" &&
git format-patch --stdout master..side >patch3 &&
sed -e "/^\$/q" patch3 > hdrs3 &&
- grep "^To: R. E. Cipient <rcipient@example.com>\$" hdrs3 &&
- grep "^Cc: S. E. Cipient <scipient@example.com>\$" hdrs3
+ grep "^To: R E Cipient <rcipient@example.com>\$" hdrs3 &&
+ grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs3
'
test_expect_success 'extra headers with multiple To:s' '
- git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
- git config --add format.headers "To: S. E. Cipient <scipient@example.com>" &&
+ git config --replace-all format.headers "To: R E Cipient <rcipient@example.com>" &&
+ git config --add format.headers "To: S E Cipient <scipient@example.com>" &&
git format-patch --stdout master..side > patch4 &&
sed -e "/^\$/q" patch4 > hdrs4 &&
- grep "^To: R. E. Cipient <rcipient@example.com>,\$" hdrs4 &&
- grep "^ *S. E. Cipient <scipient@example.com>\$" hdrs4
+ grep "^To: R E Cipient <rcipient@example.com>,\$" hdrs4 &&
+ grep "^ *S E Cipient <scipient@example.com>\$" hdrs4
+'
+
+test_expect_success 'additional command line cc (ascii)' '
+
+ 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 'additional command line cc' '
+test_expect_failure 'additional command line cc (rfc822)' '
- git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
+ git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 &&
- grep "^Cc: R. E. Cipient <rcipient@example.com>,\$" patch5 &&
- grep "^ *S. E. Cipient <scipient@example.com>\$" patch5
+ grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch5 &&
+ grep "^ *\"S. E. Cipient\" <scipient@example.com>\$" patch5
'
test_expect_success 'command line headers' '
git config --unset-all format.headers &&
- git format-patch --add-header="Cc: R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch6 &&
- grep "^Cc: R. E. Cipient <rcipient@example.com>\$" patch6
+ git format-patch --add-header="Cc: R E Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch6 &&
+ grep "^Cc: R E Cipient <rcipient@example.com>\$" patch6
'
test_expect_success 'configuration headers and command line headers' '
- git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
- git format-patch --add-header="Cc: S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch7 &&
- grep "^Cc: R. E. Cipient <rcipient@example.com>,\$" patch7 &&
- grep "^ *S. E. Cipient <scipient@example.com>\$" patch7
+ git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
+ git format-patch --add-header="Cc: S E Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch7 &&
+ grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch7 &&
+ grep "^ *S E Cipient <scipient@example.com>\$" patch7
'
-test_expect_success 'command line To: header' '
+test_expect_success 'command line To: header (ascii)' '
git config --unset-all format.headers &&
+ git format-patch --to="R E Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
+ grep "^To: R E Cipient <rcipient@example.com>\$" patch8
+'
+
+test_expect_failure 'command line To: header (rfc822)' '
+
git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
- grep "^To: R. E. Cipient <rcipient@example.com>\$" patch8
+ grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" patch8
+'
+
+test_expect_failure 'command line To: header (rfc2047)' '
+
+ git format-patch --to="R Ä Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
+ grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" patch8
+'
+
+test_expect_success 'configuration To: header (ascii)' '
+
+ git config format.to "R E Cipient <rcipient@example.com>" &&
+ git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
+ grep "^To: R E Cipient <rcipient@example.com>\$" patch9
'
-test_expect_success 'configuration To: header' '
+test_expect_failure 'configuration To: header (rfc822)' '
git config format.to "R. E. Cipient <rcipient@example.com>" &&
git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
- grep "^To: R. E. Cipient <rcipient@example.com>\$" patch9
+ grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" patch9
+'
+
+test_expect_failure 'configuration To: header (rfc2047)' '
+
+ git config format.to "R Ä Cipient <rcipient@example.com>" &&
+ git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
+ grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" patch9
'
+# check_patch <patch>: Verify that <patch> looks like a half-sane
+# patch email to avoid a false positive with !grep
+check_patch () {
+ grep -e "^From:" "$1" &&
+ grep -e "^Date:" "$1" &&
+ grep -e "^Subject:" "$1"
+}
+
test_expect_success '--no-to overrides config.to' '
git config --replace-all format.to \
- "R. E. Cipient <rcipient@example.com>" &&
+ "R E Cipient <rcipient@example.com>" &&
git format-patch --no-to --stdout master..side |
sed -e "/^\$/q" >patch10 &&
- ! grep "^To: R. E. Cipient <rcipient@example.com>\$" patch10
+ check_patch patch10 &&
+ ! grep "^To: R E Cipient <rcipient@example.com>\$" patch10
'
test_expect_success '--no-to and --to replaces config.to' '
@@ -173,6 +246,7 @@ test_expect_success '--no-to and --to replaces config.to' '
git format-patch --no-to --to="Someone Else <else@out.there>" \
--stdout master..side |
sed -e "/^\$/q" >patch11 &&
+ check_patch patch11 &&
! grep "^To: Someone <someone@out.there>\$" patch11 &&
grep "^To: Someone Else <else@out.there>\$" patch11
'
@@ -180,19 +254,21 @@ test_expect_success '--no-to and --to replaces config.to' '
test_expect_success '--no-cc overrides config.cc' '
git config --replace-all format.cc \
- "C. E. Cipient <rcipient@example.com>" &&
+ "C E Cipient <rcipient@example.com>" &&
git format-patch --no-cc --stdout master..side |
sed -e "/^\$/q" >patch12 &&
- ! grep "^Cc: C. E. Cipient <rcipient@example.com>\$" patch12
+ check_patch patch12 &&
+ ! grep "^Cc: C E Cipient <rcipient@example.com>\$" patch12
'
-test_expect_success '--no-add-headers overrides config.headers' '
+test_expect_success '--no-add-header 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 |
+ "Header1: B E Cipient <rcipient@example.com>" &&
+ git format-patch --no-add-header --stdout master..side |
sed -e "/^\$/q" >patch13 &&
- ! grep "^Header1: B. E. Cipient <rcipient@example.com>\$" patch13
+ check_patch patch13 &&
+ ! grep "^Header1: B E Cipient <rcipient@example.com>\$" patch13
'
test_expect_success 'multiple files' '
@@ -203,6 +279,22 @@ test_expect_success 'multiple files' '
ls patches/0001-Side-changes-1.patch patches/0002-Side-changes-2.patch patches/0003-Side-changes-3-with-n-backslash-n-in-it.patch
'
+test_expect_success 'reroll count' '
+ rm -fr patches &&
+ git format-patch -o patches --cover-letter --reroll-count 4 master..side >list &&
+ ! grep -v "^patches/v4-000[0-3]-" list &&
+ sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+ ! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
+'
+
+test_expect_success 'reroll count (-v)' '
+ rm -fr patches &&
+ git format-patch -o patches --cover-letter -v4 master..side >list &&
+ ! grep -v "^patches/v4-000[0-3]-" list &&
+ sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+ ! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
+'
+
check_threading () {
expect="$1" &&
shift &&
@@ -423,22 +515,22 @@ test_expect_success 'thread deep cover-letter in-reply-to' '
'
test_expect_success 'thread via config' '
- git config format.thread true &&
+ test_config format.thread true &&
check_threading expect.thread master
'
test_expect_success 'thread deep via config' '
- git config format.thread deep &&
+ test_config format.thread deep &&
check_threading expect.deep master
'
test_expect_success 'thread config + override' '
- git config format.thread deep &&
+ test_config format.thread deep &&
check_threading expect.thread --thread master
'
test_expect_success 'thread config + --no-thread' '
- git config format.thread deep &&
+ test_config format.thread deep &&
check_threading expect.no-threading --no-thread master
'
@@ -457,7 +549,8 @@ test_expect_success 'cover-letter inherits diff options' '
git mv file foo &&
git commit -m foo &&
- git format-patch --cover-letter -1 &&
+ git format-patch --no-renames --cover-letter -1 &&
+ check_patch 0000-cover-letter.patch &&
! grep "file => foo .* 0 *\$" 0000-cover-letter.patch &&
git format-patch --cover-letter -1 -M &&
grep "file => foo .* 0 *\$" 0000-cover-letter.patch
@@ -483,11 +576,6 @@ test_expect_success 'shortlog of cover-letter wraps overly-long onelines' '
'
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
@@ -502,7 +590,9 @@ EOF
test_expect_success 'format-patch respects -U' '
git format-patch -U4 -2 &&
- sed -e "1,/^\$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
+ sed -e "1,/^diff/d" -e "/^+5/q" \
+ <0001-This-is-an-excessively-long-subject-line-for-a-messa.patch \
+ >output &&
test_cmp expect output
'
@@ -584,8 +674,19 @@ test_expect_success 'format-patch --in-reply-to' '
'
test_expect_success 'format-patch --signoff' '
- git format-patch -1 --signoff --stdout |
- grep "^Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+ git format-patch -1 --signoff --stdout >out &&
+ grep "^Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" out
+'
+
+test_expect_success 'format-patch --notes --signoff' '
+ git notes --ref test add -m "test message" HEAD &&
+ git format-patch -1 --signoff --stdout --notes=test >out &&
+ # Three dashes must come after S-o-b
+ ! sed "/^Signed-off-by: /q" out | grep "test message" &&
+ sed "1,/^Signed-off-by: /d" out | grep "test message" &&
+ # Notes message must come after three dashes
+ ! sed "/^---$/q" out | grep "test message" &&
+ sed "1,/^---$/d" out | grep "test message"
'
echo "fatal: --name-only does not make sense" > expect.name-only
@@ -594,15 +695,15 @@ 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_i18ncmp expect.name-only output &&
test_must_fail git format-patch --name-status 2> output &&
- test_cmp expect.name-status output &&
+ test_i18ncmp expect.name-status output &&
test_must_fail git format-patch --check 2> output &&
- test_cmp expect.check output'
+ test_i18ncmp 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 5 = $(grep "^diff --git a/" output | wc -l)'
test_expect_success 'format-patch -- <path>' '
git format-patch master..side -- file 2>error &&
@@ -635,6 +736,7 @@ 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 &&
+ check_patch output &&
! grep "config sig" output &&
! grep "my sig" output &&
! grep "^-- \$" output
@@ -648,21 +750,819 @@ test_expect_success 'format-patch --signature --cover-letter' '
test 2 = $(grep "my sig" output | wc -l)
'
-test_expect_success 'format.signature="" supresses signatures' '
+test_expect_success 'format.signature="" suppresses signatures' '
git config format.signature "" &&
git format-patch --stdout -1 >output &&
+ check_patch output &&
! grep "^-- \$" output
'
-test_expect_success 'format-patch --no-signature supresses signatures' '
+test_expect_success 'format-patch --no-signature suppresses signatures' '
git config --unset-all format.signature &&
git format-patch --stdout --no-signature -1 >output &&
+ check_patch output &&
! grep "^-- \$" output
'
-test_expect_success 'format-patch --signature="" supresses signatures' '
- git format-patch --signature="" -1 >output &&
+test_expect_success 'format-patch --signature="" suppresses signatures' '
+ git format-patch --stdout --signature="" -1 >output &&
+ check_patch output &&
! grep "^-- \$" output
'
+test_expect_success 'prepare mail-signature input' '
+ cat >mail-signature <<-\EOF
+
+ Test User <test.email@kernel.org>
+ http://git.kernel.org/cgit/git/git.git
+
+ git.kernel.org/?p=git/git.git;a=summary
+
+ EOF
+'
+
+test_expect_success '--signature-file=file works' '
+ git format-patch --stdout --signature-file=mail-signature -1 >output &&
+ check_patch output &&
+ sed -e "1,/^-- \$/d" <output >actual &&
+ {
+ cat mail-signature && echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'format.signaturefile works' '
+ test_config format.signaturefile mail-signature &&
+ git format-patch --stdout -1 >output &&
+ check_patch output &&
+ sed -e "1,/^-- \$/d" <output >actual &&
+ {
+ cat mail-signature && echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '--no-signature suppresses format.signaturefile ' '
+ test_config format.signaturefile mail-signature &&
+ git format-patch --stdout --no-signature -1 >output &&
+ check_patch output &&
+ ! grep "^-- \$" output
+'
+
+test_expect_success '--signature-file overrides format.signaturefile' '
+ cat >other-mail-signature <<-\EOF &&
+ Use this other signature instead of mail-signature.
+ EOF
+ test_config format.signaturefile mail-signature &&
+ git format-patch --stdout \
+ --signature-file=other-mail-signature -1 >output &&
+ check_patch output &&
+ sed -e "1,/^-- \$/d" <output >actual &&
+ {
+ cat other-mail-signature && echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '--signature overrides format.signaturefile' '
+ test_config format.signaturefile mail-signature &&
+ git format-patch --stdout --signature="my sig" -1 >output &&
+ check_patch output &&
+ grep "my sig" output
+'
+
+test_expect_success TTY 'format-patch --stdout paginates' '
+ rm -f pager_used &&
+ test_terminal env GIT_PAGER="wc >pager_used" git format-patch --stdout --all &&
+ test_path_is_file pager_used
+'
+
+ test_expect_success TTY 'format-patch --stdout pagination can be disabled' '
+ rm -f pager_used &&
+ test_terminal env GIT_PAGER="wc >pager_used" git --no-pager format-patch --stdout --all &&
+ test_terminal env GIT_PAGER="wc >pager_used" git -c "pager.format-patch=false" format-patch --stdout --all &&
+ test_path_is_missing pager_used &&
+ test_path_is_missing .git/pager_used
+'
+
+test_expect_success 'format-patch handles multi-line subjects' '
+ rm -rf patches/ &&
+ echo content >>file &&
+ for i in one two three; do echo $i; done >msg &&
+ git add file &&
+ git commit -F msg &&
+ git format-patch -o patches -1 &&
+ grep ^Subject: patches/0001-one.patch >actual &&
+ echo "Subject: [PATCH] one two three" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'format-patch handles multi-line encoded subjects' '
+ rm -rf patches/ &&
+ echo content >>file &&
+ for i in en två tre; do echo $i; done >msg &&
+ git add file &&
+ git commit -F msg &&
+ git format-patch -o patches -1 &&
+ grep ^Subject: patches/0001-en.patch >actual &&
+ echo "Subject: [PATCH] =?UTF-8?q?en=20tv=C3=A5=20tre?=" >expect &&
+ test_cmp expect actual
+'
+
+M8="foo bar "
+M64=$M8$M8$M8$M8$M8$M8$M8$M8
+M512=$M64$M64$M64$M64$M64$M64$M64$M64
+cat >expect <<'EOF'
+Subject: [PATCH] foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
+ bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
+ bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
+ bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
+EOF
+test_expect_success 'format-patch wraps extremely long subject (ascii)' '
+ echo content >>file &&
+ git add file &&
+ git commit -m "$M512" &&
+ git format-patch --stdout -1 >patch &&
+ sed -n "/^Subject/p; /^ /p; /^$/q" <patch >subject &&
+ test_cmp expect subject
+'
+
+M8="föö bar "
+M64=$M8$M8$M8$M8$M8$M8$M8$M8
+M512=$M64$M64$M64$M64$M64$M64$M64$M64
+cat >expect <<'EOF'
+Subject: [PATCH] =?UTF-8?q?f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
+ =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
+ =?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?=
+ =?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?=
+ =?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
+ =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
+ =?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?=
+ =?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?=
+ =?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
+ =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
+ =?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?=
+ =?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?=
+ =?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
+ =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
+ =?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?=
+ =?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?=
+ =?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
+ =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
+ =?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?=
+ =?UTF-8?q?bar?=
+EOF
+test_expect_success 'format-patch wraps extremely long subject (rfc2047)' '
+ rm -rf patches/ &&
+ echo content >>file &&
+ git add file &&
+ git commit -m "$M512" &&
+ git format-patch --stdout -1 >patch &&
+ sed -n "/^Subject/p; /^ /p; /^$/q" <patch >subject &&
+ test_cmp expect subject
+'
+
+check_author() {
+ echo content >>file &&
+ git add file &&
+ GIT_AUTHOR_NAME=$1 git commit -m author-check &&
+ git format-patch --stdout -1 >patch &&
+ sed -n "/^From: /p; /^ /p; /^$/q" <patch >actual &&
+ test_cmp expect actual
+}
+
+cat >expect <<'EOF'
+From: "Foo B. Bar" <author@example.com>
+EOF
+test_expect_success 'format-patch quotes dot in from-headers' '
+ check_author "Foo B. Bar"
+'
+
+cat >expect <<'EOF'
+From: "Foo \"The Baz\" Bar" <author@example.com>
+EOF
+test_expect_success 'format-patch quotes double-quote in from-headers' '
+ check_author "Foo \"The Baz\" Bar"
+'
+
+cat >expect <<'EOF'
+From: =?UTF-8?q?F=C3=B6o=20Bar?= <author@example.com>
+EOF
+test_expect_success 'format-patch uses rfc2047-encoded from-headers when necessary' '
+ check_author "Föo Bar"
+'
+
+cat >expect <<'EOF'
+From: =?UTF-8?q?F=C3=B6o=20B=2E=20Bar?= <author@example.com>
+EOF
+test_expect_success 'rfc2047-encoded from-headers leave no rfc822 specials' '
+ check_author "Föo B. Bar"
+'
+
+cat >expect <<EOF
+From: foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_
+ <author@example.com>
+EOF
+test_expect_success 'format-patch wraps moderately long from-header (ascii)' '
+ check_author "foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_"
+'
+
+cat >expect <<'EOF'
+From: Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar
+ Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo
+ Bar Foo Bar Foo Bar Foo Bar <author@example.com>
+EOF
+test_expect_success 'format-patch wraps extremely long from-header (ascii)' '
+ check_author "Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar"
+'
+
+cat >expect <<'EOF'
+From: "Foo.Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar
+ Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo
+ Bar Foo Bar Foo Bar Foo Bar" <author@example.com>
+EOF
+test_expect_success 'format-patch wraps extremely long from-header (rfc822)' '
+ check_author "Foo.Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar"
+'
+
+cat >expect <<'EOF'
+From: =?UTF-8?q?Fo=C3=B6=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo?=
+ =?UTF-8?q?=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20?=
+ =?UTF-8?q?Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar?=
+ =?UTF-8?q?=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20?=
+ =?UTF-8?q?Foo=20Bar=20Foo=20Bar?= <author@example.com>
+EOF
+test_expect_success 'format-patch wraps extremely long from-header (rfc2047)' '
+ check_author "Foö Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar"
+'
+
+cat >expect <<'EOF'
+Subject: header with . in it
+EOF
+test_expect_success 'subject lines do not have 822 atom-quoting' '
+ echo content >>file &&
+ git add file &&
+ git commit -m "header with . in it" &&
+ git format-patch -k -1 --stdout >patch &&
+ grep ^Subject: patch >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Subject: [PREFIX 1/1] header with . in it
+EOF
+test_expect_success 'subject prefixes have space prepended' '
+ git format-patch -n -1 --stdout --subject-prefix=PREFIX >patch &&
+ grep ^Subject: patch >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Subject: [1/1] header with . in it
+EOF
+test_expect_success 'empty subject prefix does not have extra space' '
+ git format-patch -n -1 --stdout --subject-prefix= >patch &&
+ grep ^Subject: patch >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--from=ident notices bogus ident' '
+ test_must_fail git format-patch -1 --stdout --from=foo >patch
+'
+
+test_expect_success '--from=ident replaces author' '
+ git format-patch -1 --stdout --from="Me <me@example.com>" >patch &&
+ cat >expect <<-\EOF &&
+ From: Me <me@example.com>
+
+ From: A U Thor <author@example.com>
+
+ EOF
+ sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+ test_cmp expect patch.head
+'
+
+test_expect_success '--from uses committer ident' '
+ git format-patch -1 --stdout --from >patch &&
+ cat >expect <<-\EOF &&
+ From: C O Mitter <committer@example.com>
+
+ From: A U Thor <author@example.com>
+
+ EOF
+ sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+ test_cmp expect patch.head
+'
+
+test_expect_success '--from omits redundant in-body header' '
+ git format-patch -1 --stdout --from="A U Thor <author@example.com>" >patch &&
+ cat >expect <<-\EOF &&
+ From: A U Thor <author@example.com>
+
+ EOF
+ sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+ test_cmp expect patch.head
+'
+
+test_expect_success 'in-body headers trigger content encoding' '
+ test_env GIT_AUTHOR_NAME="éxötìc" test_commit exotic &&
+ test_when_finished "git reset --hard HEAD^" &&
+ git format-patch -1 --stdout --from >patch &&
+ cat >expect <<-\EOF &&
+ From: C O Mitter <committer@example.com>
+ Content-Type: text/plain; charset=UTF-8
+
+ From: éxötìc <author@example.com>
+
+ EOF
+ sed -ne "/^From:/p; /^$/p; /^Content-Type/p; /^---$/q" <patch >patch.head &&
+ test_cmp expect patch.head
+'
+
+append_signoff()
+{
+ C=$(git commit-tree HEAD^^{tree} -p HEAD) &&
+ git format-patch --stdout --signoff $C^..$C >append_signoff.patch &&
+ sed -n -e "1,/^---$/p" append_signoff.patch |
+ egrep -n "^Subject|Sign|^$"
+}
+
+test_expect_success 'signoff: commit with no body' '
+ append_signoff </dev/null >actual &&
+ cat <<\EOF | sed "s/EOL$//" >expected &&
+4:Subject: [PATCH] EOL
+8:
+9:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'signoff: commit with only subject' '
+ echo subject | append_signoff >actual &&
+ cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+9:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'signoff: commit with only subject that does not end with NL' '
+ printf subject | append_signoff >actual &&
+ cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+9:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'signoff: no existing signoffs' '
+ append_signoff <<\EOF >actual &&
+subject
+
+body
+EOF
+ cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:
+11:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'signoff: no existing signoffs and no trailing NL' '
+ printf "subject\n\nbody" | append_signoff >actual &&
+ cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:
+11:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'signoff: some random signoff' '
+ append_signoff <<\EOF >actual &&
+subject
+
+body
+
+Signed-off-by: my@house
+EOF
+ cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:
+11:Signed-off-by: my@house
+12:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'signoff: misc conforming footer elements' '
+ append_signoff <<\EOF >actual &&
+subject
+
+body
+
+Signed-off-by: my@house
+(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)
+Tested-by: Some One <someone@example.com>
+Bug: 1234
+EOF
+ cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:
+11:Signed-off-by: my@house
+15:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'signoff: some random signoff-alike' '
+ append_signoff <<\EOF >actual &&
+subject
+
+body
+Fooled-by-me: my@house
+EOF
+ cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+11:
+12:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'signoff: not really a signoff' '
+ append_signoff <<\EOF >actual &&
+subject
+
+I want to mention about Signed-off-by: here.
+EOF
+ cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+9:I want to mention about Signed-off-by: here.
+10:
+11:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'signoff: not really a signoff (2)' '
+ append_signoff <<\EOF >actual &&
+subject
+
+My unfortunate
+Signed-off-by: example happens to be wrapped here.
+EOF
+ cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:Signed-off-by: example happens to be wrapped here.
+11:
+12:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'signoff: valid S-o-b paragraph in the middle' '
+ append_signoff <<\EOF >actual &&
+subject
+
+Signed-off-by: my@house
+Signed-off-by: your@house
+
+A lot of houses.
+EOF
+ cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+9:Signed-off-by: my@house
+10:Signed-off-by: your@house
+11:
+13:
+14:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'signoff: the same signoff at the end' '
+ append_signoff <<\EOF >actual &&
+subject
+
+body
+
+Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:
+11:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'signoff: the same signoff at the end, no trailing NL' '
+ printf "subject\n\nSigned-off-by: C O Mitter <committer@example.com>" |
+ append_signoff >actual &&
+ cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+9:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'signoff: the same signoff NOT at the end' '
+ append_signoff <<\EOF >actual &&
+subject
+
+body
+
+Signed-off-by: C O Mitter <committer@example.com>
+Signed-off-by: my@house
+EOF
+ cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:
+11:Signed-off-by: C O Mitter <committer@example.com>
+12:Signed-off-by: my@house
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'signoff: detect garbage in non-conforming footer' '
+ append_signoff <<\EOF >actual &&
+subject
+
+body
+
+Tested-by: my@house
+Some Trash
+Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:
+13:Signed-off-by: C O Mitter <committer@example.com>
+14:
+15:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'signoff: footer begins with non-signoff without @ sign' '
+ append_signoff <<\EOF >actual &&
+subject
+
+body
+
+Reviewed-id: Noone
+Tested-by: my@house
+Change-id: Ideadbeef
+Signed-off-by: C O Mitter <committer@example.com>
+Bug: 1234
+EOF
+ cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:
+14:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'format patch ignores color.ui' '
+ test_unconfig color.ui &&
+ git format-patch --stdout -1 >expect &&
+ test_config color.ui always &&
+ git format-patch --stdout -1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cover letter using branch description (1)' '
+ git checkout rebuild-1 &&
+ test_config branch.rebuild-1.description hello &&
+ git format-patch --stdout --cover-letter master >actual &&
+ grep hello actual >/dev/null
+'
+
+test_expect_success 'cover letter using branch description (2)' '
+ git checkout rebuild-1 &&
+ test_config branch.rebuild-1.description hello &&
+ git format-patch --stdout --cover-letter rebuild-1~2..rebuild-1 >actual &&
+ grep hello actual >/dev/null
+'
+
+test_expect_success 'cover letter using branch description (3)' '
+ git checkout rebuild-1 &&
+ test_config branch.rebuild-1.description hello &&
+ git format-patch --stdout --cover-letter ^master rebuild-1 >actual &&
+ grep hello actual >/dev/null
+'
+
+test_expect_success 'cover letter using branch description (4)' '
+ git checkout rebuild-1 &&
+ test_config branch.rebuild-1.description hello &&
+ git format-patch --stdout --cover-letter master.. >actual &&
+ grep hello actual >/dev/null
+'
+
+test_expect_success 'cover letter using branch description (5)' '
+ git checkout rebuild-1 &&
+ test_config branch.rebuild-1.description hello &&
+ git format-patch --stdout --cover-letter -2 HEAD >actual &&
+ grep hello actual >/dev/null
+'
+
+test_expect_success 'cover letter using branch description (6)' '
+ git checkout rebuild-1 &&
+ test_config branch.rebuild-1.description hello &&
+ git format-patch --stdout --cover-letter -2 >actual &&
+ grep hello actual >/dev/null
+'
+
+test_expect_success 'cover letter with nothing' '
+ git format-patch --stdout --cover-letter >actual &&
+ test_line_count = 0 actual
+'
+
+test_expect_success 'cover letter auto' '
+ mkdir -p tmp &&
+ test_when_finished "rm -rf tmp;
+ git config --unset format.coverletter" &&
+
+ git config format.coverletter auto &&
+ git format-patch -o tmp -1 >list &&
+ test_line_count = 1 list &&
+ git format-patch -o tmp -2 >list &&
+ test_line_count = 3 list
+'
+
+test_expect_success 'cover letter auto user override' '
+ mkdir -p tmp &&
+ test_when_finished "rm -rf tmp;
+ git config --unset format.coverletter" &&
+
+ git config format.coverletter auto &&
+ git format-patch -o tmp --cover-letter -1 >list &&
+ test_line_count = 2 list &&
+ git format-patch -o tmp --cover-letter -2 >list &&
+ test_line_count = 3 list &&
+ git format-patch -o tmp --no-cover-letter -1 >list &&
+ test_line_count = 1 list &&
+ git format-patch -o tmp --no-cover-letter -2 >list &&
+ test_line_count = 2 list
+'
+
+test_expect_success 'format-patch --zero-commit' '
+ git format-patch --zero-commit --stdout v2..v1 >patch2 &&
+ grep "^From " patch2 | sort | uniq >actual &&
+ echo "From $_z40 Mon Sep 17 00:00:00 2001" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'From line has expected format' '
+ git format-patch --stdout v2..v1 >patch2 &&
+ grep "^From " patch2 >from &&
+ grep "^From $_x40 Mon Sep 17 00:00:00 2001$" patch2 >filtered &&
+ test_cmp from filtered
+'
+
+test_expect_success 'format-patch format.outputDirectory option' '
+ test_config format.outputDirectory patches &&
+ rm -fr patches &&
+ git format-patch master..side &&
+ test $(git rev-list master..side | wc -l) -eq $(ls patches | wc -l)
+'
+
+test_expect_success 'format-patch -o overrides format.outputDirectory' '
+ test_config format.outputDirectory patches &&
+ rm -fr patches patchset &&
+ git format-patch master..side -o patchset &&
+ test_path_is_missing patches &&
+ test_path_is_dir patchset
+'
+
+test_expect_success 'format-patch --base' '
+ git checkout side &&
+ git format-patch --stdout --base=HEAD~3 -1 >patch &&
+ grep "^base-commit:" patch >actual &&
+ grep "^prerequisite-patch-id:" patch >>actual &&
+ echo "base-commit: $(git rev-parse HEAD~3)" >expected &&
+ echo "prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --stable | awk "{print \$1}")" >>expected &&
+ echo "prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --stable | awk "{print \$1}")" >>expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'format-patch --base errors out when base commit is in revision list' '
+ test_must_fail git format-patch --base=HEAD -2 &&
+ test_must_fail git format-patch --base=HEAD~1 -2 &&
+ git format-patch --stdout --base=HEAD~2 -2 >patch &&
+ grep "^base-commit:" patch >actual &&
+ echo "base-commit: $(git rev-parse HEAD~2)" >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'format-patch --base errors out when base commit is not ancestor of revision list' '
+ # For history as below:
+ #
+ # ---Q---P---Z---Y---*---X
+ # \ /
+ # ------------W
+ #
+ # If "format-patch Z..X" is given, P and Z can not be specified as the base commit
+ git checkout -b topic1 master &&
+ git rev-parse HEAD >commit-id-base &&
+ test_commit P &&
+ git rev-parse HEAD >commit-id-P &&
+ test_commit Z &&
+ git rev-parse HEAD >commit-id-Z &&
+ test_commit Y &&
+ git checkout -b topic2 master &&
+ test_commit W &&
+ git merge topic1 &&
+ test_commit X &&
+ test_must_fail git format-patch --base=$(cat commit-id-P) -3 &&
+ test_must_fail git format-patch --base=$(cat commit-id-Z) -3 &&
+ git format-patch --stdout --base=$(cat commit-id-base) -3 >patch &&
+ grep "^base-commit:" patch >actual &&
+ echo "base-commit: $(cat commit-id-base)" >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'format-patch --base=auto' '
+ git checkout -b upstream master &&
+ git checkout -b local upstream &&
+ git branch --set-upstream-to=upstream &&
+ test_commit N1 &&
+ test_commit N2 &&
+ git format-patch --stdout --base=auto -2 >patch &&
+ grep "^base-commit:" patch >actual &&
+ echo "base-commit: $(git rev-parse upstream)" >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'format-patch errors out when history involves criss-cross' '
+ # setup criss-cross history
+ #
+ # B---M1---D
+ # / \ /
+ # A X
+ # \ / \
+ # C---M2---E
+ #
+ git checkout master &&
+ test_commit A &&
+ git checkout -b xb master &&
+ test_commit B &&
+ git checkout -b xc master &&
+ test_commit C &&
+ git checkout -b xbc xb -- &&
+ git merge xc &&
+ git checkout -b xcb xc -- &&
+ git branch --set-upstream-to=xbc &&
+ git merge xb &&
+ git checkout xbc &&
+ test_commit D &&
+ git checkout xcb &&
+ test_commit E &&
+ test_must_fail git format-patch --base=auto -1
+'
+
+test_expect_success 'format-patch format.useAutoBaseoption' '
+ test_when_finished "git config --unset format.useAutoBase" &&
+ git checkout local &&
+ git config format.useAutoBase true &&
+ git format-patch --stdout -1 >patch &&
+ grep "^base-commit:" patch >actual &&
+ echo "base-commit: $(git rev-parse upstream)" >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'format-patch --base overrides format.useAutoBase' '
+ test_when_finished "git config --unset format.useAutoBase" &&
+ git config format.useAutoBase true &&
+ git format-patch --stdout --base=HEAD~1 -1 >patch &&
+ grep "^base-commit:" patch >actual &&
+ echo "base-commit: $(git rev-parse HEAD~1)" >expected &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 935d101..2434157 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -9,397 +9,705 @@ 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
+test_expect_success "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_cmp expect out &&
+
+ git diff -w >out &&
+ test_cmp expect out &&
+
+ git diff -b >out &&
+ test_cmp expect out
+'
+
+test_expect_success 'another test, without options' '
+ tr Q "\015" <<-\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'
+ 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..22d9f73 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_cmp expect out &&
+
+ >expect &&
+ git diff -w >out &&
+ test_cmp expect out &&
+
+ git diff -w -b >out &&
+ test_cmp expect out &&
+
+ git diff -w --ignore-space-at-eol >out &&
+ test_cmp expect out &&
+
+ git diff -w -b --ignore-space-at-eol >out &&
+ test_cmp expect out &&
+
+
+ tr "Q_" "\015 " <<-\EOF >expect &&
+ diff --git a/x b/x
+ index d99af23..22d9f73 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 end
+ EOF
+ git diff -b >out &&
+ test_cmp expect out &&
+
+ git diff -b --ignore-space-at-eol >out &&
+ test_cmp expect out &&
+
+ tr "Q_" "\015 " <<-\EOF >expect &&
+ diff --git a/x b/x
+ index d99af23..22d9f73 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 end
+ EOF
+ git diff --ignore-space-at-eol >out &&
+ test_cmp expect out
+'
+
+test_expect_success 'ignore-blank-lines: only new lines' '
+ test_seq 5 >x &&
+ git update-index x &&
+ test_seq 5 | sed "/3/i\\
+" >x &&
+ git diff --ignore-blank-lines >out &&
+ >expect &&
+ test_cmp out expect
+'
+
+test_expect_success 'ignore-blank-lines: only new lines with space' '
+ test_seq 5 >x &&
+ git update-index x &&
+ test_seq 5 | sed "/3/i\\
+ " >x &&
+ git diff -w --ignore-blank-lines >out &&
+ >expect &&
+ test_cmp out expect
+'
+
+test_expect_success 'ignore-blank-lines: after change' '
+ cat <<-\EOF >x &&
+ 1
+ 2
+
+ 3
+ 4
+ 5
+
+ 6
+ 7
+ EOF
+ git update-index x &&
+ cat <<-\EOF >x &&
+ change
+
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+
+ 7
+ EOF
+ git diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&
+ cat <<-\EOF >expected &&
+ diff --git a/x b/x
+ --- a/x
+ +++ b/x
+ @@ -1,6 +1,7 @@
+ +change
+ +
+ 1
+ 2
+ -
+ 3
+ 4
+ 5
+ EOF
+ compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: before change' '
+ cat <<-\EOF >x &&
+ 1
+ 2
+
+ 3
+ 4
+ 5
+ 6
+ 7
+ EOF
+ git update-index x &&
+ cat <<-\EOF >x &&
+
+ 1
+ 2
+ 3
+ 4
+ 5
+
+ 6
+ 7
+ change
+ EOF
+ git diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&
+ cat <<-\EOF >expected &&
+ diff --git a/x b/x
+ --- a/x
+ +++ b/x
+ @@ -4,5 +4,7 @@
+ 3
+ 4
+ 5
+ +
+ 6
+ 7
+ +change
+ EOF
+ compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: between changes' '
+ cat <<-\EOF >x &&
+ 1
+ 2
+ 3
+ 4
+ 5
+
+
+ 6
+ 7
+ 8
+ 9
+ 10
+ EOF
+ git update-index x &&
+ cat <<-\EOF >x &&
+ change
+ 1
+ 2
+
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+
+ 9
+ 10
+ change
+ EOF
+ git diff --ignore-blank-lines >out.tmp &&
+ cat <<-\EOF >expected &&
+ diff --git a/x b/x
+ --- a/x
+ +++ b/x
+ @@ -1,5 +1,7 @@
+ +change
+ 1
+ 2
+ +
+ 3
+ 4
+ 5
+ @@ -8,5 +8,7 @@
+ 6
+ 7
+ 8
+ +
+ 9
+ 10
+ +change
+ EOF
+ compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: between changes (with interhunkctx)' '
+ test_seq 10 >x &&
+ git update-index x &&
+ cat <<-\EOF >x &&
+ change
+ 1
+ 2
+
+ 3
+ 4
+ 5
+
+ 6
+ 7
+ 8
+ 9
+
+ 10
+ change
+ EOF
+ git diff --inter-hunk-context=2 --ignore-blank-lines >out.tmp &&
+ cat <<-\EOF >expected &&
+ diff --git a/x b/x
+ --- a/x
+ +++ b/x
+ @@ -1,10 +1,15 @@
+ +change
+ 1
+ 2
+ +
+ 3
+ 4
+ 5
+ +
+ 6
+ 7
+ 8
+ 9
+ +
+ 10
+ +change
+ EOF
+ compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: scattered spaces' '
+ test_seq 10 >x &&
+ git update-index x &&
+ cat <<-\EOF >x &&
+ change
+ 1
+ 2
+ 3
+
+ 4
+
+ 5
+
+ 6
+
+
+ 7
+
+ 8
+ 9
+ 10
+ change
+ EOF
+ git diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp &&
+ cat <<-\EOF >expected &&
+ diff --git a/x b/x
+ --- a/x
+ +++ b/x
+ @@ -1,3 +1,4 @@
+ +change
+ 1
+ 2
+ 3
+ @@ -8,3 +15,4 @@
+ 8
+ 9
+ 10
+ +change
+ EOF
+ compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: spaces coalesce' '
+ test_seq 6 >x &&
+ git update-index x &&
+ cat <<-\EOF >x &&
+ change
+ 1
+ 2
+ 3
+
+ 4
+
+ 5
+
+ 6
+ change
+ EOF
+ git diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp &&
+ cat <<-\EOF >expected &&
+ diff --git a/x b/x
+ --- a/x
+ +++ b/x
+ @@ -1,6 +1,11 @@
+ +change
+ 1
+ 2
+ 3
+ +
+ 4
+ +
+ 5
+ +
+ 6
+ +change
+ EOF
+ compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: mix changes and blank lines' '
+ test_seq 16 >x &&
+ git update-index x &&
+ cat <<-\EOF >x &&
+ change
+ 1
+ 2
+
+ 3
+ 4
+ 5
+ change
+ 6
+ 7
+ 8
+
+ 9
+ 10
+ 11
+ change
+ 12
+ 13
+ 14
+
+ 15
+ 16
+ change
+ EOF
+ git diff --ignore-blank-lines >out.tmp &&
+ cat <<-\EOF >expected &&
+ diff --git a/x b/x
+ --- a/x
+ +++ b/x
+ @@ -1,8 +1,11 @@
+ +change
+ 1
+ 2
+ +
+ 3
+ 4
+ 5
+ +change
+ 6
+ 7
+ 8
+ @@ -9,8 +13,11 @@
+ 9
+ 10
+ 11
+ +change
+ 12
+ 13
+ 14
+ +
+ 15
+ 16
+ +change
+ EOF
+ compare_diff_patch expected out.tmp
+'
test_expect_success 'check mixed spaces and tabs in indent' '
-
# This is indented with SP HT SP.
- echo " foo();" > x &&
+ 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 &&
+ 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 &&
+ echo "foo();" >x &&
git diff --check
-
'
test_expect_success 'check with trailing whitespace' '
-
- echo "foo(); " > x &&
+ 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 &&
+ 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 &&
+ echo "foo();" >x &&
git add x &&
git diff --cached --check
-
'
test_expect_success 'check staged with trailing whitespace' '
-
- echo "foo(); " > x &&
+ 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 &&
+ 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 &&
+ echo "foo();" >x &&
git add x &&
git diff-index --check HEAD
-
'
test_expect_success 'check with trailing whitespace (diff-index)' '
-
- echo "foo(); " > x &&
+ 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 &&
+ 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 &&
+ 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 &&
+ 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 &&
+ 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 &&
+ 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 &&
+ 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 &&
+ 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 &&
+ echo "foo (); " >x &&
git diff --check
-
'
test_expect_success 'check trailing whitespace (trailing-space: on)' '
-
git config core.whitespace "trailing-space" &&
- echo "foo (); " > x &&
+ 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 &&
+ 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 &&
+ 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 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 &&
+ echo " foo ();" >x &&
test_must_fail git diff --check
+'
+test_expect_success 'ditto, but tabwidth=9' '
+ git config core.whitespace "indent-with-non-tab,tabwidth=9" &&
+ 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 &&
+ echo " foo ();" >x &&
+ test_must_fail git diff --check
+'
+
+test_expect_success 'ditto, but tabwidth=10' '
+ git config core.whitespace "indent-with-non-tab,tabwidth=10" &&
test_must_fail git diff --check
+'
+test_expect_success 'ditto, but tabwidth=20' '
+ git config core.whitespace "indent-with-non-tab,tabwidth=20" &&
+ git diff --check
'
test_expect_success 'check tabs as indentation (tab-in-indent: off)' '
-
git config core.whitespace "-tab-in-indent" &&
- echo " foo ();" > x &&
+ 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 &&
+ 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 &&
+ echo " foo ();" >x &&
test_must_fail git diff --check
+'
+test_expect_success 'ditto, but tabwidth=1 (must be irrelevant)' '
+ git config core.whitespace "tab-in-indent,tabwidth=1" &&
+ 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 &&
+ 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 &&
+ 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 &&
+ echo "" >x &&
+ echo "foo(); " >>x &&
git diff --check | grep "x:2:"
-
'
test_expect_success 'checkdiff detects new trailing blank lines (1)' '
@@ -491,4 +799,139 @@ test_expect_success 'combined diff with autocrlf conversion' '
'
+# Start testing the colored format for whitespace checks
+
+test_expect_success 'setup diff colors' '
+ git config color.diff always &&
+ git config color.diff.plain normal &&
+ git config color.diff.meta bold &&
+ git config color.diff.frag cyan &&
+ git config color.diff.func normal &&
+ git config color.diff.old red &&
+ git config color.diff.new green &&
+ git config color.diff.commit yellow &&
+ git config color.diff.whitespace blue &&
+
+ git config core.autocrlf false
+'
+
+test_expect_success 'diff that introduces a line with only tabs' '
+ git config core.whitespace blank-at-eol &&
+ git reset --hard &&
+ echo "test" >x &&
+ git commit -m "initial" x &&
+ echo "{NTN}" | tr "NT" "\n\t" >>x &&
+ git -c color.diff=always diff | test_decode_color >current &&
+
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/x b/x<RESET>
+ <BOLD>index 9daeafb..2874b91 100644<RESET>
+ <BOLD>--- a/x<RESET>
+ <BOLD>+++ b/x<RESET>
+ <CYAN>@@ -1 +1,4 @@<RESET>
+ test<RESET>
+ <GREEN>+<RESET><GREEN>{<RESET>
+ <GREEN>+<RESET><BLUE> <RESET>
+ <GREEN>+<RESET><GREEN>}<RESET>
+ EOF
+
+ test_cmp expected current
+'
+
+test_expect_success 'diff that introduces and removes ws breakages' '
+ git reset --hard &&
+ {
+ echo "0. blank-at-eol " &&
+ echo "1. blank-at-eol "
+ } >x &&
+ git commit -a --allow-empty -m preimage &&
+ {
+ echo "0. blank-at-eol " &&
+ echo "1. still-blank-at-eol " &&
+ echo "2. and a new line "
+ } >x &&
+
+ git -c color.diff=always diff |
+ test_decode_color >current &&
+
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/x b/x<RESET>
+ <BOLD>index d0233a2..700886e 100644<RESET>
+ <BOLD>--- a/x<RESET>
+ <BOLD>+++ b/x<RESET>
+ <CYAN>@@ -1,2 +1,3 @@<RESET>
+ 0. blank-at-eol <RESET>
+ <RED>-1. blank-at-eol <RESET>
+ <GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET>
+ <GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
+ EOF
+
+ test_cmp expected current
+'
+
+test_expect_success 'the same with --ws-error-highlight' '
+ git reset --hard &&
+ {
+ echo "0. blank-at-eol " &&
+ echo "1. blank-at-eol "
+ } >x &&
+ git commit -a --allow-empty -m preimage &&
+ {
+ echo "0. blank-at-eol " &&
+ echo "1. still-blank-at-eol " &&
+ echo "2. and a new line "
+ } >x &&
+
+ git -c color.diff=always diff --ws-error-highlight=default,old |
+ test_decode_color >current &&
+
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/x b/x<RESET>
+ <BOLD>index d0233a2..700886e 100644<RESET>
+ <BOLD>--- a/x<RESET>
+ <BOLD>+++ b/x<RESET>
+ <CYAN>@@ -1,2 +1,3 @@<RESET>
+ 0. blank-at-eol <RESET>
+ <RED>-<RESET><RED>1. blank-at-eol<RESET><BLUE> <RESET>
+ <GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET>
+ <GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
+ EOF
+
+ test_cmp expected current &&
+
+ git -c color.diff=always diff --ws-error-highlight=all |
+ test_decode_color >current &&
+
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/x b/x<RESET>
+ <BOLD>index d0233a2..700886e 100644<RESET>
+ <BOLD>--- a/x<RESET>
+ <BOLD>+++ b/x<RESET>
+ <CYAN>@@ -1,2 +1,3 @@<RESET>
+ <RESET>0. blank-at-eol<RESET><BLUE> <RESET>
+ <RED>-<RESET><RED>1. blank-at-eol<RESET><BLUE> <RESET>
+ <GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET>
+ <GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
+ EOF
+
+ test_cmp expected current &&
+
+ git -c color.diff=always diff --ws-error-highlight=none |
+ test_decode_color >current &&
+
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/x b/x<RESET>
+ <BOLD>index d0233a2..700886e 100644<RESET>
+ <BOLD>--- a/x<RESET>
+ <BOLD>+++ b/x<RESET>
+ <CYAN>@@ -1,2 +1,3 @@<RESET>
+ 0. blank-at-eol <RESET>
+ <RED>-1. blank-at-eol <RESET>
+ <GREEN>+1. still-blank-at-eol <RESET>
+ <GREEN>+2. and a new line <RESET>
+ EOF
+
+ test_cmp expected current
+'
+
test_done
diff --git a/t/t4016-diff-quote.sh b/t/t4016-diff-quote.sh
index 34e5144..9c48e5c 100755
--- a/t/t4016-diff-quote.sh
+++ b/t/t4016-diff-quote.sh
@@ -13,8 +13,9 @@ 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_have_prereq !MINGW &&
+echo 2>/dev/null >"$P1" && test -f "$P1" && rm -f "$P1" || {
+ skip_all='Your filesystem does not allow tabs in filenames'
test_done
}
@@ -38,6 +39,7 @@ test_expect_success setup '
:
'
+test_expect_success 'setup expected files' '
cat >expect <<\EOF
rename pathname.1 => "Rpathname\twith HT.0" (100%)
rename pathname.3 => "Rpathname\nwith LF.0" (100%)
@@ -47,24 +49,40 @@ cat >expect <<\EOF
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 --numstat -M HEAD' '
+ cat >expect <<-\EOF &&
+ 0 0 pathname.1 => "Rpathname\twith HT.0"
+ 0 0 pathname.3 => "Rpathname\nwith LF.0"
+ 0 0 "pathname\twith HT.3" => "Rpathname\nwith LF.1"
+ 0 0 pathname.2 => Rpathname with SP.0
+ 0 0 "pathname\twith HT.2" => Rpathname with SP.1
+ 0 0 pathname.0 => Rpathname.0
+ 0 0 "pathname\twith HT.0" => Rpathname.1
+ EOF
+ git diff --numstat -M HEAD >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'git diff --stat -M HEAD' '
+ cat >expect <<-\EOF &&
+ pathname.1 => "Rpathname\twith HT.0" | 0
+ pathname.3 => "Rpathname\nwith LF.0" | 0
+ "pathname\twith HT.3" => "Rpathname\nwith LF.1" | 0
+ pathname.2 => Rpathname with SP.0 | 0
+ "pathname\twith HT.2" => Rpathname with SP.1 | 0
+ pathname.0 => Rpathname.0 | 0
+ "pathname\twith HT.0" => Rpathname.1 | 0
+ 7 files changed, 0 insertions(+), 0 deletions(-)
+ EOF
git diff --stat -M HEAD >actual &&
- test_cmp expect actual
+ test_i18ncmp expect actual
'
test_done
diff --git a/t/t4017-diff-retval.sh b/t/t4017-diff-retval.sh
index 6158985..95a7ca7 100755
--- a/t/t4017-diff-retval.sh
+++ b/t/t4017-diff-retval.sh
@@ -29,66 +29,49 @@ test_expect_success '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_code 1 git diff-tree --exit-code HEAD^ HEAD
'
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_code 1 git diff-tree --exit-code HEAD^ HEAD -- b
'
test_expect_success 'echo HEAD | git diff-tree --stdin' '
- echo $(git rev-parse HEAD) | git diff-tree --exit-code --stdin
- test $? = 1
+ echo $(git rev-parse HEAD) | test_expect_code 1 git diff-tree --exit-code --stdin
'
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_code 1 git diff-index --exit-code --cached HEAD^
'
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
- }
+ git add . &&
+ test_expect_code 1 git diff-index --exit-code --cached HEAD^
'
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
- }
+ git commit -m "text in b" &&
+ test_expect_code 1 git diff-tree -p --exit-code -Stext HEAD^ HEAD -- b
'
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
- }
+ echo 3 >>c &&
+ test_expect_code 1 git diff-files --exit-code
'
test_expect_success 'git diff-index --cached HEAD' '
- git update-index c && {
- git diff-index --exit-code --cached HEAD
- test $? = 1
- }
+ git update-index c &&
+ test_expect_code 1 git diff-index --exit-code --cached HEAD
'
test_expect_success '--check --exit-code returns 0 for no difference' '
@@ -100,30 +83,26 @@ test_expect_success '--check --exit-code returns 0 for no difference' '
test_expect_success '--check --exit-code returns 1 for a clean difference' '
echo "good" > a &&
- git diff --check --exit-code
- test $? = 1
+ test_expect_code 1 git diff --check --exit-code
'
test_expect_success '--check --exit-code returns 3 for a dirty difference' '
echo "bad " >> a &&
- git diff --check --exit-code
- test $? = 3
+ test_expect_code 3 git diff --check --exit-code
'
test_expect_success '--check with --no-pager returns 2 for dirty difference' '
- git --no-pager diff --check
- test $? = 2
+ test_expect_code 2 git --no-pager diff --check
'
test_expect_success 'check should test not just the last line' '
echo "" >>a &&
- git --no-pager diff --check
- test $? = 2
+ test_expect_code 2 git --no-pager diff --check
'
@@ -133,10 +112,8 @@ test_expect_success 'check detects leftover conflict markers' '
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
- ) &&
+ git add b &&
+ test_expect_code 2 git --no-pager diff --cached --check >test.out &&
test 3 = $(grep "conflict marker" test.out | wc -l) &&
git reset --hard
'
@@ -146,19 +123,13 @@ test_expect_success 'check honors conflict marker length' '
echo ">>>>>>> boo" >>b &&
echo "======" >>a &&
git diff --check a &&
- (
- git diff --check b
- test $? = 2
- ) &&
+ test_expect_code 2 git diff --check b &&
git reset --hard &&
echo ">>>>>>>> boo" >>b &&
echo "========" >>a &&
git diff --check &&
echo "b conflict-marker-size=8" >.gitattributes &&
- (
- git diff --check b
- test $? = 2
- ) &&
+ test_expect_code 2 git diff --check b &&
git diff --check a &&
git reset --hard
'
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 5b10e97..67373dc 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -7,78 +7,104 @@ test_description='Test custom diff function name patterns'
. ./test-lib.sh
-LF='
-'
+test_expect_success 'setup' '
+ # a non-trivial custom pattern
+ git config diff.custom1.funcname "!static
+!String
+[^ ].*s.*" &&
+
+ # a custom pattern which matches to end of line
+ git config diff.custom2.funcname "......Beer\$" &&
-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
+ # alternation in pattern
+ git config diff.custom3.funcname "Beer$" &&
+ git config diff.custom3.xfuncname "^[ ]*((public|static).*)$" &&
+
+ # for regexp compilation tests
+ echo A >A.java &&
+ echo B >B.java
+'
-sed 's/beer\\/beer,\\/' < Beer.java > Beer-correct.java
+diffpatterns="
+ ada
+ bibtex
+ cpp
+ csharp
+ fortran
+ fountain
+ html
+ java
+ matlab
+ objc
+ pascal
+ perl
+ php
+ python
+ ruby
+ tex
+ custom1
+ custom2
+ custom3
+"
-builtin_patterns="bibtex cpp html java objc pascal php python ruby tex"
-for p in $builtin_patterns
+for p in $diffpatterns
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 )
+ echo "*.java diff=$p" >.gitattributes &&
+ test_expect_code 1 git diff --no-index \
+ A.java B.java 2>msg &&
+ test_i18ngrep ! fatal msg &&
+ test_i18ngrep ! error msg
+ '
+ test_expect_success "builtin $p wordRegex pattern compiles" '
+ echo "*.java diff=$p" >.gitattributes &&
+ test_expect_code 1 git diff --no-index --word-diff \
+ A.java B.java 2>msg &&
+ test_i18ngrep ! fatal msg &&
+ test_i18ngrep ! error msg
'
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' '
+test_expect_success 'last regexp must not be negated' '
echo "*.java diff=java" >.gitattributes &&
- git diff --no-index Beer.java Beer-correct.java |
- grep "^@@.*@@ public static void main("
+ test_config diff.java.funcname "!static" &&
+ test_expect_code 128 git diff --no-index A.java B.java 2>msg &&
+ test_i18ngrep ": Last expression must not be negated:" msg
'
-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 'setup hunk header tests' '
+ for i in $diffpatterns
+ do
+ echo "$i-* diff=$i"
+ done > .gitattributes &&
-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:"
-'
+ # add all test files to the index
+ (
+ cd "$TEST_DIRECTORY"/t4018 &&
+ git --git-dir="$TRASH_DIRECTORY/.git" add .
+ ) &&
-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"
+ # place modified files in the worktree
+ for i in $(git ls-files)
+ do
+ sed -e "s/ChangeMe/IWasChanged/" <"$TEST_DIRECTORY/t4018/$i" >"$i" || return 1
+ done
'
-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("
-'
+# check each individual file
+for i in $(git ls-files)
+do
+ if grep broken "$i" >/dev/null 2>&1
+ then
+ result=failure
+ else
+ result=success
+ fi
+ test_expect_$result "hunk header: $i" "
+ test_when_finished 'cat actual' && # for debugging only
+ git diff -U1 $i >actual &&
+ grep '@@ .* @@.*RIGHT' actual
+ "
+done
test_done
diff --git a/t/t4018/README b/t/t4018/README
new file mode 100644
index 0000000..283e01cc
--- /dev/null
+++ b/t/t4018/README
@@ -0,0 +1,18 @@
+How to write RIGHT test cases
+=============================
+
+Insert the word "ChangeMe" (exactly this form) at a distance of
+at least two lines from the line that must appear in the hunk header.
+
+The text that must appear in the hunk header must contain the word
+"right", but in all upper-case, like in the title above.
+
+To mark a test case that highlights a malfunction, insert the word
+BROKEN in all lower-case somewhere in the file.
+
+This text is a bit twisted and out of order, but it is itself a
+test case for the default hunk header pattern. Know what you are doing
+if you change it.
+
+BTW, this tests that the head line goes to the hunk header, not the line
+of equal signs.
diff --git a/t/t4018/cpp-c++-function b/t/t4018/cpp-c++-function
new file mode 100644
index 0000000..9ee6bbe
--- /dev/null
+++ b/t/t4018/cpp-c++-function
@@ -0,0 +1,4 @@
+Item RIGHT::DoSomething( Args with_spaces )
+{
+ ChangeMe;
+}
diff --git a/t/t4018/cpp-class-constructor b/t/t4018/cpp-class-constructor
new file mode 100644
index 0000000..ec4f115
--- /dev/null
+++ b/t/t4018/cpp-class-constructor
@@ -0,0 +1,4 @@
+Item::Item(int RIGHT)
+{
+ ChangeMe;
+}
diff --git a/t/t4018/cpp-class-constructor-mem-init b/t/t4018/cpp-class-constructor-mem-init
new file mode 100644
index 0000000..49a69f3
--- /dev/null
+++ b/t/t4018/cpp-class-constructor-mem-init
@@ -0,0 +1,5 @@
+Item::Item(int RIGHT) :
+ member(0)
+{
+ ChangeMe;
+}
diff --git a/t/t4018/cpp-class-definition b/t/t4018/cpp-class-definition
new file mode 100644
index 0000000..11b61da
--- /dev/null
+++ b/t/t4018/cpp-class-definition
@@ -0,0 +1,4 @@
+class RIGHT
+{
+ int ChangeMe;
+};
diff --git a/t/t4018/cpp-class-definition-derived b/t/t4018/cpp-class-definition-derived
new file mode 100644
index 0000000..3b98cd0
--- /dev/null
+++ b/t/t4018/cpp-class-definition-derived
@@ -0,0 +1,5 @@
+class RIGHT :
+ public Baseclass
+{
+ int ChangeMe;
+};
diff --git a/t/t4018/cpp-class-destructor b/t/t4018/cpp-class-destructor
new file mode 100644
index 0000000..5487665
--- /dev/null
+++ b/t/t4018/cpp-class-destructor
@@ -0,0 +1,4 @@
+RIGHT::~RIGHT()
+{
+ ChangeMe;
+}
diff --git a/t/t4018/cpp-function-returning-global-type b/t/t4018/cpp-function-returning-global-type
new file mode 100644
index 0000000..1084d59
--- /dev/null
+++ b/t/t4018/cpp-function-returning-global-type
@@ -0,0 +1,4 @@
+::Item get::it::RIGHT()
+{
+ ChangeMe;
+}
diff --git a/t/t4018/cpp-function-returning-nested b/t/t4018/cpp-function-returning-nested
new file mode 100644
index 0000000..d9750aa
--- /dev/null
+++ b/t/t4018/cpp-function-returning-nested
@@ -0,0 +1,5 @@
+get::Item get::it::RIGHT()
+{
+ ChangeMe;
+}
+
diff --git a/t/t4018/cpp-function-returning-pointer b/t/t4018/cpp-function-returning-pointer
new file mode 100644
index 0000000..ef15657
--- /dev/null
+++ b/t/t4018/cpp-function-returning-pointer
@@ -0,0 +1,4 @@
+const char *get_it_RIGHT(char *ptr)
+{
+ ChangeMe;
+}
diff --git a/t/t4018/cpp-function-returning-reference b/t/t4018/cpp-function-returning-reference
new file mode 100644
index 0000000..01b051d
--- /dev/null
+++ b/t/t4018/cpp-function-returning-reference
@@ -0,0 +1,4 @@
+string& get::it::RIGHT(char *ptr)
+{
+ ChangeMe;
+}
diff --git a/t/t4018/cpp-gnu-style-function b/t/t4018/cpp-gnu-style-function
new file mode 100644
index 0000000..08c7c75
--- /dev/null
+++ b/t/t4018/cpp-gnu-style-function
@@ -0,0 +1,5 @@
+const char *
+RIGHT(int arg)
+{
+ ChangeMe;
+}
diff --git a/t/t4018/cpp-namespace-definition b/t/t4018/cpp-namespace-definition
new file mode 100644
index 0000000..6749980
--- /dev/null
+++ b/t/t4018/cpp-namespace-definition
@@ -0,0 +1,4 @@
+namespace RIGHT
+{
+ ChangeMe;
+}
diff --git a/t/t4018/cpp-operator-definition b/t/t4018/cpp-operator-definition
new file mode 100644
index 0000000..1acd827
--- /dev/null
+++ b/t/t4018/cpp-operator-definition
@@ -0,0 +1,4 @@
+Value operator+(Value LEFT, Value RIGHT)
+{
+ ChangeMe;
+}
diff --git a/t/t4018/cpp-skip-access-specifiers b/t/t4018/cpp-skip-access-specifiers
new file mode 100644
index 0000000..4d4a9db
--- /dev/null
+++ b/t/t4018/cpp-skip-access-specifiers
@@ -0,0 +1,8 @@
+class RIGHT : public Baseclass
+{
+public:
+protected:
+private:
+ void DoSomething();
+ int ChangeMe;
+};
diff --git a/t/t4018/cpp-skip-comment-block b/t/t4018/cpp-skip-comment-block
new file mode 100644
index 0000000..3800b99
--- /dev/null
+++ b/t/t4018/cpp-skip-comment-block
@@ -0,0 +1,9 @@
+struct item RIGHT(int i)
+// Do not
+// pick up
+/* these
+** comments.
+*/
+{
+ ChangeMe;
+}
diff --git a/t/t4018/cpp-skip-labels b/t/t4018/cpp-skip-labels
new file mode 100644
index 0000000..b9c10ab
--- /dev/null
+++ b/t/t4018/cpp-skip-labels
@@ -0,0 +1,8 @@
+void RIGHT (void)
+{
+repeat: // C++ comment
+next: /* C comment */
+ do_something();
+
+ ChangeMe;
+}
diff --git a/t/t4018/cpp-struct-definition b/t/t4018/cpp-struct-definition
new file mode 100644
index 0000000..521c59f
--- /dev/null
+++ b/t/t4018/cpp-struct-definition
@@ -0,0 +1,9 @@
+struct RIGHT {
+ unsigned
+ /* this bit field looks like a label and should not be picked up */
+ decoy_bitfield: 2,
+ more : 1;
+ int filler;
+
+ int ChangeMe;
+};
diff --git a/t/t4018/cpp-struct-single-line b/t/t4018/cpp-struct-single-line
new file mode 100644
index 0000000..a0de5fb
--- /dev/null
+++ b/t/t4018/cpp-struct-single-line
@@ -0,0 +1,7 @@
+void wrong()
+{
+}
+
+struct RIGHT_iterator_tag {};
+
+int ChangeMe;
diff --git a/t/t4018/cpp-template-function-definition b/t/t4018/cpp-template-function-definition
new file mode 100644
index 0000000..0cdf5ba
--- /dev/null
+++ b/t/t4018/cpp-template-function-definition
@@ -0,0 +1,4 @@
+template<class T> int RIGHT(T arg)
+{
+ ChangeMe;
+}
diff --git a/t/t4018/cpp-union-definition b/t/t4018/cpp-union-definition
new file mode 100644
index 0000000..7ec94df
--- /dev/null
+++ b/t/t4018/cpp-union-definition
@@ -0,0 +1,4 @@
+union RIGHT {
+ double v;
+ int ChangeMe;
+};
diff --git a/t/t4018/cpp-void-c-function b/t/t4018/cpp-void-c-function
new file mode 100644
index 0000000..153081e
--- /dev/null
+++ b/t/t4018/cpp-void-c-function
@@ -0,0 +1,4 @@
+void RIGHT (void)
+{
+ ChangeMe;
+}
diff --git a/t/t4018/custom1-pattern b/t/t4018/custom1-pattern
new file mode 100644
index 0000000..e8fd59f
--- /dev/null
+++ b/t/t4018/custom1-pattern
@@ -0,0 +1,17 @@
+public class Beer
+{
+ int special, RIGHT;
+ 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" // ChangeMe
+ + "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");
+ }
+}
diff --git a/t/t4018/custom2-match-to-end-of-line b/t/t4018/custom2-match-to-end-of-line
new file mode 100644
index 0000000..f88ac31
--- /dev/null
+++ b/t/t4018/custom2-match-to-end-of-line
@@ -0,0 +1,8 @@
+public class RIGHT_Beer
+{
+ int special;
+ public static void main(String args[])
+ {
+ System.out.print("ChangeMe");
+ }
+}
diff --git a/t/t4018/custom3-alternation-in-pattern b/t/t4018/custom3-alternation-in-pattern
new file mode 100644
index 0000000..5f3769c
--- /dev/null
+++ b/t/t4018/custom3-alternation-in-pattern
@@ -0,0 +1,17 @@
+public class Beer
+{
+ int special;
+ public static void main(String RIGHT[])
+ {
+ String s=" ";
+ for(int x = 99; x > 0; x--)
+ {
+ System.out.print(x + " bottles of beer on the wall "
+ + x + " bottles of beer\n" // ChangeMe
+ + "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");
+ }
+}
diff --git a/t/t4018/fountain-scene b/t/t4018/fountain-scene
new file mode 100644
index 0000000..6b3257d
--- /dev/null
+++ b/t/t4018/fountain-scene
@@ -0,0 +1,4 @@
+EXT. STREET RIGHT OUTSIDE - DAY
+
+CHARACTER
+You didn't say the magic phrase, "ChangeMe".
diff --git a/t/t4018/java-class-member-function b/t/t4018/java-class-member-function
new file mode 100644
index 0000000..298bc7a
--- /dev/null
+++ b/t/t4018/java-class-member-function
@@ -0,0 +1,8 @@
+public class Beer
+{
+ int special;
+ public static void main(String RIGHT[])
+ {
+ System.out.print("ChangeMe");
+ }
+}
diff --git a/t/t4018/perl-skip-end-of-heredoc b/t/t4018/perl-skip-end-of-heredoc
new file mode 100644
index 0000000..c22d39b
--- /dev/null
+++ b/t/t4018/perl-skip-end-of-heredoc
@@ -0,0 +1,8 @@
+sub RIGHTwithheredocument {
+ print <<"EOF"
+decoy here-doc
+EOF
+ # some lines of context
+ # to pad it out
+ print "ChangeMe\n";
+}
diff --git a/t/t4018/perl-skip-forward-decl b/t/t4018/perl-skip-forward-decl
new file mode 100644
index 0000000..a98cb8b
--- /dev/null
+++ b/t/t4018/perl-skip-forward-decl
@@ -0,0 +1,10 @@
+package RIGHT;
+
+use strict;
+use warnings;
+use parent qw(Exporter);
+our @EXPORT_OK = qw(round finalround);
+
+sub other; # forward declaration
+
+# ChangeMe
diff --git a/t/t4018/perl-skip-sub-in-pod b/t/t4018/perl-skip-sub-in-pod
new file mode 100644
index 0000000..e39f024
--- /dev/null
+++ b/t/t4018/perl-skip-sub-in-pod
@@ -0,0 +1,18 @@
+=head1 NAME
+
+Beer - subroutine to output fragment of a drinking song
+
+=head1 SYNOPSIS_RIGHT
+
+ use Beer qw(round finalround);
+
+ sub song {
+ for (my $i = 99; $i > 0; $i--) {
+ round $i;
+ }
+ finalround;
+ }
+
+ ChangeMe;
+
+=cut
diff --git a/t/t4018/perl-sub-definition b/t/t4018/perl-sub-definition
new file mode 100644
index 0000000..a507d1f
--- /dev/null
+++ b/t/t4018/perl-sub-definition
@@ -0,0 +1,4 @@
+sub RIGHT {
+ my ($n) = @_;
+ print "ChangeMe";
+}
diff --git a/t/t4018/perl-sub-definition-kr-brace b/t/t4018/perl-sub-definition-kr-brace
new file mode 100644
index 0000000..330b3df
--- /dev/null
+++ b/t/t4018/perl-sub-definition-kr-brace
@@ -0,0 +1,4 @@
+sub RIGHT
+{
+ print "ChangeMe\n";
+}
diff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh
index f6d1f1e..a501975 100755
--- a/t/t4019-diff-wserror.sh
+++ b/t/t4019-diff-wserror.sh
@@ -36,11 +36,12 @@ prepare_output () {
git diff --color >output
$grep_a "$blue_grep" output >error
$grep_a -v "$blue_grep" output >normal
+ return 0
}
test_expect_success default '
- prepare_output
+ prepare_output &&
grep Eight normal >/dev/null &&
grep HT error >/dev/null &&
@@ -50,10 +51,67 @@ test_expect_success default '
'
+test_expect_success 'default (attribute)' '
+
+ test_might_fail git config --unset core.whitespace &&
+ echo "F whitespace" >.gitattributes &&
+ prepare_output &&
+
+ grep Eight error >/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 'default, tabwidth=10 (attribute)' '
+
+ git config core.whitespace "tabwidth=10" &&
+ echo "F whitespace" >.gitattributes &&
+ 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 'no check (attribute)' '
+
+ test_might_fail git config --unset core.whitespace &&
+ echo "F -whitespace" >.gitattributes &&
+ prepare_output &&
+
+ grep Eight normal >/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 'no check, tabwidth=10 (attribute), must be irrelevant' '
+
+ git config core.whitespace "tabwidth=10" &&
+ echo "F -whitespace" >.gitattributes &&
+ prepare_output &&
+
+ grep Eight normal >/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 'without -trail' '
- git config core.whitespace -trail
- prepare_output
+ rm -f .gitattributes &&
+ git config core.whitespace -trail &&
+ prepare_output &&
grep Eight normal >/dev/null &&
grep HT error >/dev/null &&
@@ -65,9 +123,9 @@ test_expect_success 'without -trail' '
test_expect_success 'without -trail (attribute)' '
- git config --unset core.whitespace
- echo "F whitespace=-trail" >.gitattributes
- prepare_output
+ test_might_fail git config --unset core.whitespace &&
+ echo "F whitespace=-trail" >.gitattributes &&
+ prepare_output &&
grep Eight normal >/dev/null &&
grep HT error >/dev/null &&
@@ -79,9 +137,9 @@ test_expect_success 'without -trail (attribute)' '
test_expect_success 'without -space' '
- rm -f .gitattributes
- git config core.whitespace -space
- prepare_output
+ rm -f .gitattributes &&
+ git config core.whitespace -space &&
+ prepare_output &&
grep Eight normal >/dev/null &&
grep HT normal >/dev/null &&
@@ -93,9 +151,9 @@ test_expect_success 'without -space' '
test_expect_success 'without -space (attribute)' '
- git config --unset core.whitespace
- echo "F whitespace=-space" >.gitattributes
- prepare_output
+ test_might_fail git config --unset core.whitespace &&
+ echo "F whitespace=-space" >.gitattributes &&
+ prepare_output &&
grep Eight normal >/dev/null &&
grep HT normal >/dev/null &&
@@ -107,9 +165,9 @@ test_expect_success 'without -space (attribute)' '
test_expect_success 'with indent-non-tab only' '
- rm -f .gitattributes
- git config core.whitespace indent,-trailing,-space
- prepare_output
+ rm -f .gitattributes &&
+ git config core.whitespace indent,-trailing,-space &&
+ prepare_output &&
grep Eight error >/dev/null &&
grep HT normal >/dev/null &&
@@ -121,9 +179,9 @@ test_expect_success 'with indent-non-tab only' '
test_expect_success 'with indent-non-tab only (attribute)' '
- git config --unset core.whitespace
- echo "F whitespace=indent,-trailing,-space" >.gitattributes
- prepare_output
+ test_might_fail git config --unset core.whitespace &&
+ echo "F whitespace=indent,-trailing,-space" >.gitattributes &&
+ prepare_output &&
grep Eight error >/dev/null &&
grep HT normal >/dev/null &&
@@ -133,11 +191,39 @@ test_expect_success 'with indent-non-tab only (attribute)' '
'
+test_expect_success 'with indent-non-tab only, tabwidth=10' '
+
+ rm -f .gitattributes &&
+ git config core.whitespace indent,tabwidth=10,-trailing,-space &&
+ prepare_output &&
+
+ grep Eight normal >/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, tabwidth=10 (attribute)' '
+
+ test_might_fail git config --unset core.whitespace &&
+ echo "F whitespace=indent,-trailing,-space,tabwidth=10" >.gitattributes &&
+ prepare_output &&
+
+ grep Eight normal >/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
+ rm -f .gitattributes &&
+ git config core.whitespace cr-at-eol &&
+ prepare_output &&
grep Eight normal >/dev/null &&
grep HT error >/dev/null &&
@@ -149,9 +235,9 @@ test_expect_success 'with cr-at-eol' '
test_expect_success 'with cr-at-eol (attribute)' '
- git config --unset core.whitespace
- echo "F whitespace=trailing,cr-at-eol" >.gitattributes
- prepare_output
+ test_might_fail 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 &&
@@ -178,12 +264,21 @@ test_expect_success 'trailing empty lines (2)' '
'
+test_expect_success 'checkdiff shows correct line number for trailing blank lines' '
+
+ printf "a\nb\n" > G &&
+ git add G &&
+ printf "x\nx\nx\na\nb\nc\n\n" > G &&
+ [ "$(git diff --check -- G)" = "G:7: new blank line at EOF." ]
+
+'
+
test_expect_success 'do not color trailing cr in context' '
- git config --unset core.whitespace
+ test_might_fail git config --unset core.whitespace &&
rm -f .gitattributes &&
echo AAAQ | tr Q "\015" >G &&
git add G &&
- echo BBBQ | tr Q "\015" >>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
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index a7602cf..0446201 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -4,8 +4,6 @@ test_description='external diff interface test'
. ./test-lib.sh
-_z40=0000000000000000000000000000000000000000
-
test_expect_success setup '
test_tick &&
@@ -50,7 +48,53 @@ test_expect_success 'GIT_EXTERNAL_DIFF environment and --no-ext-diff' '
'
+test_expect_success SYMLINKS 'typechange diff' '
+ rm -f file &&
+ ln -s elif file &&
+ GIT_EXTERNAL_DIFF=echo git diff | {
+ read path oldfile oldhex oldmode newfile newhex newmode &&
+ test "z$path" = zfile &&
+ test "z$oldmode" = z100644 &&
+ test "z$newhex" = "z$_z40" &&
+ test "z$newmode" = z120000 &&
+ oh=$(git rev-parse --verify HEAD:file) &&
+ test "z$oh" = "z$oldhex"
+ } &&
+ GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff >actual &&
+ git diff >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'diff.external' '
+ git reset --hard &&
+ echo third >file &&
+ test_config diff.external echo &&
+ git diff | {
+ read path oldfile oldhex oldmode newfile newhex newmode &&
+ test "z$path" = zfile &&
+ test "z$oldmode" = z100644 &&
+ test "z$newhex" = "z$_z40" &&
+ test "z$newmode" = z100644 &&
+ oh=$(git rev-parse --verify HEAD:file) &&
+ test "z$oh" = "z$oldhex"
+ }
+'
+
+test_expect_success 'diff.external should apply only to diff' '
+ test_config diff.external echo &&
+ git log -p -1 HEAD |
+ grep "^diff --git a/file b/file"
+'
+
+test_expect_success 'diff.external and --no-ext-diff' '
+ test_config diff.external echo &&
+ git diff --no-ext-diff |
+ grep "^diff --git a/file b/file"
+'
+
test_expect_success 'diff attribute' '
+ git reset --hard &&
+ echo third >file &&
git config diff.parrot.command echo &&
@@ -115,6 +159,19 @@ test_expect_success 'diff attribute and --no-ext-diff' '
'
+test_expect_success 'GIT_EXTERNAL_DIFF trumps diff.external' '
+ >.gitattributes &&
+ test_config diff.external "echo ext-global" &&
+ GIT_EXTERNAL_DIFF="echo ext-env" git diff | grep ext-env
+'
+
+test_expect_success 'attributes trump GIT_EXTERNAL_DIFF and diff.external' '
+ test_config diff.foo.command "echo ext-attribute" &&
+ test_config diff.external "echo ext-global" &&
+ echo "file diff=foo" >.gitattributes &&
+ GIT_EXTERNAL_DIFF="echo ext-env" git diff | grep ext-attribute
+'
+
test_expect_success 'no diff with -diff' '
echo >.gitattributes "file -diff" &&
git diff | grep Binary
@@ -136,6 +193,19 @@ test_expect_success 'GIT_EXTERNAL_DIFF with more than one changed files' '
GIT_EXTERNAL_DIFF=echo git diff
'
+test_expect_success 'GIT_EXTERNAL_DIFF path counter/total' '
+ write_script external-diff.sh <<-\EOF &&
+ echo $GIT_DIFF_PATH_COUNTER of $GIT_DIFF_PATH_TOTAL >>counter.txt
+ EOF
+ >counter.txt &&
+ cat >expect <<-\EOF &&
+ 1 of 2
+ 2 of 2
+ EOF
+ GIT_EXTERNAL_DIFF=./external-diff.sh git diff &&
+ test_cmp expect counter.txt
+'
+
test_expect_success 'GIT_EXTERNAL_DIFF generates pretty paths' '
touch file.ext &&
git add file.ext &&
@@ -156,12 +226,13 @@ keep_only_cr () {
}
test_expect_success 'external diff with autocrlf = true' '
- git config core.autocrlf true &&
+ test_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' '
+ test_config core.autocrlf true &&
git add file &&
git update-index --assume-unchanged file &&
echo second >file &&
@@ -169,4 +240,31 @@ test_expect_success 'diff --cached' '
test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual
'
+test_expect_success 'clean up crlf leftovers' '
+ git update-index --no-assume-unchanged file &&
+ rm -f file* &&
+ git reset --hard
+'
+
+test_expect_success 'submodule diff' '
+ git init sub &&
+ ( cd sub && test_commit sub1 ) &&
+ git add sub &&
+ test_tick &&
+ git commit -m "add submodule" &&
+ ( cd sub && test_commit sub2 ) &&
+ write_script gather_pre_post.sh <<-\EOF &&
+ echo "$1 $4" # path, mode
+ cat "$2" # old file
+ cat "$5" # new file
+ EOF
+ GIT_EXTERNAL_DIFF=./gather_pre_post.sh git diff >actual &&
+ cat >expected <<-EOF &&
+ sub 160000
+ Subproject commit $(git rev-parse HEAD:sub)
+ Subproject commit $(cd sub && git rev-parse HEAD)
+ EOF
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh
index 709b323..886494b 100755
--- a/t/t4021-format-patch-numbered.sh
+++ b/t/t4021-format-patch-numbered.sh
@@ -95,7 +95,7 @@ test_expect_success 'format.numbered && --keep-subject' '
test_expect_success 'format.numbered = auto' '
- git config format.numbered auto
+ git config format.numbered auto &&
git format-patch --stdout HEAD~2 > patch5 &&
test_numbered patch5
diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh
index 2a537a2..cb51d9f 100755
--- a/t/t4022-diff-rewrite.sh
+++ b/t/t4022-diff-rewrite.sh
@@ -11,19 +11,87 @@ test_expect_success setup '
tr \
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \
"nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM" \
- <"$TEST_DIRECTORY"/../COPYING >test
+ <"$TEST_DIRECTORY"/../COPYING >test &&
+ echo "to be deleted" >test2 &&
+ git add test2
'
test_expect_success 'detect rewrite' '
actual=$(git diff-files -B --summary test) &&
- expr "$actual" : " rewrite test ([0-9]*%)$" || {
- echo "Eh? <<$actual>>"
- false
- }
+ verbose expr "$actual" : " rewrite test ([0-9]*%)$"
'
+cat >expect <<EOF
+diff --git a/test2 b/test2
+deleted file mode 100644
+index 4202011..0000000
+--- a/test2
++++ /dev/null
+@@ -1 +0,0 @@
+-to be deleted
+EOF
+test_expect_success 'show deletion diff without -D' '
+
+ rm test2 &&
+ git diff -- test2 >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+diff --git a/test2 b/test2
+deleted file mode 100644
+index 4202011..0000000
+EOF
+test_expect_success 'suppress deletion diff with -D' '
+
+ git diff -D -- test2 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'show deletion diff with -B' '
+
+ git diff -B -- test >actual &&
+ grep "Linus Torvalds" actual
+'
+
+test_expect_success 'suppress deletion diff with -B -D' '
+
+ git diff -B -D -- test >actual &&
+ grep -v "Linus Torvalds" actual
+'
+
+test_expect_success 'prepare a file that ends with an incomplete line' '
+ test_seq 1 99 >seq &&
+ printf 100 >>seq &&
+ git add seq &&
+ git commit seq -m seq
+'
+
+test_expect_success 'rewrite the middle 90% of sequence file and terminate with newline' '
+ test_seq 1 5 >seq &&
+ test_seq 9331 9420 >>seq &&
+ test_seq 96 100 >>seq
+'
+
+test_expect_success 'confirm that sequence file is considered a rewrite' '
+ git diff -B seq >res &&
+ grep "dissimilarity index" res
+'
+
+test_expect_success 'no newline at eof is on its own line without -B' '
+ git diff seq >res &&
+ grep "^\\\\ " res &&
+ ! grep "^..*\\\\ " res
+'
+
+test_expect_success 'no newline at eof is on its own line with -B' '
+ git diff -B seq >res &&
+ grep "^\\\\ " res &&
+ ! grep "^..*\\\\ " res
+'
+
test_done
diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh
index 40a95a1..8c98237 100755
--- a/t/t4023-diff-rename-typechange.sh
+++ b/t/t4023-diff-rename-typechange.sh
@@ -4,50 +4,44 @@ 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 &&
+ test_ln_s_add linklink bar &&
+ git add foo &&
git commit -a -m Initial &&
git tag one &&
- rm -f foo bar &&
+ git rm -f foo bar &&
cat "$TEST_DIRECTORY"/../COPYING >bar &&
- ln -s linklink foo &&
- git add foo bar &&
+ test_ln_s_add linklink foo &&
+ git add bar &&
git commit -a -m Second &&
git tag two &&
- rm -f foo bar &&
+ git rm -f foo bar &&
cat "$TEST_DIRECTORY"/../COPYING >foo &&
git add foo &&
git commit -a -m Third &&
git tag three &&
mv foo bar &&
- ln -s linklink foo &&
- git add foo bar &&
+ test_ln_s_add linklink foo &&
+ git add bar &&
git commit -a -m Fourth &&
git tag four &&
# This is purely for sanity check
- rm -f foo bar &&
+ git rm -f foo bar &&
cat "$TEST_DIRECTORY"/../COPYING >foo &&
cat "$TEST_DIRECTORY"/../Makefile >bar &&
git add foo bar &&
git commit -a -m Fifth &&
git tag five &&
- rm -f foo bar &&
+ git rm -f foo bar &&
cat "$TEST_DIRECTORY"/../Makefile >foo &&
cat "$TEST_DIRECTORY"/../COPYING >bar &&
git add foo bar &&
@@ -82,7 +76,8 @@ test_expect_success 'moves and renames' '
git diff-tree three four -r --name-status -B -M | sort >actual &&
{
- echo "R100 foo bar"
+ # see -B -M (#6) in t4008
+ echo "C100 foo bar"
echo "T100 foo"
} | sort >expect &&
test_cmp expect actual
diff --git a/t/t4024-diff-optimize-common.sh b/t/t4024-diff-optimize-common.sh
index c4d733f..7e76018 100755
--- a/t/t4024-diff-optimize-common.sh
+++ b/t/t4024-diff-optimize-common.sh
@@ -139,7 +139,7 @@ test_expect_success setup '
( printf C; zs $n ) >file-c$n &&
( echo D; zs $n ) >file-d$n &&
- expect_pattern $n || break
+ expect_pattern $n || return 1
done >expect
'
diff --git a/t/t4026-color.sh b/t/t4026-color.sh
index d5ccdd0..2b32c4f 100755
--- a/t/t4026-color.sh
+++ b/t/t4026-color.sh
@@ -45,14 +45,41 @@ test_expect_success 'fg bg attr...' '
color "blue bold dim ul blink reverse" "[1;2;4;5;7;34m"
'
+# note that nobold and nodim are the same code (22)
+test_expect_success 'attr negation' '
+ color "nobold nodim noul noblink noreverse" "[22;24;25;27m"
+'
+
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 'absurdly long color specification' '
+ color \
+ "#ffffff #ffffff bold nobold dim nodim ul noul blink noblink reverse noreverse" \
+ "[1;2;4;5;7;22;24;25;27;38;2;255;255;255;48;2;255;255;255m"
+'
+
+test_expect_success '0-7 are aliases for basic ANSI color names' '
+ color "0 7" "[30;47m"
+'
+
test_expect_success '256 colors' '
color "254 bold 255" "[1;38;5;254;48;5;255m"
'
+test_expect_success '24-bit colors' '
+ color "#ff00ff black" "[38;2;255;0;255;40m"
+'
+
+test_expect_success '"normal" yields no color at all"' '
+ color "normal black" "[40m"
+'
+
+test_expect_success '-1 is a synonym for "normal"' '
+ color "-1 black" "[40m"
+'
+
test_expect_success 'color too small' '
invalid_color "-2"
'
@@ -74,7 +101,6 @@ test_expect_success 'extra character after attribute' '
'
test_expect_success 'unknown color slots are ignored (diff)' '
- git config --unset diff.color.new
git config color.diff.nosuchslotwilleverbedefined white &&
git diff --color
'
@@ -85,9 +111,9 @@ test_expect_success 'unknown color slots are ignored (branch)' '
'
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
+ git config color.status.nosuchslotwilleverbedefined white &&
+ { git status; ret=$?; } &&
+ case $ret in 0|1) : ok ;; *) false ;; esac
'
test_done
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index 1bd8e5e..518bf95 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -5,7 +5,6 @@ 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 &&
@@ -114,6 +113,69 @@ test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match)'
! test -s actual4
'
+test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.git/config]' '
+ git config diff.ignoreSubmodules all &&
+ git diff HEAD >actual &&
+ ! test -s actual &&
+ git config submodule.subname.ignore none &&
+ git config submodule.subname.path sub &&
+ 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 config submodule.subname.ignore all &&
+ git diff HEAD >actual2 &&
+ ! test -s actual2 &&
+ git config submodule.subname.ignore untracked &&
+ git diff HEAD >actual3 &&
+ sed -e "1,/^@@/d" actual3 >actual3.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual3.body &&
+ git config submodule.subname.ignore dirty &&
+ git diff HEAD >actual4 &&
+ ! test -s actual4 &&
+ git diff HEAD --ignore-submodules=none >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body &&
+ git config --remove-section submodule.subname &&
+ git config --unset diff.ignoreSubmodules
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.gitmodules]' '
+ git config diff.ignoreSubmodules dirty &&
+ git diff HEAD >actual &&
+ ! test -s actual &&
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sub &&
+ 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 config -f .gitmodules submodule.subname.ignore all &&
+ git config -f .gitmodules submodule.subname.path sub &&
+ git diff HEAD >actual2 &&
+ ! test -s actual2 &&
+ git config -f .gitmodules submodule.subname.ignore untracked &&
+ git diff HEAD >actual3 &&
+ sed -e "1,/^@@/d" actual3 >actual3.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual3.body &&
+ git config -f .gitmodules submodule.subname.ignore dirty &&
+ git diff HEAD >actual4 &&
+ ! test -s actual4 &&
+ git config submodule.subname.ignore none &&
+ git config submodule.subname.path sub &&
+ 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 config --remove-section submodule.subname &&
+ git config --remove-section -f .gitmodules submodule.subname &&
+ git config --unset diff.ignoreSubmodules &&
+ rm .gitmodules
+'
+
test_expect_success 'git diff HEAD with dirty submodule (index, refs match)' '
(
cd sub &&
@@ -146,6 +208,103 @@ test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match)'
! test -s actual4
'
+test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.git/config]' '
+ git config submodule.subname.ignore all &&
+ git config submodule.subname.path sub &&
+ git diff HEAD >actual2 &&
+ ! test -s actual2 &&
+ git config submodule.subname.ignore untracked &&
+ git diff HEAD >actual3 &&
+ ! test -s actual3 &&
+ git config submodule.subname.ignore dirty &&
+ git diff HEAD >actual4 &&
+ ! test -s actual4 &&
+ git diff --ignore-submodules=none HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body &&
+ git config --remove-section submodule.subname
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.gitmodules]' '
+ git config --add -f .gitmodules submodule.subname.ignore all &&
+ git config --add -f .gitmodules submodule.subname.path sub &&
+ git diff HEAD >actual2 &&
+ ! test -s actual2 &&
+ git config -f .gitmodules submodule.subname.ignore untracked &&
+ git diff HEAD >actual3 &&
+ ! test -s actual3 &&
+ git config -f .gitmodules submodule.subname.ignore dirty &&
+ git diff HEAD >actual4 &&
+ ! test -s actual4 &&
+ git config submodule.subname.ignore none &&
+ git config submodule.subname.path sub &&
+ 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 config --remove-section submodule.subname &&
+ git config --remove-section -f .gitmodules submodule.subname &&
+ rm .gitmodules
+'
+
+test_expect_success 'git diff between submodule commits' '
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git diff --ignore-submodules=dirty HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git diff --ignore-submodules HEAD^..HEAD >actual &&
+ ! test -s actual
+'
+
+test_expect_success 'git diff between submodule commits [.git/config]' '
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git config submodule.subname.ignore dirty &&
+ git config submodule.subname.path sub &&
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git config submodule.subname.ignore all &&
+ git diff HEAD^..HEAD >actual &&
+ ! test -s actual &&
+ git diff --ignore-submodules=dirty HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ git config --remove-section submodule.subname
+'
+
+test_expect_success 'git diff between submodule commits [.gitmodules]' '
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git config --add -f .gitmodules submodule.subname.ignore dirty &&
+ git config --add -f .gitmodules submodule.subname.path sub &&
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git config -f .gitmodules submodule.subname.ignore all &&
+ git diff HEAD^..HEAD >actual &&
+ ! test -s actual &&
+ git config submodule.subname.ignore dirty &&
+ git config submodule.subname.path sub &&
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ git config --remove-section submodule.subname &&
+ git config --remove-section -f .gitmodules submodule.subname &&
+ rm .gitmodules
+'
+
test_expect_success 'git diff (empty submodule dir)' '
: >empty &&
rm -rf sub/* sub/.git &&
@@ -156,11 +315,11 @@ test_expect_success 'git diff (empty submodule dir)' '
test_expect_success 'conflicted submodule setup' '
# 39 efs
- c=fffffffffffffffffffffffffffffffffffffff
+ c=fffffffffffffffffffffffffffffffffffffff &&
(
- echo "000000 $_z40 0 sub"
- echo "160000 1$c 1 sub"
- echo "160000 2$c 2 sub"
+ 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
diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh
index 88c5619..aad6c7f 100755
--- a/t/t4030-diff-textconv.sh
+++ b/t/t4030-diff-textconv.sh
@@ -21,7 +21,7 @@ EOF
cat >hexdump <<'EOF'
#!/bin/sh
-perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1"
+"$PERL_PATH" -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1"
EOF
chmod +x hexdump
@@ -58,6 +58,12 @@ test_expect_success 'diff produces text' '
test_cmp expect.text actual
'
+test_expect_success 'show commit produces text' '
+ git show HEAD >diff &&
+ find_diff <diff >actual &&
+ test_cmp expect.text actual
+'
+
test_expect_success 'diff-tree produces binary' '
git diff-tree -p HEAD^ HEAD >diff &&
find_diff <diff >actual &&
@@ -84,14 +90,60 @@ test_expect_success 'status -v produces text' '
git reset --soft HEAD@{1}
'
+test_expect_success 'show blob produces binary' '
+ git show HEAD:file >actual &&
+ printf "\\0\\n\\01\\n" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'show --textconv blob produces text' '
+ git show --textconv HEAD:file >actual &&
+ printf "0\\n1\\n" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'show --no-textconv blob produces binary' '
+ git show --no-textconv HEAD:file >actual &&
+ printf "\\0\\n\\01\\n" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'grep-diff (-G) operates on textconv data (add)' '
+ echo one >expect &&
+ git log --root --format=%s -G0 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'grep-diff (-G) operates on textconv data (modification)' '
+ echo two >expect &&
+ git log --root --format=%s -G1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pickaxe (-S) operates on textconv data (add)' '
+ echo one >expect &&
+ git log --root --format=%s -S0 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pickaxe (-S) operates on textconv data (modification)' '
+ echo two >expect &&
+ git log --root --format=%s -S1 >actual &&
+ test_cmp expect actual
+'
+
cat >expect.stat <<'EOF'
- file | Bin 2 -> 4 bytes
- 1 files changed, 0 insertions(+), 0 deletions(-)
+ file | Bin 2 -> 4 bytes
+ 1 file changed, 0 insertions(+), 0 deletions(-)
EOF
test_expect_success 'diffstat does not run textconv' '
echo file diff=fail >.gitattributes &&
git diff --stat HEAD^ HEAD >actual &&
- test_cmp expect.stat actual
+ test_i18ncmp expect.stat actual &&
+
+ head -n1 <expect.stat >expect.line1 &&
+ head -n1 <actual >actual.line1 &&
+ test_cmp expect.line1 actual.line1
'
# restore working setup
echo file diff=foo >.gitattributes
@@ -111,12 +163,10 @@ index 0000000..67be421
+frotz
\ No newline at end of file
EOF
-# make a symlink the hard way that works on symlink-challenged file systems
+
test_expect_success 'textconv does not act on symlinks' '
- printf frotz > file &&
- git add file &&
- git ls-files -s | sed -e s/100644/120000/ |
- git update-index --index-info &&
+ rm -f file &&
+ test_ln_s_add frotz file &&
git commit -m typechange &&
git show >diff &&
find_diff <diff >actual &&
diff --git a/t/t4031-diff-rewrite-binary.sh b/t/t4031-diff-rewrite-binary.sh
index 7e7b307..eacc669 100755
--- a/t/t4031-diff-rewrite-binary.sh
+++ b/t/t4031-diff-rewrite-binary.sh
@@ -44,10 +44,23 @@ test_expect_success 'rewrite diff can show binary patch' '
grep "GIT binary patch" diff
'
+test_expect_success 'rewrite diff --numstat shows binary changes' '
+ git diff -B --numstat --summary >diff &&
+ grep -e "- - " diff &&
+ grep " rewrite file" diff
+'
+
+test_expect_success 'diff --stat counts binary rewrite as 0 lines' '
+ git diff -B --stat --summary >diff &&
+ grep "Bin" diff &&
+ test_i18ngrep "0 insertions.*0 deletions" diff &&
+ grep " rewrite file" diff
+'
+
{
echo "#!$SHELL_PATH"
cat <<'EOF'
-perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1"
+"$PERL_PATH" -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1"
EOF
} >dump
chmod +x dump
diff --git a/t/t4033-diff-patience.sh b/t/t4033-diff-patience.sh
index 1eb1498..3c9932e 100755
--- a/t/t4033-diff-patience.sh
+++ b/t/t4033-diff-patience.sh
@@ -3,166 +3,10 @@
test_description='patience diff algorithm'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-alternative.sh
-cat >file1 <<\EOF
-#include <stdio.h>
+test_diff_frobnitz "patience"
-// 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_diff_unique "patience"
test_done
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index 6f7548c..f2f55fc 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -3,332 +3,385 @@
test_description='word diff colors'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
-test_expect_success setup '
+cat >pre.simple <<-\EOF
+ h(4)
- git config diff.color.old red
- git config diff.color.new green
- git config diff.color.func magenta
+ a = b + c
+EOF
+cat >post.simple <<-\EOF
+ h(4),hh[44]
-'
+ a = b + c
-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)
+ aa = a
-a = b + c
+ aeff = aeff * ( aaa )
EOF
+cat >expect.letter-runs-are-words <<-\EOF
+ <BOLD>diff --git a/pre b/post<RESET>
+ <BOLD>index 330b04f..5ed8eff 100644<RESET>
+ <BOLD>--- a/pre<RESET>
+ <BOLD>+++ b/post<RESET>
+ <CYAN>@@ -1,3 +1,7 @@<RESET>
+ h(4),<GREEN>hh<RESET>[44]
-cat > post <<\EOF
-h(4),hh[44]
-
-a = b + c
+ a = b + c<RESET>
-aa = a
+ <GREEN>aa = a<RESET>
-aeff = aeff * ( aaa )
+ <GREEN>aeff = aeff * ( aaa<RESET> )
EOF
+cat >expect.non-whitespace-is-word <<-\EOF
+ <BOLD>diff --git a/pre b/post<RESET>
+ <BOLD>index 330b04f..5ed8eff 100644<RESET>
+ <BOLD>--- a/pre<RESET>
+ <BOLD>+++ b/post<RESET>
+ <CYAN>@@ -1,3 +1,7 @@<RESET>
+ h(4)<GREEN>,hh[44]<RESET>
-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>
-a = b + c<RESET>
+ <GREEN>aa = a<RESET>
-<GREEN>aa = a<RESET>
-
-<GREEN>aeff = aeff * ( aaa )<RESET>
+ <GREEN>aeff = aeff * ( aaa )<RESET>
EOF
-test_expect_success 'word diff with runs of whitespace' '
+word_diff () {
+ test_must_fail git diff --no-index "$@" pre post >output &&
+ test_decode_color <output >output.decrypted &&
+ test_cmp expect output.decrypted
+}
- word_diff --color-words
+test_language_driver () {
+ lang=$1
+ test_expect_success "diff driver '$lang'" '
+ cp "$TEST_DIRECTORY/t4034/'"$lang"'/pre" \
+ "$TEST_DIRECTORY/t4034/'"$lang"'/post" \
+ "$TEST_DIRECTORY/t4034/'"$lang"'/expect" . &&
+ echo "* diff='"$lang"'" >.gitattributes &&
+ word_diff --color-words
+ '
+}
+test_expect_success setup '
+ git config diff.color.old red &&
+ git config diff.color.new green &&
+ git config diff.color.func magenta
'
-test_expect_success '--word-diff=color' '
-
- word_diff --word-diff=color
-
+test_expect_success 'set up pre and post with runs of whitespace' '
+ cp pre.simple pre &&
+ cp post.simple post
'
-test_expect_success '--color --word-diff=color' '
-
+test_expect_success 'word diff with runs of whitespace' '
+ cat >expect <<-\EOF &&
+ <BOLD>diff --git a/pre b/post<RESET>
+ <BOLD>index 330b04f..5ed8eff 100644<RESET>
+ <BOLD>--- a/pre<RESET>
+ <BOLD>+++ 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
+ word_diff --color-words &&
+ word_diff --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' '
-
+ 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
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' '
+ 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]+}
- word_diff --word-diff=plain
-
-'
+ a = b + c
-test_expect_success '--word-diff=plain --no-color' '
+ {+aa = a+}
+ {+aeff = aeff * ( aaa )+}
+ EOF
+ word_diff --word-diff=plain &&
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>
+test_expect_success '--word-diff=plain --color' '
+ cat >expect <<-\EOF &&
+ <BOLD>diff --git a/pre b/post<RESET>
+ <BOLD>index 330b04f..5ed8eff 100644<RESET>
+ <BOLD>--- a/pre<RESET>
+ <BOLD>+++ b/post<RESET>
+ <CYAN>@@ -1,3 +1,7 @@<RESET>
+ <RED>[-h(4)-]<RESET><GREEN>{+h(4),hh[44]+}<RESET>
-<GREEN>{+aeff = aeff * ( aaa )+}<RESET>
-EOF
+ a = b + c<RESET>
-test_expect_success '--word-diff=plain --color' '
+ <GREEN>{+aa = a+}<RESET>
+ <GREEN>{+aeff = aeff * ( aaa )+}<RESET>
+ EOF
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' '
-
+ cat >expect <<-\EOF &&
+ <BOLD>diff --git a/pre b/post<RESET>
+ <BOLD>index 330b04f..5ed8eff 100644<RESET>
+ <BOLD>--- a/pre<RESET>
+ <BOLD>+++ 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
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' '
-
+ cp expect.letter-runs-are-words expect &&
word_diff --color-words="[a-z]+"
-
'
-test_expect_success 'set a diff driver' '
+test_expect_success 'set up a diff driver' '
git config diff.testdriver.wordRegex "[^[:space:]]" &&
- cat <<EOF > .gitattributes
-pre diff=testdriver
-post diff=testdriver
-EOF
+ cat <<-\EOF >.gitattributes
+ pre diff=testdriver
+ post diff=testdriver
+ EOF
'
test_expect_success 'option overrides .gitattributes' '
-
+ cp expect.letter-runs-are-words expect &&
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' '
-
+ cp expect.non-whitespace-is-word expect &&
word_diff --color-words
-
'
-test_expect_success 'set diff.wordRegex option' '
+test_expect_success 'set up diff.wordRegex option' '
git config diff.wordRegex "[[:alnum:]]+"
'
-cp expect.letter-runs-are-words expect
-
test_expect_success 'command-line overrides config' '
+ cp expect.letter-runs-are-words expect &&
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>
+test_expect_success 'command-line overrides config: --word-diff-regex' '
+ cat >expect <<-\EOF &&
+ <BOLD>diff --git a/pre b/post<RESET>
+ <BOLD>index 330b04f..5ed8eff 100644<RESET>
+ <BOLD>--- a/pre<RESET>
+ <BOLD>+++ b/post<RESET>
+ <CYAN>@@ -1,3 +1,7 @@<RESET>
+ h(4),<GREEN>{+hh+}<RESET>[44]
-<GREEN>{+aa = a+}<RESET>
+ a = b + c<RESET>
-<GREEN>{+aeff = aeff * ( aaa+}<RESET> )
-EOF
+ <GREEN>{+aa = a+}<RESET>
-test_expect_success 'command-line overrides config: --word-diff-regex' '
+ <GREEN>{+aeff = aeff * ( aaa+}<RESET> )
+ EOF
word_diff --color --word-diff-regex="[a-z]+"
'
-cp expect.non-whitespace-is-word expect
-
test_expect_success '.gitattributes override config' '
+ cp expect.non-whitespace-is-word expect &&
word_diff --color-words
'
-test_expect_success 'remove diff driver regex' '
- git config --unset diff.testdriver.wordRegex
+test_expect_success 'setup: remove diff driver regex' '
+ test_unconfig 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>
+test_expect_success 'use configured regex' '
+ cat >expect <<-\EOF &&
+ <BOLD>diff --git a/pre b/post<RESET>
+ <BOLD>index 330b04f..5ed8eff 100644<RESET>
+ <BOLD>--- a/pre<RESET>
+ <BOLD>+++ b/post<RESET>
+ <CYAN>@@ -1,3 +1,7 @@<RESET>
+ h(4),<GREEN>hh[44<RESET>]
-<GREEN>aa = a<RESET>
+ a = b + c<RESET>
-<GREEN>aeff = aeff * ( aaa<RESET> )
-EOF
+ <GREEN>aa = a<RESET>
-test_expect_success 'use configured regex' '
+ <GREEN>aeff = aeff * ( aaa<RESET> )
+ EOF
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' '
-
+ echo "aaa (aaa)" >pre &&
+ echo "aaa (aaa) aaa" >post &&
+ cat >expect <<-\EOF &&
+ <BOLD>diff --git a/pre b/post<RESET>
+ <BOLD>index c29453b..be22f37 100644<RESET>
+ <BOLD>--- a/pre<RESET>
+ <BOLD>+++ b/post<RESET>
+ <CYAN>@@ -1 +1 @@<RESET>
+ aaa (aaa) <GREEN>aaa<RESET>
+ EOF
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' '
-
+ echo "(:" >pre &&
+ echo "(" >post &&
+ cat >expect <<-\EOF &&
+ <BOLD>diff --git a/pre b/post<RESET>
+ <BOLD>index 289cb9d..2d06f37 100644<RESET>
+ <BOLD>--- a/pre<RESET>
+ <BOLD>+++ b/post<RESET>
+ <CYAN>@@ -1 +1 @@<RESET>
+ (<RED>:<RESET>
+ EOF
word_diff --color-words=.
+'
+test_expect_success '--word-diff=none' '
+ echo "(:" >pre &&
+ echo "(" >post &&
+ cat >expect <<-\EOF &&
+ diff --git a/pre b/post
+ index 289cb9d..2d06f37 100644
+ --- a/pre
+ +++ b/post
+ @@ -1 +1 @@
+ -(:
+ +(
+ EOF
+ word_diff --word-diff=plain --word-diff=none
'
-cat > expect <<\EOF
-diff --git a/pre b/post
-index 289cb9d..2d06f37 100644
---- a/pre
-+++ b/post
-@@ -1 +1 @@
--(:
-+(
-EOF
+test_expect_success 'unset default driver' '
+ test_unconfig diff.wordregex
+'
-test_expect_success '--word-diff=none' '
+test_language_driver ada
+test_language_driver bibtex
+test_language_driver cpp
+test_language_driver csharp
+test_language_driver fortran
+test_language_driver html
+test_language_driver java
+test_language_driver matlab
+test_language_driver objc
+test_language_driver pascal
+test_language_driver perl
+test_language_driver php
+test_language_driver python
+test_language_driver ruby
+test_language_driver tex
+
+test_expect_success 'word-diff with diff.sbe' '
+ cat >expect <<-\EOF &&
+ diff --git a/pre b/post
+ index a1a53b5..bc8fe6d 100644
+ --- a/pre
+ +++ b/post
+ @@ -1,3 +1,3 @@
+ a
+
+ [-b-]{+c+}
+ EOF
+ cat >pre <<-\EOF &&
+ a
+
+ b
+ EOF
+ cat >post <<-\EOF &&
+ a
+
+ c
+ EOF
+ test_config diff.suppress-blank-empty true &&
+ word_diff --word-diff=plain
+'
- word_diff --word-diff=plain --word-diff=none
+test_expect_success 'word-diff with no newline at EOF' '
+ cat >expect <<-\EOF &&
+ diff --git a/pre b/post
+ index 7bf316e..3dd0303 100644
+ --- a/pre
+ +++ b/post
+ @@ -1 +1 @@
+ a a [-a-]{+ab+} a a
+ EOF
+ printf "%s" "a a a a a" >pre &&
+ printf "%s" "a a ab a a" >post &&
+ word_diff --word-diff=plain
+'
+
+test_expect_success 'setup history with two files' '
+ echo "a b; c" >a.tex &&
+ echo "a b; c" >z.txt &&
+ git add a.tex z.txt &&
+ git commit -minitial &&
+
+ # modify both
+ echo "a bx; c" >a.tex &&
+ echo "a bx; c" >z.txt &&
+ git commit -mmodified -a
+'
+test_expect_success 'wordRegex for the first file does not apply to the second' '
+ echo "*.tex diff=tex" >.gitattributes &&
+ test_config diff.tex.wordRegex "[a-z]+|." &&
+ cat >expect <<-\EOF &&
+ diff --git a/a.tex b/a.tex
+ --- a/a.tex
+ +++ b/a.tex
+ @@ -1 +1 @@
+ a [-b-]{+bx+}; c
+ diff --git a/z.txt b/z.txt
+ --- a/z.txt
+ +++ b/z.txt
+ @@ -1 +1 @@
+ a [-b;-]{+bx;+} c
+ EOF
+ git diff --word-diff HEAD~ >actual &&
+ compare_diff_patch expect actual
'
test_done
diff --git a/t/t4034/ada/expect b/t/t4034/ada/expect
new file mode 100644
index 0000000..a682d28
--- /dev/null
+++ b/t/t4034/ada/expect
@@ -0,0 +1,27 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index d96fdd1..df21bb0 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,13 +1,13 @@<RESET>
+Ada.Text_IO.Put_Line("Hello World<RED>!<RESET><GREEN>?<RESET>");
+1 <RED>1e-10<RESET><GREEN>1e10<RESET> 16#FE12#E2 3.141_592 '<RED>x<RESET><GREEN>y<RESET>'
+<RED>a<RESET><GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>**<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>(<RED>b<RESET><GREEN>y<RESET>)
+<RED>a<RESET><GREEN>x<RESET>:=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>/= <RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>=><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>..<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><><RED>b<RESET><GREEN>y<RESET>
diff --git a/t/t4034/ada/post b/t/t4034/ada/post
new file mode 100644
index 0000000..df21bb0
--- /dev/null
+++ b/t/t4034/ada/post
@@ -0,0 +1,13 @@
+Ada.Text_IO.Put_Line("Hello World?");
+1 1e10 16#FE12#E2 3.141_592 'y'
+x+y x-y
+x*y x/y
+x**y
+x(y)
+x:=y
+x=y x/= y
+x<y x<=y x>y x>=y
+x,y
+x=>y
+x..y
+x<>y
diff --git a/t/t4034/ada/pre b/t/t4034/ada/pre
new file mode 100644
index 0000000..d96fdd1
--- /dev/null
+++ b/t/t4034/ada/pre
@@ -0,0 +1,13 @@
+Ada.Text_IO.Put_Line("Hello World!");
+1 1e-10 16#FE12#E2 3.141_592 'x'
+a+b a-b
+a*b a/b
+a**b
+a(b)
+a:=b
+a=b a/= b
+a<b a<=b a>b a>=b
+a,b
+a=>b
+a..b
+a<>b
diff --git a/t/t4034/bibtex/expect b/t/t4034/bibtex/expect
new file mode 100644
index 0000000..a157774
--- /dev/null
+++ b/t/t4034/bibtex/expect
@@ -0,0 +1,15 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 95cd55b..ddcba9b 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,9 +1,10 @@<RESET>
+@article{aldous1987uie,<RESET>
+ title={{Ultimate instability of exponential back-off protocol for acknowledgment-based transmission control of random access communication channels}},<RESET>
+ author={Aldous, <RED>D.<RESET><GREEN>David<RESET>},
+ journal={Information Theory, IEEE Transactions on},<RESET>
+ volume={<RED>33<RESET><GREEN>Bogus.<RESET>},
+ number={<RED>2<RESET><GREEN>4<RESET>},
+ pages={219--223},<RESET>
+ year=<GREEN>1987,<RESET>
+<GREEN> note={This is in fact a rather funny read since ethernet works well in practice. The<RESET> {<RED>1987<RESET><GREEN>\em pre} reference is the right one, however.<RESET>}<RED>,<RESET>
+}<RESET>
diff --git a/t/t4034/bibtex/post b/t/t4034/bibtex/post
new file mode 100644
index 0000000..ddcba9b
--- /dev/null
+++ b/t/t4034/bibtex/post
@@ -0,0 +1,10 @@
+@article{aldous1987uie,
+ title={{Ultimate instability of exponential back-off protocol for acknowledgment-based transmission control of random access communication channels}},
+ author={Aldous, David},
+ journal={Information Theory, IEEE Transactions on},
+ volume={Bogus.},
+ number={4},
+ pages={219--223},
+ year=1987,
+ note={This is in fact a rather funny read since ethernet works well in practice. The {\em pre} reference is the right one, however.}
+}
diff --git a/t/t4034/bibtex/pre b/t/t4034/bibtex/pre
new file mode 100644
index 0000000..95cd55b
--- /dev/null
+++ b/t/t4034/bibtex/pre
@@ -0,0 +1,9 @@
+@article{aldous1987uie,
+ title={{Ultimate instability of exponential back-off protocol for acknowledgment-based transmission control of random access communication channels}},
+ author={Aldous, D.},
+ journal={Information Theory, IEEE Transactions on},
+ volume={33},
+ number={2},
+ pages={219--223},
+ year={1987},
+}
diff --git a/t/t4034/cpp/expect b/t/t4034/cpp/expect
new file mode 100644
index 0000000..37d1ea2
--- /dev/null
+++ b/t/t4034/cpp/expect
@@ -0,0 +1,36 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 23d5c8a..7e8c026 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,19 +1,19 @@<RESET>
+Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <GREEN>bar(x);<RESET> }
+cout<<"Hello World<RED>!<RESET><GREEN>?<RESET>\n"<<endl;
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
+<RED>a<RESET><GREEN>x<RESET>::<RED>b<RESET><GREEN>y<RESET>
diff --git a/t/t4034/cpp/post b/t/t4034/cpp/post
new file mode 100644
index 0000000..7e8c026
--- /dev/null
+++ b/t/t4034/cpp/post
@@ -0,0 +1,19 @@
+Foo() : x(0&42) { bar(x); }
+cout<<"Hello World?\n"<<endl;
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
+x::y
diff --git a/t/t4034/cpp/pre b/t/t4034/cpp/pre
new file mode 100644
index 0000000..23d5c8a
--- /dev/null
+++ b/t/t4034/cpp/pre
@@ -0,0 +1,19 @@
+Foo():x(0&&1){}
+cout<<"Hello World!\n"<<endl;
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
+a::b
diff --git a/t/t4034/csharp/expect b/t/t4034/csharp/expect
new file mode 100644
index 0000000..e5d1dd2
--- /dev/null
+++ b/t/t4034/csharp/expect
@@ -0,0 +1,35 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 9106d63..dd5f421 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,18 +1,18 @@<RESET>
+Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <GREEN>bar(x);<RESET> }
+cout<<"Hello World<RED>!<RESET><GREEN>?<RESET>\n"<<endl;
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
diff --git a/t/t4034/csharp/post b/t/t4034/csharp/post
new file mode 100644
index 0000000..dd5f421
--- /dev/null
+++ b/t/t4034/csharp/post
@@ -0,0 +1,18 @@
+Foo() : x(0&42) { bar(x); }
+cout<<"Hello World?\n"<<endl;
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
diff --git a/t/t4034/csharp/pre b/t/t4034/csharp/pre
new file mode 100644
index 0000000..9106d63
--- /dev/null
+++ b/t/t4034/csharp/pre
@@ -0,0 +1,18 @@
+Foo():x(0&&1){}
+cout<<"Hello World!\n"<<endl;
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
diff --git a/t/t4034/fortran/expect b/t/t4034/fortran/expect
new file mode 100644
index 0000000..b233dbd
--- /dev/null
+++ b/t/t4034/fortran/expect
@@ -0,0 +1,10 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 87f0d0b..d308da2 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,5 +1,5 @@<RESET>
+print *, "Hello World<RED>!<RESET><GREEN>?<RESET>"
+
+DO10I = 1,10<RESET>
+<RED>DO10I<RESET><GREEN>DO 10 I<RESET> = 1,10
+<RED>DO10I<RESET><GREEN>DO 1 0 I<RESET> = 1,10
diff --git a/t/t4034/fortran/post b/t/t4034/fortran/post
new file mode 100644
index 0000000..d308da2
--- /dev/null
+++ b/t/t4034/fortran/post
@@ -0,0 +1,5 @@
+print *, "Hello World?"
+
+DO10I = 1,10
+DO 10 I = 1,10
+DO 1 0 I = 1,10
diff --git a/t/t4034/fortran/pre b/t/t4034/fortran/pre
new file mode 100644
index 0000000..87f0d0b
--- /dev/null
+++ b/t/t4034/fortran/pre
@@ -0,0 +1,5 @@
+print *, "Hello World!"
+
+DO10I = 1,10
+DO10I = 1,10
+DO10I = 1,10
diff --git a/t/t4034/html/expect b/t/t4034/html/expect
new file mode 100644
index 0000000..447b49a
--- /dev/null
+++ b/t/t4034/html/expect
@@ -0,0 +1,8 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 8ca4aea..46921e5 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,3 +1,3 @@<RESET>
+<tag <GREEN>newattr="newvalue"<RESET>><GREEN>added<RESET> content</tag>
+<tag attr=<RED>"value"<RESET><GREEN>"newvalue"<RESET>><RED>content<RESET><GREEN>changed<RESET></tag>
+<<RED>tag<RESET><GREEN>newtag<RESET>>content <RED>&entity;<RESET><GREEN>&newentity;<RESET><<RED>/tag<RESET><GREEN>/newtag<RESET>>
diff --git a/t/t4034/html/post b/t/t4034/html/post
new file mode 100644
index 0000000..46921e5
--- /dev/null
+++ b/t/t4034/html/post
@@ -0,0 +1,3 @@
+<tag newattr="newvalue">added content</tag>
+<tag attr="newvalue">changed</tag>
+<newtag>content &newentity;</newtag>
diff --git a/t/t4034/html/pre b/t/t4034/html/pre
new file mode 100644
index 0000000..8ca4aea
--- /dev/null
+++ b/t/t4034/html/pre
@@ -0,0 +1,3 @@
+<tag>content</tag>
+<tag attr="value">content</tag>
+<tag>content &entity;</tag>
diff --git a/t/t4034/java/expect b/t/t4034/java/expect
new file mode 100644
index 0000000..37d1ea2
--- /dev/null
+++ b/t/t4034/java/expect
@@ -0,0 +1,36 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 23d5c8a..7e8c026 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,19 +1,19 @@<RESET>
+Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <GREEN>bar(x);<RESET> }
+cout<<"Hello World<RED>!<RESET><GREEN>?<RESET>\n"<<endl;
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
+<RED>a<RESET><GREEN>x<RESET>::<RED>b<RESET><GREEN>y<RESET>
diff --git a/t/t4034/java/post b/t/t4034/java/post
new file mode 100644
index 0000000..7e8c026
--- /dev/null
+++ b/t/t4034/java/post
@@ -0,0 +1,19 @@
+Foo() : x(0&42) { bar(x); }
+cout<<"Hello World?\n"<<endl;
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
+x::y
diff --git a/t/t4034/java/pre b/t/t4034/java/pre
new file mode 100644
index 0000000..23d5c8a
--- /dev/null
+++ b/t/t4034/java/pre
@@ -0,0 +1,19 @@
+Foo():x(0&&1){}
+cout<<"Hello World!\n"<<endl;
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
+a::b
diff --git a/t/t4034/matlab/expect b/t/t4034/matlab/expect
new file mode 100644
index 0000000..72cf3e9
--- /dev/null
+++ b/t/t4034/matlab/expect
@@ -0,0 +1,14 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index dc204db..70e05f0 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,9 +1,9 @@<RESET>
+(<RED>1<RESET><GREEN>0<RESET>) (<RED>-1e10<RESET><GREEN>-0e10<RESET>) '<RED>b<RESET><GREEN>y<RESET>';
+[<RED>a<RESET><GREEN>x<RESET>] {<RED>a<RESET><GREEN>x<RESET>} <RED>a<RESET><GREEN>x<RESET>.<RED>b<RESET><GREEN>y<RESET>;
+~<RED>a<RESET><GREEN>x<RESET>;
+<RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>.*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>./<RED>b a<RESET><GREEN>y x<RESET>^<RED>b a<RESET><GREEN>y x<RESET>.^<RED>b a<RESET><GREEN>y x<RESET>.\<RED>b a<RESET><GREEN>y x<RESET>.';
+<RED>a<RESET><GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET><GREEN>y<RESET>;
+<RED>a<RESET><GREEN>x<RESET>&<RED>b a<RESET><GREEN>y x<RESET>&&<RED>b a<RESET><GREEN>y x<RESET>|<RED>b a<RESET><GREEN>y x<RESET>||<RED>b<RESET><GREEN>y<RESET>;
+<RED>a<RESET><GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET><GREEN>y<RESET>;
+<RED>a<RESET><GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>~=<RED>b<RESET><GREEN>y<RESET>;
+<RED>a<RESET><GREEN>x<RESET>,<RED>b<RESET><GREEN>y<RESET>;
diff --git a/t/t4034/matlab/post b/t/t4034/matlab/post
new file mode 100644
index 0000000..70e05f0
--- /dev/null
+++ b/t/t4034/matlab/post
@@ -0,0 +1,9 @@
+(0) (-0e10) 'y';
+[x] {x} x.y;
+~x;
+x*y x.*y x/y x./y x^y x.^y x.\y x.';
+x+y x-y;
+x&y x&&y x|y x||y;
+x<y x<=y x>y x>=y;
+x==y x~=y;
+x,y;
diff --git a/t/t4034/matlab/pre b/t/t4034/matlab/pre
new file mode 100644
index 0000000..dc204db
--- /dev/null
+++ b/t/t4034/matlab/pre
@@ -0,0 +1,9 @@
+(1) (-1e10) 'b';
+[a] {a} a.b;
+~a;
+a*b a.*b a/b a./b a^b a.^b a.\b a.';
+a+b a-b;
+a&b a&&b a|b a||b;
+a<b a<=b a>b a>=b;
+a==b a~=b;
+a,b;
diff --git a/t/t4034/objc/expect b/t/t4034/objc/expect
new file mode 100644
index 0000000..e5d1dd2
--- /dev/null
+++ b/t/t4034/objc/expect
@@ -0,0 +1,35 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 9106d63..dd5f421 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,18 +1,18 @@<RESET>
+Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <GREEN>bar(x);<RESET> }
+cout<<"Hello World<RED>!<RESET><GREEN>?<RESET>\n"<<endl;
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
diff --git a/t/t4034/objc/post b/t/t4034/objc/post
new file mode 100644
index 0000000..dd5f421
--- /dev/null
+++ b/t/t4034/objc/post
@@ -0,0 +1,18 @@
+Foo() : x(0&42) { bar(x); }
+cout<<"Hello World?\n"<<endl;
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
diff --git a/t/t4034/objc/pre b/t/t4034/objc/pre
new file mode 100644
index 0000000..9106d63
--- /dev/null
+++ b/t/t4034/objc/pre
@@ -0,0 +1,18 @@
+Foo():x(0&&1){}
+cout<<"Hello World!\n"<<endl;
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
diff --git a/t/t4034/pascal/expect b/t/t4034/pascal/expect
new file mode 100644
index 0000000..2ce4230
--- /dev/null
+++ b/t/t4034/pascal/expect
@@ -0,0 +1,35 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 077046c..8865e6b 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,18 +1,18 @@<RESET>
+writeln("Hello World<RED>!<RESET><GREEN>?<RESET>");
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
+<RED>a<RESET><GREEN>x<RESET>::<RED>b<RESET><GREEN>y<RESET>
diff --git a/t/t4034/pascal/post b/t/t4034/pascal/post
new file mode 100644
index 0000000..8865e6b
--- /dev/null
+++ b/t/t4034/pascal/post
@@ -0,0 +1,18 @@
+writeln("Hello World?");
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
+x::y
diff --git a/t/t4034/pascal/pre b/t/t4034/pascal/pre
new file mode 100644
index 0000000..077046c
--- /dev/null
+++ b/t/t4034/pascal/pre
@@ -0,0 +1,18 @@
+writeln("Hello World!");
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
+a::b
diff --git a/t/t4034/perl/expect b/t/t4034/perl/expect
new file mode 100644
index 0000000..a1deb6b
--- /dev/null
+++ b/t/t4034/perl/expect
@@ -0,0 +1,13 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index f6610d3..e8b72ef 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -4,8 +4,8 @@<RESET>
+
+package Frotz;<RESET>
+sub new {<RESET>
+ my <GREEN>(<RESET>$class<GREEN>, %opts)<RESET> = <RED>shift<RESET><GREEN>@_<RESET>;
+ return bless { <GREEN>xyzzy => "nitfol", %opts<RESET> }, $class;
+}<RESET>
+
+__END__<RESET>
diff --git a/t/t4034/perl/post b/t/t4034/perl/post
new file mode 100644
index 0000000..e8b72ef
--- /dev/null
+++ b/t/t4034/perl/post
@@ -0,0 +1,22 @@
+#!/usr/bin/perl
+
+use strict;
+
+package Frotz;
+sub new {
+ my ($class, %opts) = @_;
+ return bless { xyzzy => "nitfol", %opts }, $class;
+}
+
+__END__
+=head1 NAME
+
+frotz - Frotz
+
+=head1 SYNOPSIS
+
+ use frotz;
+
+ $nitfol = new Frotz();
+
+=cut
diff --git a/t/t4034/perl/pre b/t/t4034/perl/pre
new file mode 100644
index 0000000..f6610d3
--- /dev/null
+++ b/t/t4034/perl/pre
@@ -0,0 +1,22 @@
+#!/usr/bin/perl
+
+use strict;
+
+package Frotz;
+sub new {
+ my $class = shift;
+ return bless {}, $class;
+}
+
+__END__
+=head1 NAME
+
+frotz - Frotz
+
+=head1 SYNOPSIS
+
+ use frotz;
+
+ $nitfol = new Frotz();
+
+=cut
diff --git a/t/t4034/php/expect b/t/t4034/php/expect
new file mode 100644
index 0000000..0404408
--- /dev/null
+++ b/t/t4034/php/expect
@@ -0,0 +1,35 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index cf6e06b..4420a49 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,18 +1,18 @@<RESET>
+<GREEN>(<RESET>$var<GREEN>)<RESET> $ var
+<?="Hello World<RED>!<RESET><GREEN>?<RESET>"?>
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
diff --git a/t/t4034/php/post b/t/t4034/php/post
new file mode 100644
index 0000000..4420a49
--- /dev/null
+++ b/t/t4034/php/post
@@ -0,0 +1,18 @@
+($var) $ var
+<?="Hello World?"?>
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
diff --git a/t/t4034/php/pre b/t/t4034/php/pre
new file mode 100644
index 0000000..cf6e06b
--- /dev/null
+++ b/t/t4034/php/pre
@@ -0,0 +1,18 @@
+$var $var
+<?= "Hello World!" ?>
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
diff --git a/t/t4034/python/expect b/t/t4034/python/expect
new file mode 100644
index 0000000..8abb8a4
--- /dev/null
+++ b/t/t4034/python/expect
@@ -0,0 +1,34 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 438f776..68baf34 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,17 +1,17 @@<RESET>
+print<RED>u<RESET> "Hello World<RED>!<RESET><GREEN>?<RESET>\n"<GREEN>; print<RESET>
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>) u<RESET>'<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
diff --git a/t/t4034/python/post b/t/t4034/python/post
new file mode 100644
index 0000000..68baf34
--- /dev/null
+++ b/t/t4034/python/post
@@ -0,0 +1,17 @@
+print "Hello World?\n"; print
+(1) (-1e10) (0xabcdef) u'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
diff --git a/t/t4034/python/pre b/t/t4034/python/pre
new file mode 100644
index 0000000..438f776
--- /dev/null
+++ b/t/t4034/python/pre
@@ -0,0 +1,17 @@
+print u"Hello World!\n"
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
diff --git a/t/t4034/ruby/expect b/t/t4034/ruby/expect
new file mode 100644
index 0000000..16e1dd5
--- /dev/null
+++ b/t/t4034/ruby/expect
@@ -0,0 +1,34 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 30ed9a1..7678f14 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,17 +1,17 @@<RESET>
+10.downto(1) {|<RED>x<RESET><GREEN>y<RESET>| puts <RED>x<RESET><GREEN>y<RESET>}
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a?b<RESET><GREEN>y<RESET>
+<GREEN>x?y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
diff --git a/t/t4034/ruby/post b/t/t4034/ruby/post
new file mode 100644
index 0000000..7678f14
--- /dev/null
+++ b/t/t4034/ruby/post
@@ -0,0 +1,17 @@
+10.downto(1) {|y| puts y}
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
diff --git a/t/t4034/ruby/pre b/t/t4034/ruby/pre
new file mode 100644
index 0000000..30ed9a1
--- /dev/null
+++ b/t/t4034/ruby/pre
@@ -0,0 +1,17 @@
+10.downto(1) {|x| puts x}
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
diff --git a/t/t4034/tex/expect b/t/t4034/tex/expect
new file mode 100644
index 0000000..604969b
--- /dev/null
+++ b/t/t4034/tex/expect
@@ -0,0 +1,9 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 2b2dfcb..65cab61 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,4 +1,4 @@<RESET>
+\section{Something <GREEN>new<RESET>}
+<RED>\emph<RESET><GREEN>\textbf<RESET>{Macro style}
+{<RED>\em<RESET><GREEN>\bfseries<RESET> State toggle style}
+\\[<RED>1em<RESET><GREEN>1cm<RESET>]
diff --git a/t/t4034/tex/post b/t/t4034/tex/post
new file mode 100644
index 0000000..65cab61
--- /dev/null
+++ b/t/t4034/tex/post
@@ -0,0 +1,4 @@
+\section{Something new}
+\textbf{Macro style}
+{\bfseries State toggle style}
+\\[1cm]
diff --git a/t/t4034/tex/pre b/t/t4034/tex/pre
new file mode 100644
index 0000000..2b2dfcb
--- /dev/null
+++ b/t/t4034/tex/pre
@@ -0,0 +1,4 @@
+\section{Something}
+\emph{Macro style}
+{\em State toggle style}
+\\[1em]
diff --git a/t/t4035-diff-quiet.sh b/t/t4035-diff-quiet.sh
index e747e84..461f4bb 100755
--- a/t/t4035-diff-quiet.sh
+++ b/t/t4035-diff-quiet.sh
@@ -10,71 +10,146 @@ test_expect_success 'setup' '
git commit -m first &&
echo 2 >b &&
git add . &&
- git commit -a -m second
+ git commit -a -m second &&
+ mkdir -p test-outside/repo && (
+ cd test-outside/repo &&
+ git init &&
+ echo "1 1" >a &&
+ git add . &&
+ git commit -m 1
+ ) &&
+ mkdir -p test-outside/non/git && (
+ cd test-outside/non/git &&
+ echo "1 1" >a &&
+ echo "1 1" >matching-file &&
+ echo "1 1 " >trailing-space &&
+ echo "1 1" >extra-space &&
+ echo "2" >never-match
+ )
'
test_expect_success 'git diff-tree HEAD^ HEAD' '
- git diff-tree --quiet HEAD^ HEAD >cnt
- test $? = 1 && test $(wc -l <cnt) = 0
+ test_expect_code 1 git diff-tree --quiet HEAD^ HEAD >cnt &&
+ test_line_count = 0 cnt
'
test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
- git diff-tree --quiet HEAD^ HEAD -- a >cnt
- test $? = 0 && test $(wc -l <cnt) = 0
+ test_expect_code 0 git diff-tree --quiet HEAD^ HEAD -- a >cnt &&
+ test_line_count = 0 cnt
'
test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
- git diff-tree --quiet HEAD^ HEAD -- b >cnt
- test $? = 1 && test $(wc -l <cnt) = 0
+ test_expect_code 1 git diff-tree --quiet HEAD^ HEAD -- b >cnt &&
+ test_line_count = 0 cnt
'
# this diff outputs one line: sha1 of the given head
test_expect_success 'echo HEAD | git diff-tree --stdin' '
- echo $(git rev-parse HEAD) | git diff-tree --quiet --stdin >cnt
- test $? = 1 && test $(wc -l <cnt) = 1
+ echo $(git rev-parse HEAD) |
+ test_expect_code 1 git diff-tree --quiet --stdin >cnt &&
+ test_line_count = 1 cnt
'
test_expect_success 'git diff-tree HEAD HEAD' '
- git diff-tree --quiet HEAD HEAD >cnt
- test $? = 0 && test $(wc -l <cnt) = 0
+ test_expect_code 0 git diff-tree --quiet HEAD HEAD >cnt &&
+ test_line_count = 0 cnt
'
test_expect_success 'git diff-files' '
- git diff-files --quiet >cnt
- test $? = 0 && test $(wc -l <cnt) = 0
+ test_expect_code 0 git diff-files --quiet >cnt &&
+ test_line_count = 0 cnt
'
test_expect_success 'git diff-index --cached HEAD' '
- git diff-index --quiet --cached HEAD >cnt
- test $? = 0 && test $(wc -l <cnt) = 0
+ test_expect_code 0 git diff-index --quiet --cached HEAD >cnt &&
+ test_line_count = 0 cnt
'
test_expect_success 'git diff-index --cached HEAD^' '
- git diff-index --quiet --cached HEAD^ >cnt
- test $? = 1 && test $(wc -l <cnt) = 0
+ test_expect_code 1 git diff-index --quiet --cached HEAD^ >cnt &&
+ test_line_count = 0 cnt
'
test_expect_success 'git diff-index --cached HEAD^' '
echo text >>b &&
echo 3 >c &&
- git add . && {
- git diff-index --quiet --cached HEAD^ >cnt
- test $? = 1 && test $(wc -l <cnt) = 0
- }
+ git add . &&
+ test_expect_code 1 git diff-index --quiet --cached HEAD^ >cnt &&
+ test_line_count = 0 cnt
'
test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
- git commit -m "text in b" && {
- git diff-tree --quiet -Stext HEAD^ HEAD -- b >cnt
- test $? = 1 && test $(wc -l <cnt) = 0
- }
+ git commit -m "text in b" &&
+ test_expect_code 1 git diff-tree --quiet -Stext HEAD^ HEAD -- b >cnt &&
+ test_line_count = 0 cnt
'
test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
- git diff-tree --quiet -Snot-found HEAD^ HEAD -- b >cnt
- test $? = 0 && test $(wc -l <cnt) = 0
+ test_expect_code 0 git diff-tree --quiet -Snot-found HEAD^ HEAD -- b >cnt &&
+ test_line_count = 0 cnt
'
test_expect_success 'git diff-files' '
- echo 3 >>c && {
- git diff-files --quiet >cnt
- test $? = 1 && test $(wc -l <cnt) = 0
- }
+ echo 3 >>c &&
+ test_expect_code 1 git diff-files --quiet >cnt &&
+ test_line_count = 0 cnt
'
+
test_expect_success 'git diff-index --cached HEAD' '
- git update-index c && {
- git diff-index --quiet --cached HEAD >cnt
- test $? = 1 && test $(wc -l <cnt) = 0
- }
+ git update-index c &&
+ test_expect_code 1 git diff-index --quiet --cached HEAD >cnt &&
+ test_line_count = 0 cnt
+'
+
+test_expect_success 'git diff, one file outside repo' '
+ (
+ cd test-outside/repo &&
+ test_expect_code 0 git diff --quiet a ../non/git/matching-file &&
+ test_expect_code 1 git diff --quiet a ../non/git/extra-space
+ )
+'
+
+test_expect_success 'git diff, both files outside repo' '
+ (
+ GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/test-outside" &&
+ export GIT_CEILING_DIRECTORIES &&
+ cd test-outside/non/git &&
+ test_expect_code 0 git diff --quiet a matching-file &&
+ test_expect_code 1 git diff --quiet a extra-space
+ )
+'
+
+test_expect_success 'git diff --ignore-space-at-eol, one file outside repo' '
+ (
+ cd test-outside/repo &&
+ test_expect_code 0 git diff --quiet --ignore-space-at-eol a ../non/git/trailing-space &&
+ test_expect_code 1 git diff --quiet --ignore-space-at-eol a ../non/git/extra-space
+ )
+'
+
+test_expect_success 'git diff --ignore-space-at-eol, both files outside repo' '
+ (
+ GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/test-outside" &&
+ export GIT_CEILING_DIRECTORIES &&
+ cd test-outside/non/git &&
+ test_expect_code 0 git diff --quiet --ignore-space-at-eol a trailing-space &&
+ test_expect_code 1 git diff --quiet --ignore-space-at-eol a extra-space
+ )
+'
+
+test_expect_success 'git diff --ignore-all-space, one file outside repo' '
+ (
+ cd test-outside/repo &&
+ test_expect_code 0 git diff --quiet --ignore-all-space a ../non/git/trailing-space &&
+ test_expect_code 0 git diff --quiet --ignore-all-space a ../non/git/extra-space &&
+ test_expect_code 1 git diff --quiet --ignore-all-space a ../non/git/never-match
+ )
+'
+
+test_expect_success 'git diff --ignore-all-space, both files outside repo' '
+ (
+ GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/test-outside" &&
+ export GIT_CEILING_DIRECTORIES &&
+ cd test-outside/non/git &&
+ test_expect_code 0 git diff --quiet --ignore-all-space a trailing-space &&
+ test_expect_code 0 git diff --quiet --ignore-all-space a extra-space &&
+ test_expect_code 1 git diff --quiet --ignore-all-space a never-match
+ )
+'
+
+test_expect_success 'git diff --quiet ignores stat-change only entries' '
+ test-chmtime +10 a &&
+ echo modified >>b &&
+ test_expect_code 1 git diff --quiet
'
test_done
diff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh
index ba43f18..98d9713 100755
--- a/t/t4036-format-patch-signer-mime.sh
+++ b/t/t4036-format-patch-signer-mime.sh
@@ -42,7 +42,7 @@ 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 $(grep -ci ^MIME-Version: output) = 1
'
diff --git a/t/t4038-diff-combined.sh b/t/t4038-diff-combined.sh
index 40277c7..0b4f7df 100755
--- a/t/t4038-diff-combined.sh
+++ b/t/t4038-diff-combined.sh
@@ -3,6 +3,7 @@
test_description='combined diff'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
setup_helper () {
one=$1 branch=$2 side=$3 &&
@@ -89,4 +90,349 @@ test_expect_success 'diagnose truncated file' '
grep "diff --cc file" out
'
+test_expect_success 'setup for --cc --raw' '
+ blob=$(echo file | git hash-object --stdin -w) &&
+ base_tree=$(echo "100644 blob $blob file" | git mktree) &&
+ trees= &&
+ for i in $(test_seq 1 40)
+ do
+ blob=$(echo file$i | git hash-object --stdin -w) &&
+ trees="$trees$(echo "100644 blob $blob file" | git mktree)$LF"
+ done
+'
+
+test_expect_success 'check --cc --raw with four trees' '
+ four_trees=$(echo "$trees" | sed -e 4q) &&
+ git diff --cc --raw $four_trees $base_tree >out &&
+ # Check for four leading colons in the output:
+ grep "^::::[^:]" out
+'
+
+test_expect_success 'check --cc --raw with forty trees' '
+ git diff --cc --raw $trees $base_tree >out &&
+ # Check for forty leading colons in the output:
+ grep "^::::::::::::::::::::::::::::::::::::::::[^:]" out
+'
+
+test_expect_success 'setup combined ignore spaces' '
+ git checkout master &&
+ >test &&
+ git add test &&
+ git commit -m initial &&
+
+ tr -d Q <<-\EOF >test &&
+ always coalesce
+ eol space coalesce Q
+ space change coalesce
+ all spa ces coalesce
+ eol spaces Q
+ space change
+ all spa ces
+ EOF
+ git commit -m "test space change" -a &&
+
+ git checkout -b side HEAD^ &&
+ tr -d Q <<-\EOF >test &&
+ always coalesce
+ eol space coalesce
+ space change coalesce
+ all spaces coalesce
+ eol spaces
+ space change
+ all spaces
+ EOF
+ git commit -m "test other space changes" -a &&
+
+ test_must_fail git merge master &&
+ tr -d Q <<-\EOF >test &&
+ eol spaces Q
+ space change
+ all spa ces
+ EOF
+ git commit -m merged -a
+'
+
+test_expect_success 'check combined output (no ignore space)' '
+ git show >actual.tmp &&
+ sed -e "1,/^@@@/d" < actual.tmp >actual &&
+ tr -d Q <<-\EOF >expected &&
+ --always coalesce
+ - eol space coalesce
+ - space change coalesce
+ - all spaces coalesce
+ - eol spaces
+ - space change
+ - all spaces
+ -eol space coalesce Q
+ -space change coalesce
+ -all spa ces coalesce
+ + eol spaces Q
+ + space change
+ + all spa ces
+ EOF
+ compare_diff_patch expected actual
+'
+
+test_expect_success 'check combined output (ignore space at eol)' '
+ git show --ignore-space-at-eol >actual.tmp &&
+ sed -e "1,/^@@@/d" < actual.tmp >actual &&
+ tr -d Q <<-\EOF >expected &&
+ --always coalesce
+ --eol space coalesce
+ - space change coalesce
+ - all spaces coalesce
+ -space change coalesce
+ -all spa ces coalesce
+ eol spaces Q
+ - space change
+ - all spaces
+ + space change
+ + all spa ces
+ EOF
+ compare_diff_patch expected actual
+'
+
+test_expect_success 'check combined output (ignore space change)' '
+ git show -b >actual.tmp &&
+ sed -e "1,/^@@@/d" < actual.tmp >actual &&
+ tr -d Q <<-\EOF >expected &&
+ --always coalesce
+ --eol space coalesce
+ --space change coalesce
+ - all spaces coalesce
+ -all spa ces coalesce
+ eol spaces Q
+ space change
+ - all spaces
+ + all spa ces
+ EOF
+ compare_diff_patch expected actual
+'
+
+test_expect_success 'check combined output (ignore all spaces)' '
+ git show -w >actual.tmp &&
+ sed -e "1,/^@@@/d" < actual.tmp >actual &&
+ tr -d Q <<-\EOF >expected &&
+ --always coalesce
+ --eol space coalesce
+ --space change coalesce
+ --all spaces coalesce
+ eol spaces Q
+ space change
+ all spa ces
+ EOF
+ compare_diff_patch expected actual
+'
+
+test_expect_success 'combine diff coalesce simple' '
+ >test &&
+ git add test &&
+ git commit -m initial &&
+ test_seq 4 >test &&
+ git commit -a -m empty1 &&
+ git branch side1 &&
+ git checkout HEAD^ &&
+ test_seq 5 >test &&
+ git commit -a -m empty2 &&
+ test_must_fail git merge side1 &&
+ >test &&
+ git commit -a -m merge &&
+ git show >actual.tmp &&
+ sed -e "1,/^@@@/d" < actual.tmp >actual &&
+ tr -d Q <<-\EOF >expected &&
+ --1
+ --2
+ --3
+ --4
+ - 5
+ EOF
+ compare_diff_patch expected actual
+'
+
+test_expect_success 'combine diff coalesce tricky' '
+ >test &&
+ git add test &&
+ git commit -m initial --allow-empty &&
+ cat <<-\EOF >test &&
+ 3
+ 1
+ 2
+ 3
+ 4
+ EOF
+ git commit -a -m empty1 &&
+ git branch -f side1 &&
+ git checkout HEAD^ &&
+ cat <<-\EOF >test &&
+ 1
+ 3
+ 5
+ 4
+ EOF
+ git commit -a -m empty2 &&
+ git branch -f side2 &&
+ test_must_fail git merge side1 &&
+ >test &&
+ git commit -a -m merge &&
+ git show >actual.tmp &&
+ sed -e "1,/^@@@/d" < actual.tmp >actual &&
+ tr -d Q <<-\EOF >expected &&
+ -3
+ --1
+ -2
+ --3
+ - 5
+ --4
+ EOF
+ compare_diff_patch expected actual &&
+ git checkout -f side1 &&
+ test_must_fail git merge side2 &&
+ >test &&
+ git commit -a -m merge &&
+ git show >actual.tmp &&
+ sed -e "1,/^@@@/d" < actual.tmp >actual &&
+ tr -d Q <<-\EOF >expected &&
+ - 3
+ --1
+ - 2
+ --3
+ -5
+ --4
+ EOF
+ compare_diff_patch expected actual
+'
+
+test_expect_failure 'combine diff coalesce three parents' '
+ >test &&
+ git add test &&
+ git commit -m initial --allow-empty &&
+ cat <<-\EOF >test &&
+ 3
+ 1
+ 2
+ 3
+ 4
+ EOF
+ git commit -a -m empty1 &&
+ git checkout -B side1 &&
+ git checkout HEAD^ &&
+ cat <<-\EOF >test &&
+ 1
+ 3
+ 7
+ 5
+ 4
+ EOF
+ git commit -a -m empty2 &&
+ git branch -f side2 &&
+ git checkout HEAD^ &&
+ cat <<-\EOF >test &&
+ 3
+ 1
+ 6
+ 5
+ 4
+ EOF
+ git commit -a -m empty3 &&
+ >test &&
+ git add test &&
+ TREE=$(git write-tree) &&
+ COMMIT=$(git commit-tree -p HEAD -p side1 -p side2 -m merge $TREE) &&
+ git show $COMMIT >actual.tmp &&
+ sed -e "1,/^@@@/d" < actual.tmp >actual &&
+ tr -d Q <<-\EOF >expected &&
+ -- 3
+ ---1
+ - 6
+ - 2
+ --3
+ -7
+ - -5
+ ---4
+ EOF
+ compare_diff_patch expected actual
+'
+
+# Test for a bug reported at
+# http://thread.gmane.org/gmane.comp.version-control.git/224410
+# where a delete lines were missing from combined diff output when they
+# occurred exactly before the context lines of a later change.
+test_expect_success 'combine diff missing delete bug' '
+ git commit -m initial --allow-empty &&
+ cat <<-\EOF >test &&
+ 1
+ 2
+ 3
+ 4
+ EOF
+ git add test &&
+ git commit -a -m side1 &&
+ git checkout -B side1 &&
+ git checkout HEAD^ &&
+ cat <<-\EOF >test &&
+ 0
+ 1
+ 2
+ 3
+ 4modified
+ EOF
+ git add test &&
+ git commit -m side2 &&
+ git branch -f side2 &&
+ test_must_fail git merge --no-commit side1 &&
+ cat <<-\EOF >test &&
+ 1
+ 2
+ 3
+ 4modified
+ EOF
+ git add test &&
+ git commit -a -m merge &&
+ git diff-tree -c -p HEAD >actual.tmp &&
+ sed -e "1,/^@@@/d" < actual.tmp >actual &&
+ tr -d Q <<-\EOF >expected &&
+ - 0
+ 1
+ 2
+ 3
+ -4
+ +4modified
+ EOF
+ compare_diff_patch expected actual
+'
+
+test_expect_success 'combine diff gets tree sorting right' '
+ # create a directory and a file that sort differently in trees
+ # versus byte-wise (implied "/" sorts after ".")
+ git checkout -f master &&
+ mkdir foo &&
+ echo base >foo/one &&
+ echo base >foo/two &&
+ echo base >foo.ext &&
+ git add foo foo.ext &&
+ git commit -m base &&
+
+ # one side modifies a file in the directory, along with the root
+ # file...
+ echo master >foo/one &&
+ echo master >foo.ext &&
+ git commit -a -m master &&
+
+ # the other side modifies the other file in the directory
+ git checkout -b other HEAD^ &&
+ echo other >foo/two &&
+ git commit -a -m other &&
+
+ # And now we merge. The files in the subdirectory will resolve cleanly,
+ # meaning that a combined diff will not find them interesting. But it
+ # will find the tree itself interesting, because it had to be merged.
+ git checkout master &&
+ git merge other &&
+
+ printf "MM\tfoo\n" >expect &&
+ git diff-tree -c --name-status -t HEAD >actual.tmp &&
+ sed 1d <actual.tmp >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
index 9d9498b..23c0e35 100755
--- a/t/t4039-diff-assume-unchanged.sh
+++ b/t/t4039-diff-assume-unchanged.sh
@@ -28,4 +28,15 @@ test_expect_success 'diff-files does not examine assume-unchanged entries' '
test -z "$(git diff-files -- one)"
'
+test_expect_success POSIXPERM 'find-copies-harder is not confused by mode bits' '
+ echo content >exec &&
+ chmod +x exec &&
+ git add exec &&
+ git commit -m exec &&
+ git update-index --assume-unchanged exec &&
+ >expect &&
+ git diff-files --find-copies-harder -- exec >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4040-whitespace-status.sh b/t/t4040-whitespace-status.sh
index a30b03b..3c728a3 100755
--- a/t/t4040-whitespace-status.sh
+++ b/t/t4040-whitespace-status.sh
@@ -60,4 +60,16 @@ test_expect_success 'diff-files -b -p --exit-code' '
git diff-files -b -p --exit-code
'
+test_expect_success 'diff-files --diff-filter --quiet' '
+ git reset --hard &&
+ rm a/d &&
+ echo x >>b/e &&
+ test_must_fail git diff-files --diff-filter=M --quiet
+'
+
+test_expect_success 'diff-tree --diff-filter --quiet' '
+ git commit -a -m "worktree state" &&
+ test_must_fail git diff-tree --diff-filter=M --quiet HEAD^ HEAD
+'
+
test_done
diff --git a/t/t4041-diff-submodule-option.sh b/t/t4041-diff-submodule-option.sh
index 8e391cf..2d9731b 100755
--- a/t/t4041-diff-submodule-option.sh
+++ b/t/t4041-diff-submodule-option.sh
@@ -1,6 +1,7 @@
#!/bin/sh
#
# Copyright (c) 2009 Jens Lehmann, based on t7401 by Ping Yin
+# Copyright (c) 2011 Alexey Shumkin (+ non-UTF-8 commit encoding tests)
#
test_description='Support for verbose submodule differences in git diff
@@ -10,19 +11,28 @@ This test tries to verify the sanity of the --submodule option of git diff.
. ./test-lib.sh
+# Tested non-UTF-8 encoding
+test_encoding="ISO8859-1"
+
+# String "added" in German (translated with Google Translate), encoded in UTF-8,
+# used in sample commit log messages in add_file() function below.
+added=$(printf "hinzugef\303\274gt")
add_file () {
- sm=$1
- shift
- owd=$(pwd)
- cd "$sm"
- for name; do
- echo "$name" > "$name" &&
- git add "$name" &&
- test_tick &&
- git commit -m "Add $name"
- done >/dev/null
- git rev-parse --verify HEAD | cut -c1-7
- cd "$owd"
+ (
+ cd "$1" &&
+ shift &&
+ for name
+ do
+ echo "$name" >"$name" &&
+ git add "$name" &&
+ test_tick &&
+ # "git commit -m" would break MinGW, as Windows refuse to pass
+ # $test_encoding encoded parameter to git.
+ echo "Add $name ($added $name)" | iconv -f utf-8 -t $test_encoding |
+ git -c "i18n.commitEncoding=$test_encoding" commit -F -
+ done >/dev/null &&
+ git rev-parse --short --verify HEAD
+ )
}
commit_file () {
test_tick &&
@@ -33,84 +43,133 @@ test_create_repo sm1 &&
add_file . foo >/dev/null
head1=$(add_file sm1 foo1 foo2)
+fullhead1=$(cd sm1; git rev-parse --verify HEAD)
-test_expect_success 'added submodule' "
+test_expect_success 'added submodule' '
git add sm1 &&
git diff-index -p --submodule=log HEAD >actual &&
- diff actual - <<-EOF
-Submodule sm1 0000000...$head1 (new submodule)
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 0000000...$head1 (new submodule)
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'added submodule, set diff.submodule' '
+ git config diff.submodule log &&
+ git add sm1 &&
+ git diff --cached >actual &&
+ cat >expected <<-EOF &&
+ Submodule sm1 0000000...$head1 (new submodule)
+ EOF
+ git config --unset diff.submodule &&
+ test_cmp expected actual
+'
+
+test_expect_success '--submodule=short overrides diff.submodule' '
+ test_config diff.submodule log &&
+ git add sm1 &&
+ git diff --submodule=short --cached >actual &&
+ cat >expected <<-EOF &&
+ diff --git a/sm1 b/sm1
+ new file mode 160000
+ index 0000000..$head1
+ --- /dev/null
+ +++ b/sm1
+ @@ -0,0 +1 @@
+ +Subproject commit $fullhead1
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'diff.submodule does not affect plumbing' '
+ test_config diff.submodule log &&
+ git diff-index -p HEAD >actual &&
+ cat >expected <<-EOF &&
+ diff --git a/sm1 b/sm1
+ new file mode 160000
+ index 0000000..$head1
+ --- /dev/null
+ +++ b/sm1
+ @@ -0,0 +1 @@
+ +Subproject commit $fullhead1
+ EOF
+ test_cmp expected actual
+'
commit_file sm1 &&
head2=$(add_file sm1 foo3)
-test_expect_success 'modified submodule(forward)' "
+test_expect_success 'modified submodule(forward)' '
git diff-index -p --submodule=log HEAD >actual &&
- diff actual - <<-EOF
-Submodule sm1 $head1..$head2:
- > Add foo3
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 $head1..$head2:
+ > Add foo3 ($added foo3)
+ EOF
+ test_cmp expected actual
+'
-test_expect_success 'modified submodule(forward)' "
+test_expect_success 'modified submodule(forward)' '
git diff --submodule=log >actual &&
- diff actual - <<-EOF
-Submodule sm1 $head1..$head2:
- > Add foo3
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 $head1..$head2:
+ > Add foo3 ($added foo3)
+ EOF
+ test_cmp expected actual
+'
-test_expect_success 'modified submodule(forward) --submodule' "
+test_expect_success 'modified submodule(forward) --submodule' '
git diff --submodule >actual &&
- 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' "
+ cat >expected <<-EOF &&
+ Submodule sm1 $head1..$head2:
+ > Add foo3 ($added foo3)
+ EOF
+ test_cmp expected actual
+'
+
+fullhead2=$(cd sm1; git rev-parse --verify HEAD)
+test_expect_success 'modified submodule(forward) --submodule=short' '
git diff --submodule=short >actual &&
- 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
-"
+ cat >expected <<-EOF &&
+ diff --git a/sm1 b/sm1
+ index $head1..$head2 160000
+ --- a/sm1
+ +++ b/sm1
+ @@ -1 +1 @@
+ -Subproject commit $fullhead1
+ +Subproject commit $fullhead2
+ EOF
+ test_cmp expected actual
+'
commit_file sm1 &&
-cd sm1 &&
-git reset --hard HEAD~2 >/dev/null &&
-head3=$(git rev-parse --verify HEAD | cut -c1-7) &&
-cd ..
+head3=$(
+ cd sm1 &&
+ git reset --hard HEAD~2 >/dev/null &&
+ git rev-parse --short --verify HEAD
+)
-test_expect_success 'modified submodule(backward)' "
+test_expect_success 'modified submodule(backward)' '
git diff-index -p --submodule=log HEAD >actual &&
- 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)' "
+ cat >expected <<-EOF &&
+ Submodule sm1 $head2..$head3 (rewind):
+ < Add foo3 ($added foo3)
+ < Add foo2 ($added foo2)
+ EOF
+ test_cmp expected actual
+'
+
+head4=$(add_file sm1 foo4 foo5)
+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
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 $head2...$head4:
+ > Add foo5 ($added foo5)
+ > Add foo4 ($added foo4)
+ < Add foo3 ($added foo3)
+ < Add foo2 ($added foo2)
+ EOF
+ test_cmp expected actual
+'
commit_file sm1 &&
mv sm1 sm1-bak &&
@@ -120,295 +179,319 @@ git add sm1 &&
rm -f sm1 &&
mv sm1-bak sm1
-test_expect_success 'typechanged submodule(submodule->blob), --cached' "
+test_expect_success 'typechanged submodule(submodule->blob), --cached' '
git diff --submodule=log --cached >actual &&
- 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)' "
+ cat >expected <<-EOF &&
+ Submodule sm1 $head4...0000000 (submodule deleted)
+ diff --git a/sm1 b/sm1
+ new file mode 100644
+ index 0000000..$head5
+ --- /dev/null
+ +++ b/sm1
+ @@ -0,0 +1 @@
+ +sm1
+ EOF
+ test_cmp expected actual
+'
+
+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
-"
+ cat >expected <<-EOF &&
+ diff --git a/sm1 b/sm1
+ deleted file mode 100644
+ index $head5..0000000
+ --- a/sm1
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -sm1
+ Submodule sm1 0000000...$head4 (new submodule)
+ EOF
+ test_cmp expected actual
+'
rm -rf sm1 &&
git checkout-index sm1
-test_expect_success 'typechanged submodule(submodule->blob)' "
+test_expect_success 'typechanged submodule(submodule->blob)' '
git diff-index -p --submodule=log HEAD >actual &&
- 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
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 $head4...0000000 (submodule deleted)
+ diff --git a/sm1 b/sm1
+ new file mode 100644
+ index 0000000..$head5
+ --- /dev/null
+ +++ b/sm1
+ @@ -0,0 +1 @@
+ +sm1
+ EOF
+ test_cmp expected actual
+'
rm -f sm1 &&
test_create_repo sm1 &&
head6=$(add_file sm1 foo6 foo7)
-fullhead6=$(cd sm1; git rev-list --max-count=1 $head6)
-test_expect_success 'nonexistent commit' "
+fullhead6=$(cd sm1; git rev-parse --verify HEAD)
+test_expect_success 'nonexistent commit' '
git diff-index -p --submodule=log HEAD >actual &&
- diff actual - <<-EOF
-Submodule sm1 $head4...$head6 (commits not present)
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 $head4...$head6 (commits not present)
+ EOF
+ test_cmp expected actual
+'
commit_file
-test_expect_success 'typechanged submodule(blob->submodule)' "
+test_expect_success 'typechanged submodule(blob->submodule)' '
git diff-index -p --submodule=log HEAD >actual &&
- 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
-"
+ cat >expected <<-EOF &&
+ diff --git a/sm1 b/sm1
+ deleted file mode 100644
+ index $head5..0000000
+ --- a/sm1
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -sm1
+ Submodule sm1 0000000...$head6 (new submodule)
+ EOF
+ test_cmp expected actual
+'
commit_file sm1 &&
-test_expect_success 'submodule is up to date' "
+test_expect_success 'submodule is up to date' '
git diff-index -p --submodule=log HEAD >actual &&
- diff actual - <<-EOF
-EOF
-"
+ cat >expected <<-EOF &&
+ EOF
+ test_cmp expected actual
+'
-test_expect_success 'submodule contains untracked content' "
+test_expect_success 'submodule contains untracked content' '
echo new > sm1/new-file &&
git diff-index -p --submodule=log HEAD >actual &&
- diff actual - <<-EOF
-Submodule sm1 contains untracked content
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 contains untracked content
+ EOF
+ test_cmp expected actual
+'
-test_expect_success 'submodule contains untracked content (untracked ignored)' "
+test_expect_success 'submodule contains untracked content (untracked ignored)' '
git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
! test -s actual
-"
+'
-test_expect_success 'submodule contains untracked content (dirty ignored)' "
+test_expect_success 'submodule contains untracked content (dirty ignored)' '
git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
! test -s actual
-"
+'
-test_expect_success 'submodule contains untracked content (all ignored)' "
+test_expect_success 'submodule contains untracked content (all ignored)' '
git diff-index -p --ignore-submodules=all --submodule=log HEAD >actual &&
! test -s actual
-"
+'
-test_expect_success 'submodule contains untracked and modifed content' "
+test_expect_success 'submodule contains untracked and modifed content' '
echo new > sm1/foo6 &&
git diff-index -p --submodule=log HEAD >actual &&
- diff actual - <<-EOF
-Submodule sm1 contains untracked content
-Submodule sm1 contains modified content
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 contains untracked content
+ Submodule sm1 contains modified content
+ EOF
+ test_cmp expected actual
+'
-test_expect_success 'submodule contains untracked and modifed content (untracked ignored)' "
+test_expect_success 'submodule contains untracked and modifed content (untracked ignored)' '
echo new > sm1/foo6 &&
git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
- diff actual - <<-EOF
-Submodule sm1 contains modified content
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 contains modified content
+ EOF
+ test_cmp expected actual
+'
-test_expect_success 'submodule contains untracked and modifed content (dirty ignored)' "
+test_expect_success 'submodule contains untracked and modifed content (dirty ignored)' '
echo new > sm1/foo6 &&
git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
! test -s actual
-"
+'
-test_expect_success 'submodule contains untracked and modifed content (all ignored)' "
+test_expect_success 'submodule contains untracked and modifed content (all ignored)' '
echo new > sm1/foo6 &&
git diff-index -p --ignore-submodules --submodule=log HEAD >actual &&
! test -s actual
-"
+'
-test_expect_success 'submodule contains modifed content' "
+test_expect_success 'submodule contains modifed content' '
rm -f sm1/new-file &&
git diff-index -p --submodule=log HEAD >actual &&
- diff actual - <<-EOF
-Submodule sm1 contains modified content
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 contains modified content
+ EOF
+ test_cmp expected actual
+'
(cd sm1; git commit -mchange foo6 >/dev/null) &&
-head8=$(cd sm1; git rev-parse --verify HEAD | cut -c1-7) &&
-test_expect_success 'submodule is modified' "
+head8=$(cd sm1; git rev-parse --short --verify HEAD) &&
+test_expect_success 'submodule is modified' '
git diff-index -p --submodule=log HEAD >actual &&
- diff actual - <<-EOF
-Submodule sm1 $head6..$head8:
- > change
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 $head6..$head8:
+ > change
+ EOF
+ test_cmp expected actual
+'
-test_expect_success 'modified submodule contains untracked content' "
+test_expect_success 'modified submodule contains untracked content' '
echo new > sm1/new-file &&
git diff-index -p --submodule=log HEAD >actual &&
- diff actual - <<-EOF
-Submodule sm1 contains untracked content
-Submodule sm1 $head6..$head8:
- > change
-EOF
-"
-
-test_expect_success 'modified submodule contains untracked content (untracked ignored)' "
+ cat >expected <<-EOF &&
+ Submodule sm1 contains untracked content
+ Submodule sm1 $head6..$head8:
+ > change
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'modified submodule contains untracked content (untracked ignored)' '
git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
- diff actual - <<-EOF
-Submodule sm1 $head6..$head8:
- > change
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 $head6..$head8:
+ > change
+ EOF
+ test_cmp expected actual
+'
-test_expect_success 'modified submodule contains untracked content (dirty ignored)' "
+test_expect_success 'modified submodule contains untracked content (dirty ignored)' '
git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
- diff actual - <<-EOF
-Submodule sm1 $head6..$head8:
- > change
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 $head6..$head8:
+ > change
+ EOF
+ test_cmp expected actual
+'
-test_expect_success 'modified submodule contains untracked content (all ignored)' "
+test_expect_success 'modified submodule contains untracked content (all ignored)' '
git diff-index -p --ignore-submodules=all --submodule=log HEAD >actual &&
! test -s actual
-"
+'
-test_expect_success 'modified submodule contains untracked and modifed content' "
+test_expect_success 'modified submodule contains untracked and modifed content' '
echo modification >> sm1/foo6 &&
git diff-index -p --submodule=log HEAD >actual &&
- 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)' "
+ cat >expected <<-EOF &&
+ Submodule sm1 contains untracked content
+ Submodule sm1 contains modified content
+ Submodule sm1 $head6..$head8:
+ > change
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'modified submodule contains untracked and modifed content (untracked ignored)' '
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)' "
+ cat >expected <<-EOF &&
+ Submodule sm1 contains modified content
+ Submodule sm1 $head6..$head8:
+ > change
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'modified submodule contains untracked and modifed content (dirty ignored)' '
echo modification >> sm1/foo6 &&
git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
- diff actual - <<-EOF
-Submodule sm1 $head6..$head8:
- > change
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 $head6..$head8:
+ > change
+ EOF
+ test_cmp expected actual
+'
-test_expect_success 'modified submodule contains untracked and modifed content (all ignored)' "
+test_expect_success 'modified submodule contains untracked and modifed content (all ignored)' '
echo modification >> sm1/foo6 &&
git diff-index -p --ignore-submodules --submodule=log HEAD >actual &&
! test -s actual
-"
+'
-test_expect_success 'modified submodule contains modifed content' "
+test_expect_success 'modified submodule contains modifed content' '
rm -f sm1/new-file &&
git diff-index -p --submodule=log HEAD >actual &&
- diff actual - <<-EOF
-Submodule sm1 contains modified content
-Submodule sm1 $head6..$head8:
- > change
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 contains modified content
+ Submodule sm1 $head6..$head8:
+ > change
+ EOF
+ test_cmp expected actual
+'
rm -rf sm1
-test_expect_success 'deleted submodule' "
+test_expect_success 'deleted submodule' '
git diff-index -p --submodule=log HEAD >actual &&
- diff actual - <<-EOF
-Submodule sm1 $head6...0000000 (submodule deleted)
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 $head6...0000000 (submodule deleted)
+ EOF
+ test_cmp expected actual
+'
test_create_repo sm2 &&
head7=$(add_file sm2 foo8 foo9) &&
git add sm2
-test_expect_success 'multiple submodules' "
+test_expect_success 'multiple submodules' '
git diff-index -p --submodule=log HEAD >actual &&
- diff actual - <<-EOF
-Submodule sm1 $head6...0000000 (submodule deleted)
-Submodule sm2 0000000...$head7 (new submodule)
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 $head6...0000000 (submodule deleted)
+ Submodule sm2 0000000...$head7 (new submodule)
+ EOF
+ test_cmp expected actual
+'
-test_expect_success 'path filter' "
+test_expect_success 'path filter' '
git diff-index -p --submodule=log HEAD sm2 >actual &&
- diff actual - <<-EOF
-Submodule sm2 0000000...$head7 (new submodule)
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm2 0000000...$head7 (new submodule)
+ EOF
+ test_cmp expected actual
+'
commit_file sm2
-test_expect_success 'given commit' "
+test_expect_success 'given commit' '
git diff-index -p --submodule=log HEAD^ >actual &&
- diff actual - <<-EOF
-Submodule sm1 $head6...0000000 (submodule deleted)
-Submodule sm2 0000000...$head7 (new submodule)
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 $head6...0000000 (submodule deleted)
+ Submodule sm2 0000000...$head7 (new submodule)
+ EOF
+ test_cmp expected actual
+'
-test_expect_success 'given commit --submodule' "
+test_expect_success 'given commit --submodule' '
git diff-index -p --submodule HEAD^ >actual &&
- diff actual - <<-EOF
-Submodule sm1 $head6...0000000 (submodule deleted)
-Submodule sm2 0000000...$head7 (new submodule)
-EOF
-"
+ cat >expected <<-EOF &&
+ Submodule sm1 $head6...0000000 (submodule deleted)
+ Submodule sm2 0000000...$head7 (new submodule)
+ EOF
+ test_cmp expected actual
+'
-fullhead7=$(cd sm2; git rev-list --max-count=1 $head7)
+fullhead7=$(cd sm2; git rev-parse --verify HEAD)
-test_expect_success 'given commit --submodule=short' "
+test_expect_success 'given commit --submodule=short' '
git diff-index -p --submodule=short HEAD^ >actual &&
- 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
-"
+ cat >expected <<-EOF &&
+ diff --git a/sm1 b/sm1
+ deleted file mode 160000
+ index $head6..0000000
+ --- a/sm1
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -Subproject commit $fullhead6
+ diff --git a/sm2 b/sm2
+ new file mode 160000
+ index 0000000..$head7
+ --- /dev/null
+ +++ b/sm2
+ @@ -0,0 +1 @@
+ +Subproject commit $fullhead7
+ EOF
+ test_cmp expected actual
+'
test_expect_success 'setup .git file for sm2' '
(cd sm2 &&
@@ -419,10 +502,47 @@ test_expect_success 'setup .git file for sm2' '
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
+ cat >expected <<-EOF &&
+ Submodule sm1 $head6...0000000 (submodule deleted)
+ Submodule sm2 0000000...$head7 (new submodule)
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'diff --submodule with objects referenced by alternates' '
+ mkdir sub_alt &&
+ (cd sub_alt &&
+ git init &&
+ echo a >a &&
+ git add a &&
+ git commit -m a
+ ) &&
+ mkdir super &&
+ (cd super &&
+ git clone -s ../sub_alt sub &&
+ git init &&
+ git add sub &&
+ git commit -m "sub a"
+ ) &&
+ (cd sub_alt &&
+ sha1_before=$(git rev-parse --short HEAD)
+ echo b >b &&
+ git add b &&
+ git commit -m b &&
+ sha1_after=$(git rev-parse --short HEAD) &&
+ {
+ echo "Submodule sub $sha1_before..$sha1_after:" &&
+ echo " > b"
+ } >../expected
+ ) &&
+ (cd super &&
+ (cd sub &&
+ git fetch &&
+ git checkout origin/master
+ ) &&
+ git diff --submodule > ../actual
+ ) &&
+ test_cmp expected actual
'
test_done
diff --git a/t/t4042-diff-textconv-caching.sh b/t/t4042-diff-textconv-caching.sh
index 91f8198..04a44d5 100755
--- a/t/t4042-diff-textconv-caching.sh
+++ b/t/t4042-diff-textconv-caching.sh
@@ -106,4 +106,12 @@ test_expect_success 'switching diff driver produces correct results' '
test_cmp expect actual
'
+# The point here is to test that we can log the notes cache and still use it to
+# produce a diff later (older versions of git would segfault on this). It's
+# much more likely to come up in the real world with "log --all -p", but using
+# --no-walk lets us reliably reproduce the order of traversal.
+test_expect_success 'log notes cache and still use cache for -p' '
+ git log --no-walk -p refs/notes/textconv/magic HEAD
+'
+
test_done
diff --git a/t/t4043-diff-rename-binary.sh b/t/t4043-diff-rename-binary.sh
index 0601281..2a2cf91 100755
--- a/t/t4043-diff-rename-binary.sh
+++ b/t/t4043-diff-rename-binary.sh
@@ -23,9 +23,8 @@ test_expect_success 'move the files into a "sub" directory' '
'
cat > expected <<\EOF
- bar => sub/bar | Bin 5 -> 5 bytes
- foo => sub/foo | 0
- 2 files changed, 0 insertions(+), 0 deletions(-)
+- - bar => sub/bar
+0 0 foo => sub/foo
diff --git a/bar b/sub/bar
similarity index 100%
@@ -38,7 +37,8 @@ rename to sub/foo
EOF
test_expect_success 'git show -C -C report renames' '
- git show -C -C --raw --binary --stat | tail -n 12 > current &&
+ git show -C -C --raw --binary --numstat >patch-with-stat &&
+ tail -n 11 patch-with-stat >current &&
test_cmp expected current
'
diff --git a/t/t4045-diff-relative.sh b/t/t4045-diff-relative.sh
new file mode 100755
index 0000000..3950f50
--- /dev/null
+++ b/t/t4045-diff-relative.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+test_description='diff --relative tests'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ git commit --allow-empty -m empty &&
+ echo content >file1 &&
+ mkdir subdir &&
+ echo other content >subdir/file2 &&
+ git add . &&
+ git commit -m one
+'
+
+check_diff() {
+expect=$1; shift
+cat >expected <<EOF
+diff --git a/$expect b/$expect
+new file mode 100644
+index 0000000..25c05ef
+--- /dev/null
++++ b/$expect
+@@ -0,0 +1 @@
++other content
+EOF
+test_expect_success "-p $*" "
+ git diff -p $* HEAD^ >actual &&
+ test_cmp expected actual
+"
+}
+
+check_numstat() {
+expect=$1; shift
+cat >expected <<EOF
+1 0 $expect
+EOF
+test_expect_success "--numstat $*" "
+ echo '1 0 $expect' >expected &&
+ git diff --numstat $* HEAD^ >actual &&
+ test_cmp expected actual
+"
+}
+
+check_stat() {
+expect=$1; shift
+cat >expected <<EOF
+ $expect | 1 +
+ 1 file changed, 1 insertion(+)
+EOF
+test_expect_success "--stat $*" "
+ git diff --stat $* HEAD^ >actual &&
+ test_i18ncmp expected actual
+"
+}
+
+check_raw() {
+expect=$1; shift
+cat >expected <<EOF
+:000000 100644 0000000000000000000000000000000000000000 25c05ef3639d2d270e7fe765a67668f098092bc5 A $expect
+EOF
+test_expect_success "--raw $*" "
+ git diff --no-abbrev --raw $* HEAD^ >actual &&
+ test_cmp expected actual
+"
+}
+
+for type in diff numstat stat raw; do
+ check_$type file2 --relative=subdir/
+ check_$type file2 --relative=subdir
+ check_$type dir/file2 --relative=sub
+done
+
+test_done
diff --git a/t/t4046-diff-unmerged.sh b/t/t4046-diff-unmerged.sh
new file mode 100755
index 0000000..d0f1447
--- /dev/null
+++ b/t/t4046-diff-unmerged.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='diff with unmerged index entries'
+. ./test-lib.sh
+
+test_expect_success setup '
+ for i in 0 1 2 3
+ do
+ blob=$(echo $i | git hash-object --stdin) &&
+ eval "blob$i=$blob" &&
+ eval "m$i=\"100644 \$blob$i $i\"" || return 1
+ done &&
+ paths= &&
+ for b in o x
+ do
+ for o in o x
+ do
+ for t in o x
+ do
+ path="$b$o$t" &&
+ case "$path" in ooo) continue ;; esac
+ paths="$paths$path " &&
+ p=" $path" &&
+ case "$b" in x) echo "$m1$p" ;; esac &&
+ case "$o" in x) echo "$m2$p" ;; esac &&
+ case "$t" in x) echo "$m3$p" ;; esac ||
+ return 1
+ done
+ done
+ done >ls-files-s.expect &&
+ git update-index --index-info <ls-files-s.expect &&
+ git ls-files -s >ls-files-s.actual &&
+ test_cmp ls-files-s.expect ls-files-s.actual
+'
+
+test_expect_success 'diff-files -0' '
+ for path in $paths
+ do
+ >"$path" &&
+ echo ":000000 100644 $_z40 $_z40 U $path"
+ done >diff-files-0.expect &&
+ git diff-files -0 >diff-files-0.actual &&
+ test_cmp diff-files-0.expect diff-files-0.actual
+'
+
+test_expect_success 'diff-files -1' '
+ for path in $paths
+ do
+ >"$path" &&
+ echo ":000000 100644 $_z40 $_z40 U $path" &&
+ case "$path" in
+ x??) echo ":100644 100644 $blob1 $_z40 M $path"
+ esac
+ done >diff-files-1.expect &&
+ git diff-files -1 >diff-files-1.actual &&
+ test_cmp diff-files-1.expect diff-files-1.actual
+'
+
+test_expect_success 'diff-files -2' '
+ for path in $paths
+ do
+ >"$path" &&
+ echo ":000000 100644 $_z40 $_z40 U $path" &&
+ case "$path" in
+ ?x?) echo ":100644 100644 $blob2 $_z40 M $path"
+ esac
+ done >diff-files-2.expect &&
+ git diff-files -2 >diff-files-2.actual &&
+ test_cmp diff-files-2.expect diff-files-2.actual &&
+ git diff-files >diff-files-default-2.actual &&
+ test_cmp diff-files-2.expect diff-files-default-2.actual
+'
+
+test_expect_success 'diff-files -3' '
+ for path in $paths
+ do
+ >"$path" &&
+ echo ":000000 100644 $_z40 $_z40 U $path" &&
+ case "$path" in
+ ??x) echo ":100644 100644 $blob3 $_z40 M $path"
+ esac
+ done >diff-files-3.expect &&
+ git diff-files -3 >diff-files-3.actual &&
+ test_cmp diff-files-3.expect diff-files-3.actual
+'
+
+test_done
diff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh
new file mode 100755
index 0000000..447a8ff
--- /dev/null
+++ b/t/t4047-diff-dirstat.sh
@@ -0,0 +1,991 @@
+#!/bin/sh
+
+test_description='diff --dirstat tests'
+. ./test-lib.sh
+
+# set up two commits where the second commit has these files
+# (10 lines in each file):
+#
+# unchanged/text (unchanged from 1st commit)
+# changed/text (changed 1st line)
+# rearranged/text (swapped 1st and 2nd line)
+# dst/copy/unchanged/text (copied from src/copy/unchanged/text, unchanged)
+# dst/copy/changed/text (copied from src/copy/changed/text, changed)
+# dst/copy/rearranged/text (copied from src/copy/rearranged/text, rearranged)
+# dst/move/unchanged/text (moved from src/move/unchanged/text, unchanged)
+# dst/move/changed/text (moved from src/move/changed/text, changed)
+# dst/move/rearranged/text (moved from src/move/rearranged/text, rearranged)
+
+test_expect_success 'setup' '
+ mkdir unchanged &&
+ mkdir changed &&
+ mkdir rearranged &&
+ mkdir src &&
+ mkdir src/copy &&
+ mkdir src/copy/unchanged &&
+ mkdir src/copy/changed &&
+ mkdir src/copy/rearranged &&
+ mkdir src/move &&
+ mkdir src/move/unchanged &&
+ mkdir src/move/changed &&
+ mkdir src/move/rearranged &&
+ cat <<EOF >unchanged/text &&
+unchanged line #0
+unchanged line #1
+unchanged line #2
+unchanged line #3
+unchanged line #4
+unchanged line #5
+unchanged line #6
+unchanged line #7
+unchanged line #8
+unchanged line #9
+EOF
+ cat <<EOF >changed/text &&
+changed line #0
+changed line #1
+changed line #2
+changed line #3
+changed line #4
+changed line #5
+changed line #6
+changed line #7
+changed line #8
+changed line #9
+EOF
+ cat <<EOF >rearranged/text &&
+rearranged line #0
+rearranged line #1
+rearranged line #2
+rearranged line #3
+rearranged line #4
+rearranged line #5
+rearranged line #6
+rearranged line #7
+rearranged line #8
+rearranged line #9
+EOF
+ cat <<EOF >src/copy/unchanged/text &&
+copy unchanged line #0
+copy unchanged line #1
+copy unchanged line #2
+copy unchanged line #3
+copy unchanged line #4
+copy unchanged line #5
+copy unchanged line #6
+copy unchanged line #7
+copy unchanged line #8
+copy unchanged line #9
+EOF
+ cat <<EOF >src/copy/changed/text &&
+copy changed line #0
+copy changed line #1
+copy changed line #2
+copy changed line #3
+copy changed line #4
+copy changed line #5
+copy changed line #6
+copy changed line #7
+copy changed line #8
+copy changed line #9
+EOF
+ cat <<EOF >src/copy/rearranged/text &&
+copy rearranged line #0
+copy rearranged line #1
+copy rearranged line #2
+copy rearranged line #3
+copy rearranged line #4
+copy rearranged line #5
+copy rearranged line #6
+copy rearranged line #7
+copy rearranged line #8
+copy rearranged line #9
+EOF
+ cat <<EOF >src/move/unchanged/text &&
+move unchanged line #0
+move unchanged line #1
+move unchanged line #2
+move unchanged line #3
+move unchanged line #4
+move unchanged line #5
+move unchanged line #6
+move unchanged line #7
+move unchanged line #8
+move unchanged line #9
+EOF
+ cat <<EOF >src/move/changed/text &&
+move changed line #0
+move changed line #1
+move changed line #2
+move changed line #3
+move changed line #4
+move changed line #5
+move changed line #6
+move changed line #7
+move changed line #8
+move changed line #9
+EOF
+ cat <<EOF >src/move/rearranged/text &&
+move rearranged line #0
+move rearranged line #1
+move rearranged line #2
+move rearranged line #3
+move rearranged line #4
+move rearranged line #5
+move rearranged line #6
+move rearranged line #7
+move rearranged line #8
+move rearranged line #9
+EOF
+ git add . &&
+ git commit -m "initial" &&
+ mkdir dst &&
+ mkdir dst/copy &&
+ mkdir dst/copy/unchanged &&
+ mkdir dst/copy/changed &&
+ mkdir dst/copy/rearranged &&
+ mkdir dst/move &&
+ mkdir dst/move/unchanged &&
+ mkdir dst/move/changed &&
+ mkdir dst/move/rearranged &&
+ cat <<EOF >changed/text &&
+CHANGED XXXXXXX line #0
+changed line #1
+changed line #2
+changed line #3
+changed line #4
+changed line #5
+changed line #6
+changed line #7
+changed line #8
+changed line #9
+EOF
+ cat <<EOF >rearranged/text &&
+rearranged line #1
+rearranged line #0
+rearranged line #2
+rearranged line #3
+rearranged line #4
+rearranged line #5
+rearranged line #6
+rearranged line #7
+rearranged line #8
+rearranged line #9
+EOF
+ cat <<EOF >dst/copy/unchanged/text &&
+copy unchanged line #0
+copy unchanged line #1
+copy unchanged line #2
+copy unchanged line #3
+copy unchanged line #4
+copy unchanged line #5
+copy unchanged line #6
+copy unchanged line #7
+copy unchanged line #8
+copy unchanged line #9
+EOF
+ cat <<EOF >dst/copy/changed/text &&
+copy XXXCHANGED line #0
+copy changed line #1
+copy changed line #2
+copy changed line #3
+copy changed line #4
+copy changed line #5
+copy changed line #6
+copy changed line #7
+copy changed line #8
+copy changed line #9
+EOF
+ cat <<EOF >dst/copy/rearranged/text &&
+copy rearranged line #1
+copy rearranged line #0
+copy rearranged line #2
+copy rearranged line #3
+copy rearranged line #4
+copy rearranged line #5
+copy rearranged line #6
+copy rearranged line #7
+copy rearranged line #8
+copy rearranged line #9
+EOF
+ cat <<EOF >dst/move/unchanged/text &&
+move unchanged line #0
+move unchanged line #1
+move unchanged line #2
+move unchanged line #3
+move unchanged line #4
+move unchanged line #5
+move unchanged line #6
+move unchanged line #7
+move unchanged line #8
+move unchanged line #9
+EOF
+ cat <<EOF >dst/move/changed/text &&
+move XXXCHANGED line #0
+move changed line #1
+move changed line #2
+move changed line #3
+move changed line #4
+move changed line #5
+move changed line #6
+move changed line #7
+move changed line #8
+move changed line #9
+EOF
+ cat <<EOF >dst/move/rearranged/text &&
+move rearranged line #1
+move rearranged line #0
+move rearranged line #2
+move rearranged line #3
+move rearranged line #4
+move rearranged line #5
+move rearranged line #6
+move rearranged line #7
+move rearranged line #8
+move rearranged line #9
+EOF
+ git add . &&
+ git rm -r src/move/unchanged &&
+ git rm -r src/move/changed &&
+ git rm -r src/move/rearranged &&
+ git commit -m "changes" &&
+ git config diff.renames false
+'
+
+cat <<EOF >expect_diff_stat
+1 1 changed/text
+10 0 dst/copy/changed/text
+10 0 dst/copy/rearranged/text
+10 0 dst/copy/unchanged/text
+10 0 dst/move/changed/text
+10 0 dst/move/rearranged/text
+10 0 dst/move/unchanged/text
+1 1 rearranged/text
+0 10 src/move/changed/text
+0 10 src/move/rearranged/text
+0 10 src/move/unchanged/text
+EOF
+
+cat <<EOF >expect_diff_stat_M
+1 1 changed/text
+10 0 dst/copy/changed/text
+10 0 dst/copy/rearranged/text
+10 0 dst/copy/unchanged/text
+1 1 {src => dst}/move/changed/text
+1 1 {src => dst}/move/rearranged/text
+0 0 {src => dst}/move/unchanged/text
+1 1 rearranged/text
+EOF
+
+cat <<EOF >expect_diff_stat_CC
+1 1 changed/text
+1 1 {src => dst}/copy/changed/text
+1 1 {src => dst}/copy/rearranged/text
+0 0 {src => dst}/copy/unchanged/text
+1 1 {src => dst}/move/changed/text
+1 1 {src => dst}/move/rearranged/text
+0 0 {src => dst}/move/unchanged/text
+1 1 rearranged/text
+EOF
+
+test_expect_success 'sanity check setup (--numstat)' '
+ git diff --numstat HEAD^..HEAD >actual_diff_stat &&
+ test_cmp expect_diff_stat actual_diff_stat &&
+ git diff --numstat -M HEAD^..HEAD >actual_diff_stat_M &&
+ test_cmp expect_diff_stat_M actual_diff_stat_M &&
+ git diff --numstat -C -C HEAD^..HEAD >actual_diff_stat_CC &&
+ test_cmp expect_diff_stat_CC actual_diff_stat_CC
+'
+
+# changed/text and rearranged/text falls below default 3% threshold
+cat <<EOF >expect_diff_dirstat
+ 10.8% dst/copy/changed/
+ 10.8% dst/copy/rearranged/
+ 10.8% dst/copy/unchanged/
+ 10.8% dst/move/changed/
+ 10.8% dst/move/rearranged/
+ 10.8% dst/move/unchanged/
+ 10.8% src/move/changed/
+ 10.8% src/move/rearranged/
+ 10.8% src/move/unchanged/
+EOF
+
+# rearranged/text falls below default 3% threshold
+cat <<EOF >expect_diff_dirstat_M
+ 5.8% changed/
+ 29.3% dst/copy/changed/
+ 29.3% dst/copy/rearranged/
+ 29.3% dst/copy/unchanged/
+ 5.8% dst/move/changed/
+EOF
+
+# rearranged/text falls below default 3% threshold
+cat <<EOF >expect_diff_dirstat_CC
+ 32.6% changed/
+ 32.6% dst/copy/changed/
+ 32.6% dst/move/changed/
+EOF
+
+test_expect_success 'various ways to misspell --dirstat' '
+ test_must_fail git show --dirstat10 &&
+ test_must_fail git show --dirstat10,files &&
+ test_must_fail git show -X=20 &&
+ test_must_fail git show -X=20,cumulative
+'
+
+test_expect_success 'vanilla --dirstat' '
+ git diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'vanilla -X' '
+ git diff -X HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff -X -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff -X -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'explicit defaults: --dirstat=changes,noncumulative,3' '
+ git diff --dirstat=changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat=changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat=changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'explicit defaults: -Xchanges,noncumulative,3' '
+ git diff -Xchanges,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff -Xchanges,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff -Xchanges,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'later options override earlier options:' '
+ git diff --dirstat=files,10,cumulative,changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat=files,10,cumulative,changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat=files,10,cumulative,changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC &&
+ git diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'non-defaults in config overridden by explicit defaults on command line' '
+ git -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+ 2.1% changed/
+ 10.8% dst/copy/changed/
+ 10.8% dst/copy/rearranged/
+ 10.8% dst/copy/unchanged/
+ 10.8% dst/move/changed/
+ 10.8% dst/move/rearranged/
+ 10.8% dst/move/unchanged/
+ 0.0% rearranged/
+ 10.8% src/move/changed/
+ 10.8% src/move/rearranged/
+ 10.8% src/move/unchanged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+ 5.8% changed/
+ 29.3% dst/copy/changed/
+ 29.3% dst/copy/rearranged/
+ 29.3% dst/copy/unchanged/
+ 5.8% dst/move/changed/
+ 0.1% dst/move/rearranged/
+ 0.1% rearranged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+ 32.6% changed/
+ 32.6% dst/copy/changed/
+ 0.6% dst/copy/rearranged/
+ 32.6% dst/move/changed/
+ 0.6% dst/move/rearranged/
+ 0.6% rearranged/
+EOF
+
+test_expect_success '--dirstat=0' '
+ git diff --dirstat=0 HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat=0 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat=0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '-X0' '
+ git diff -X0 HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff -X0 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff -X0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=0' '
+ git -c diff.dirstat=0 diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git -c diff.dirstat=0 diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git -c diff.dirstat=0 diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+ 2.1% changed/
+ 10.8% dst/copy/changed/
+ 10.8% dst/copy/rearranged/
+ 10.8% dst/copy/unchanged/
+ 32.5% dst/copy/
+ 10.8% dst/move/changed/
+ 10.8% dst/move/rearranged/
+ 10.8% dst/move/unchanged/
+ 32.5% dst/move/
+ 65.1% dst/
+ 0.0% rearranged/
+ 10.8% src/move/changed/
+ 10.8% src/move/rearranged/
+ 10.8% src/move/unchanged/
+ 32.5% src/move/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+ 5.8% changed/
+ 29.3% dst/copy/changed/
+ 29.3% dst/copy/rearranged/
+ 29.3% dst/copy/unchanged/
+ 88.0% dst/copy/
+ 5.8% dst/move/changed/
+ 0.1% dst/move/rearranged/
+ 5.9% dst/move/
+ 94.0% dst/
+ 0.1% rearranged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+ 32.6% changed/
+ 32.6% dst/copy/changed/
+ 0.6% dst/copy/rearranged/
+ 33.3% dst/copy/
+ 32.6% dst/move/changed/
+ 0.6% dst/move/rearranged/
+ 33.3% dst/move/
+ 66.6% dst/
+ 0.6% rearranged/
+EOF
+
+test_expect_success '--dirstat=0 --cumulative' '
+ git diff --dirstat=0 --cumulative HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat=0 --cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat=0 --cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '--dirstat=0,cumulative' '
+ git diff --dirstat=0,cumulative HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat=0,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat=0,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '-X0,cumulative' '
+ git diff -X0,cumulative HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff -X0,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff -X0,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=0,cumulative' '
+ git -c diff.dirstat=0,cumulative diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git -c diff.dirstat=0,cumulative diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git -c diff.dirstat=0,cumulative diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=0 & --dirstat=cumulative' '
+ git -c diff.dirstat=0 diff --dirstat=cumulative HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git -c diff.dirstat=0 diff --dirstat=cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git -c diff.dirstat=0 diff --dirstat=cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+ 9.0% changed/
+ 9.0% dst/copy/changed/
+ 9.0% dst/copy/rearranged/
+ 9.0% dst/copy/unchanged/
+ 9.0% dst/move/changed/
+ 9.0% dst/move/rearranged/
+ 9.0% dst/move/unchanged/
+ 9.0% rearranged/
+ 9.0% src/move/changed/
+ 9.0% src/move/rearranged/
+ 9.0% src/move/unchanged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+ 14.2% changed/
+ 14.2% dst/copy/changed/
+ 14.2% dst/copy/rearranged/
+ 14.2% dst/copy/unchanged/
+ 14.2% dst/move/changed/
+ 14.2% dst/move/rearranged/
+ 14.2% rearranged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+ 16.6% changed/
+ 16.6% dst/copy/changed/
+ 16.6% dst/copy/rearranged/
+ 16.6% dst/move/changed/
+ 16.6% dst/move/rearranged/
+ 16.6% rearranged/
+EOF
+
+test_expect_success '--dirstat-by-file' '
+ git diff --dirstat-by-file HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat-by-file -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat-by-file -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '--dirstat=files' '
+ git diff --dirstat=files HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat=files -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat=files -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=files' '
+ git -c diff.dirstat=files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git -c diff.dirstat=files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git -c diff.dirstat=files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+ 27.2% dst/copy/
+ 27.2% dst/move/
+ 27.2% src/move/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+ 14.2% changed/
+ 14.2% dst/copy/changed/
+ 14.2% dst/copy/rearranged/
+ 14.2% dst/copy/unchanged/
+ 14.2% dst/move/changed/
+ 14.2% dst/move/rearranged/
+ 14.2% rearranged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+ 16.6% changed/
+ 16.6% dst/copy/changed/
+ 16.6% dst/copy/rearranged/
+ 16.6% dst/move/changed/
+ 16.6% dst/move/rearranged/
+ 16.6% rearranged/
+EOF
+
+test_expect_success '--dirstat-by-file=10' '
+ git diff --dirstat-by-file=10 HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat-by-file=10 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat-by-file=10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '--dirstat=files,10' '
+ git diff --dirstat=files,10 HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat=files,10 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat=files,10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=10,files' '
+ git -c diff.dirstat=10,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git -c diff.dirstat=10,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git -c diff.dirstat=10,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+ 9.0% changed/
+ 9.0% dst/copy/changed/
+ 9.0% dst/copy/rearranged/
+ 9.0% dst/copy/unchanged/
+ 27.2% dst/copy/
+ 9.0% dst/move/changed/
+ 9.0% dst/move/rearranged/
+ 9.0% dst/move/unchanged/
+ 27.2% dst/move/
+ 54.5% dst/
+ 9.0% rearranged/
+ 9.0% src/move/changed/
+ 9.0% src/move/rearranged/
+ 9.0% src/move/unchanged/
+ 27.2% src/move/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+ 14.2% changed/
+ 14.2% dst/copy/changed/
+ 14.2% dst/copy/rearranged/
+ 14.2% dst/copy/unchanged/
+ 42.8% dst/copy/
+ 14.2% dst/move/changed/
+ 14.2% dst/move/rearranged/
+ 28.5% dst/move/
+ 71.4% dst/
+ 14.2% rearranged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+ 16.6% changed/
+ 16.6% dst/copy/changed/
+ 16.6% dst/copy/rearranged/
+ 33.3% dst/copy/
+ 16.6% dst/move/changed/
+ 16.6% dst/move/rearranged/
+ 33.3% dst/move/
+ 66.6% dst/
+ 16.6% rearranged/
+EOF
+
+test_expect_success '--dirstat-by-file --cumulative' '
+ git diff --dirstat-by-file --cumulative HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat-by-file --cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat-by-file --cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '--dirstat=files,cumulative' '
+ git diff --dirstat=files,cumulative HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat=files,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat=files,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=cumulative,files' '
+ git -c diff.dirstat=cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git -c diff.dirstat=cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git -c diff.dirstat=cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+ 27.2% dst/copy/
+ 27.2% dst/move/
+ 54.5% dst/
+ 27.2% src/move/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+ 14.2% changed/
+ 14.2% dst/copy/changed/
+ 14.2% dst/copy/rearranged/
+ 14.2% dst/copy/unchanged/
+ 42.8% dst/copy/
+ 14.2% dst/move/changed/
+ 14.2% dst/move/rearranged/
+ 28.5% dst/move/
+ 71.4% dst/
+ 14.2% rearranged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+ 16.6% changed/
+ 16.6% dst/copy/changed/
+ 16.6% dst/copy/rearranged/
+ 33.3% dst/copy/
+ 16.6% dst/move/changed/
+ 16.6% dst/move/rearranged/
+ 33.3% dst/move/
+ 66.6% dst/
+ 16.6% rearranged/
+EOF
+
+test_expect_success '--dirstat=files,cumulative,10' '
+ git diff --dirstat=files,cumulative,10 HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat=files,cumulative,10 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat=files,cumulative,10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=10,cumulative,files' '
+ git -c diff.dirstat=10,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git -c diff.dirstat=10,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git -c diff.dirstat=10,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+ 27.2% dst/copy/
+ 27.2% dst/move/
+ 54.5% dst/
+ 27.2% src/move/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+ 42.8% dst/copy/
+ 28.5% dst/move/
+ 71.4% dst/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+ 33.3% dst/copy/
+ 33.3% dst/move/
+ 66.6% dst/
+EOF
+
+test_expect_success '--dirstat=files,cumulative,16.7' '
+ git diff --dirstat=files,cumulative,16.7 HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat=files,cumulative,16.7 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat=files,cumulative,16.7 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=16.7,cumulative,files' '
+ git -c diff.dirstat=16.7,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git -c diff.dirstat=16.7,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git -c diff.dirstat=16.7,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=16.70,cumulative,files' '
+ git -c diff.dirstat=16.70,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git -c diff.dirstat=16.70,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git -c diff.dirstat=16.70,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '--dirstat=files,cumulative,27.2' '
+ git diff --dirstat=files,cumulative,27.2 HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat=files,cumulative,27.2 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat=files,cumulative,27.2 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '--dirstat=files,cumulative,27.09' '
+ git diff --dirstat=files,cumulative,27.09 HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat=files,cumulative,27.09 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat=files,cumulative,27.09 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+ 10.6% dst/copy/changed/
+ 10.6% dst/copy/rearranged/
+ 10.6% dst/copy/unchanged/
+ 10.6% dst/move/changed/
+ 10.6% dst/move/rearranged/
+ 10.6% dst/move/unchanged/
+ 10.6% src/move/changed/
+ 10.6% src/move/rearranged/
+ 10.6% src/move/unchanged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+ 5.2% changed/
+ 26.3% dst/copy/changed/
+ 26.3% dst/copy/rearranged/
+ 26.3% dst/copy/unchanged/
+ 5.2% dst/move/changed/
+ 5.2% dst/move/rearranged/
+ 5.2% rearranged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+ 16.6% changed/
+ 16.6% dst/copy/changed/
+ 16.6% dst/copy/rearranged/
+ 16.6% dst/move/changed/
+ 16.6% dst/move/rearranged/
+ 16.6% rearranged/
+EOF
+
+test_expect_success '--dirstat=lines' '
+ git diff --dirstat=lines HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat=lines -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat=lines -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=lines' '
+ git -c diff.dirstat=lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git -c diff.dirstat=lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git -c diff.dirstat=lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+ 2.1% changed/
+ 10.6% dst/copy/changed/
+ 10.6% dst/copy/rearranged/
+ 10.6% dst/copy/unchanged/
+ 10.6% dst/move/changed/
+ 10.6% dst/move/rearranged/
+ 10.6% dst/move/unchanged/
+ 2.1% rearranged/
+ 10.6% src/move/changed/
+ 10.6% src/move/rearranged/
+ 10.6% src/move/unchanged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+ 5.2% changed/
+ 26.3% dst/copy/changed/
+ 26.3% dst/copy/rearranged/
+ 26.3% dst/copy/unchanged/
+ 5.2% dst/move/changed/
+ 5.2% dst/move/rearranged/
+ 5.2% rearranged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+ 16.6% changed/
+ 16.6% dst/copy/changed/
+ 16.6% dst/copy/rearranged/
+ 16.6% dst/move/changed/
+ 16.6% dst/move/rearranged/
+ 16.6% rearranged/
+EOF
+
+test_expect_success '--dirstat=lines,0' '
+ git diff --dirstat=lines,0 HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git diff --dirstat=lines,0 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git diff --dirstat=lines,0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=0,lines' '
+ git -c diff.dirstat=0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ git -c diff.dirstat=0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ git -c diff.dirstat=0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '--dirstat=future_param,lines,0 should fail loudly' '
+ test_must_fail git diff --dirstat=future_param,lines,0 HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
+ test_debug "cat actual_error" &&
+ test_cmp /dev/null actual_diff_dirstat &&
+ test_i18ngrep -q "future_param" actual_error &&
+ test_i18ngrep -q "\--dirstat" actual_error
+'
+
+test_expect_success '--dirstat=dummy1,cumulative,2dummy should report both unrecognized parameters' '
+ test_must_fail git diff --dirstat=dummy1,cumulative,2dummy HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
+ test_debug "cat actual_error" &&
+ test_cmp /dev/null actual_diff_dirstat &&
+ test_i18ngrep -q "dummy1" actual_error &&
+ test_i18ngrep -q "2dummy" actual_error &&
+ test_i18ngrep -q "\--dirstat" actual_error
+'
+
+test_expect_success 'diff.dirstat=future_param,0,lines should warn, but still work' '
+ git -c diff.dirstat=future_param,0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
+ test_debug "cat actual_error" &&
+ test_cmp expect_diff_dirstat actual_diff_dirstat &&
+ test_i18ngrep -q "future_param" actual_error &&
+ test_i18ngrep -q "diff\\.dirstat" actual_error &&
+
+ git -c diff.dirstat=future_param,0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M 2>actual_error &&
+ test_debug "cat actual_error" &&
+ test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+ test_i18ngrep -q "future_param" actual_error &&
+ test_i18ngrep -q "diff\\.dirstat" actual_error &&
+
+ git -c diff.dirstat=future_param,0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC 2>actual_error &&
+ test_debug "cat actual_error" &&
+ test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC &&
+ test_i18ngrep -q "future_param" actual_error &&
+ test_i18ngrep -q "diff\\.dirstat" actual_error
+'
+
+test_expect_success '--shortstat --dirstat should output only one dirstat' '
+ git diff --shortstat --dirstat=changes HEAD^..HEAD >out &&
+ grep " dst/copy/changed/$" out >actual_diff_shortstat_dirstat_changes &&
+ test_line_count = 1 actual_diff_shortstat_dirstat_changes &&
+
+ git diff --shortstat --dirstat=lines HEAD^..HEAD >out &&
+ grep " dst/copy/changed/$" out >actual_diff_shortstat_dirstat_lines &&
+ test_line_count = 1 actual_diff_shortstat_dirstat_lines &&
+
+ git diff --shortstat --dirstat=files HEAD^..HEAD >out &&
+ grep " dst/copy/changed/$" out >actual_diff_shortstat_dirstat_files &&
+ test_line_count = 1 actual_diff_shortstat_dirstat_files
+'
+
+test_done
diff --git a/t/t4048-diff-combined-binary.sh b/t/t4048-diff-combined-binary.sh
new file mode 100755
index 0000000..87a8949
--- /dev/null
+++ b/t/t4048-diff-combined-binary.sh
@@ -0,0 +1,212 @@
+#!/bin/sh
+
+test_description='combined and merge diff handle binary files and textconv'
+. ./test-lib.sh
+
+test_expect_success 'setup binary merge conflict' '
+ echo oneQ1 | q_to_nul >binary &&
+ git add binary &&
+ git commit -m one &&
+ echo twoQ2 | q_to_nul >binary &&
+ git commit -a -m two &&
+ git checkout -b branch-binary HEAD^ &&
+ echo threeQ3 | q_to_nul >binary &&
+ git commit -a -m three &&
+ test_must_fail git merge master &&
+ echo resolvedQhooray | q_to_nul >binary &&
+ git commit -a -m resolved
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --git a/binary b/binary
+index 7ea6ded..9563691 100644
+Binary files a/binary and b/binary differ
+resolved
+
+diff --git a/binary b/binary
+index 6197570..9563691 100644
+Binary files a/binary and b/binary differ
+EOF
+test_expect_success 'diff -m indicates binary-ness' '
+ git show --format=%s -m >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --combined binary
+index 7ea6ded,6197570..9563691
+Binary files differ
+EOF
+test_expect_success 'diff -c indicates binary-ness' '
+ git show --format=%s -c >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --cc binary
+index 7ea6ded,6197570..9563691
+Binary files differ
+EOF
+test_expect_success 'diff --cc indicates binary-ness' '
+ git show --format=%s --cc >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup non-binary with binary attribute' '
+ git checkout master &&
+ test_commit one text &&
+ test_commit two text &&
+ git checkout -b branch-text HEAD^ &&
+ test_commit three text &&
+ test_must_fail git merge master &&
+ test_commit resolved text &&
+ echo text -diff >.gitattributes
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --git a/text b/text
+index 2bdf67a..2ab19ae 100644
+Binary files a/text and b/text differ
+resolved
+
+diff --git a/text b/text
+index f719efd..2ab19ae 100644
+Binary files a/text and b/text differ
+EOF
+test_expect_success 'diff -m respects binary attribute' '
+ git show --format=%s -m >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --combined text
+index 2bdf67a,f719efd..2ab19ae
+Binary files differ
+EOF
+test_expect_success 'diff -c respects binary attribute' '
+ git show --format=%s -c >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --cc text
+index 2bdf67a,f719efd..2ab19ae
+Binary files differ
+EOF
+test_expect_success 'diff --cc respects binary attribute' '
+ git show --format=%s --cc >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup textconv attribute' '
+ echo "text diff=upcase" >.gitattributes &&
+ git config diff.upcase.textconv "tr a-z A-Z <"
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --git a/text b/text
+index 2bdf67a..2ab19ae 100644
+--- a/text
++++ b/text
+@@ -1 +1 @@
+-THREE
++RESOLVED
+resolved
+
+diff --git a/text b/text
+index f719efd..2ab19ae 100644
+--- a/text
++++ b/text
+@@ -1 +1 @@
+-TWO
++RESOLVED
+EOF
+test_expect_success 'diff -m respects textconv attribute' '
+ git show --format=%s -m >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --combined text
+index 2bdf67a,f719efd..2ab19ae
+--- a/text
++++ b/text
+@@@ -1,1 -1,1 +1,1 @@@
+- THREE
+ -TWO
+++RESOLVED
+EOF
+test_expect_success 'diff -c respects textconv attribute' '
+ git show --format=%s -c >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --cc text
+index 2bdf67a,f719efd..2ab19ae
+--- a/text
++++ b/text
+@@@ -1,1 -1,1 +1,1 @@@
+- THREE
+ -TWO
+++RESOLVED
+EOF
+test_expect_success 'diff --cc respects textconv attribute' '
+ git show --format=%s --cc >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+diff --combined text
+index 2bdf67a,f719efd..2ab19ae
+--- a/text
++++ b/text
+@@@ -1,1 -1,1 +1,1 @@@
+- three
+ -two
+++resolved
+EOF
+test_expect_success 'diff-tree plumbing does not respect textconv' '
+ git diff-tree HEAD -c -p >full &&
+ tail -n +2 full >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+diff --cc text
+index 2bdf67a,f719efd..0000000
+--- a/text
++++ b/text
+@@@ -1,1 -1,1 +1,5 @@@
+++<<<<<<< HEAD
+ +THREE
+++=======
++ TWO
+++>>>>>>> MASTER
+EOF
+test_expect_success 'diff --cc respects textconv on worktree file' '
+ git reset --hard HEAD^ &&
+ test_must_fail git merge master &&
+ git diff >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4049-diff-stat-count.sh b/t/t4049-diff-stat-count.sh
new file mode 100755
index 0000000..a341217
--- /dev/null
+++ b/t/t4049-diff-stat-count.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+# Copyright (c) 2011, Google Inc.
+
+test_description='diff --stat-count'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ >a &&
+ >b &&
+ >c &&
+ >d &&
+ git add a b c d &&
+ git commit -m initial
+'
+
+test_expect_success 'mode-only change show as a 0-line change' '
+ git reset --hard &&
+ test_chmod +x b d &&
+ echo a >a &&
+ echo c >c &&
+ cat >expect <<-\EOF &&
+ a | 1 +
+ b | 0
+ ...
+ 4 files changed, 2 insertions(+)
+ EOF
+ git diff --stat --stat-count=2 HEAD >actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'binary changes do not count in lines' '
+ git reset --hard &&
+ echo a >a &&
+ echo c >c &&
+ cat "$TEST_DIRECTORY"/test-binary-1.png >d &&
+ cat >expect <<-\EOF &&
+ a | 1 +
+ c | 1 +
+ ...
+ 3 files changed, 2 insertions(+)
+ EOF
+ git diff --stat --stat-count=2 >actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'exclude unmerged entries from total file count' '
+ git reset --hard &&
+ echo a >a &&
+ echo b >b &&
+ git ls-files -s a >x &&
+ git rm -f d &&
+ for stage in 1 2 3
+ do
+ sed -e "s/ 0 a/ $stage d/" x
+ done |
+ git update-index --index-info &&
+ echo d >d &&
+ cat >expect <<-\EOF &&
+ a | 1 +
+ b | 1 +
+ ...
+ 3 files changed, 3 insertions(+)
+ EOF
+ git diff --stat --stat-count=2 >actual &&
+ test_i18ncmp expect actual
+'
+
+test_done
diff --git a/t/t4050-diff-histogram.sh b/t/t4050-diff-histogram.sh
new file mode 100755
index 0000000..fd3e86a
--- /dev/null
+++ b/t/t4050-diff-histogram.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+test_description='histogram diff algorithm'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-alternative.sh
+
+test_diff_frobnitz "histogram"
+
+test_diff_unique "histogram"
+
+test_done
diff --git a/t/t4051-diff-function-context.sh b/t/t4051-diff-function-context.sh
new file mode 100755
index 0000000..b79b877
--- /dev/null
+++ b/t/t4051-diff-function-context.sh
@@ -0,0 +1,182 @@
+#!/bin/sh
+
+test_description='diff function context'
+
+. ./test-lib.sh
+
+dir="$TEST_DIRECTORY/t4051"
+
+commit_and_tag () {
+ tag=$1 &&
+ shift &&
+ git add "$@" &&
+ test_tick &&
+ git commit -m "$tag" &&
+ git tag "$tag"
+}
+
+first_context_line () {
+ awk '
+ found {print; exit}
+ /^@@/ {found = 1}
+ '
+}
+
+last_context_line () {
+ sed -ne \$p
+}
+
+check_diff () {
+ name=$1
+ desc=$2
+ options="-W $3"
+
+ test_expect_success "$desc" '
+ git diff $options "$name^" "$name" >"$name.diff"
+ '
+
+ test_expect_success ' diff applies' '
+ test_when_finished "git reset --hard" &&
+ git checkout --detach "$name^" &&
+ git apply --index "$name.diff" &&
+ git diff --exit-code "$name"
+ '
+}
+
+test_expect_success 'setup' '
+ cat "$dir/includes.c" "$dir/dummy.c" "$dir/dummy.c" "$dir/hello.c" \
+ "$dir/dummy.c" "$dir/dummy.c" >file.c &&
+ commit_and_tag initial file.c &&
+
+ grep -v "delete me from hello" <file.c >file.c.new &&
+ mv file.c.new file.c &&
+ commit_and_tag changed_hello file.c &&
+
+ grep -v "delete me from includes" <file.c >file.c.new &&
+ mv file.c.new file.c &&
+ commit_and_tag changed_includes file.c &&
+
+ cat "$dir/appended1.c" >>file.c &&
+ commit_and_tag appended file.c &&
+
+ cat "$dir/appended2.c" >>file.c &&
+ commit_and_tag extended file.c &&
+
+ grep -v "Begin of second part" <file.c >file.c.new &&
+ mv file.c.new file.c &&
+ commit_and_tag long_common_tail file.c &&
+
+ git checkout initial &&
+ grep -v "delete me from hello" <file.c >file.c.new &&
+ mv file.c.new file.c &&
+ cat "$dir/appended1.c" >>file.c &&
+ commit_and_tag changed_hello_appended file.c
+'
+
+check_diff changed_hello 'changed function'
+
+test_expect_success ' context includes begin' '
+ grep "^ .*Begin of hello" changed_hello.diff
+'
+
+test_expect_success ' context includes end' '
+ grep "^ .*End of hello" changed_hello.diff
+'
+
+test_expect_success ' context does not include other functions' '
+ test $(grep -c "^[ +-].*Begin" changed_hello.diff) -le 1
+'
+
+test_expect_success ' context does not include preceding empty lines' '
+ test "$(first_context_line <changed_hello.diff)" != " "
+'
+
+test_expect_success ' context does not include trailing empty lines' '
+ test "$(last_context_line <changed_hello.diff)" != " "
+'
+
+check_diff changed_includes 'changed includes'
+
+test_expect_success ' context includes begin' '
+ grep "^ .*Begin.h" changed_includes.diff
+'
+
+test_expect_success ' context includes end' '
+ grep "^ .*End.h" changed_includes.diff
+'
+
+test_expect_success ' context does not include other functions' '
+ test $(grep -c "^[ +-].*Begin" changed_includes.diff) -le 1
+'
+
+test_expect_success ' context does not include trailing empty lines' '
+ test "$(last_context_line <changed_includes.diff)" != " "
+'
+
+check_diff appended 'appended function'
+
+test_expect_success ' context includes begin' '
+ grep "^[+].*Begin of first part" appended.diff
+'
+
+test_expect_success ' context includes end' '
+ grep "^[+].*End of first part" appended.diff
+'
+
+test_expect_success ' context does not include other functions' '
+ test $(grep -c "^[ +-].*Begin" appended.diff) -le 1
+'
+
+check_diff extended 'appended function part'
+
+test_expect_success ' context includes begin' '
+ grep "^ .*Begin of first part" extended.diff
+'
+
+test_expect_success ' context includes end' '
+ grep "^[+].*End of second part" extended.diff
+'
+
+test_expect_success ' context does not include other functions' '
+ test $(grep -c "^[ +-].*Begin" extended.diff) -le 2
+'
+
+test_expect_success ' context does not include preceding empty lines' '
+ test "$(first_context_line <extended.diff)" != " "
+'
+
+check_diff long_common_tail 'change with long common tail and no context' -U0
+
+test_expect_success ' context includes begin' '
+ grep "^ .*Begin of first part" long_common_tail.diff
+'
+
+test_expect_success ' context includes end' '
+ grep "^ .*End of second part" long_common_tail.diff
+'
+
+test_expect_success ' context does not include other functions' '
+ test $(grep -c "^[ +-].*Begin" long_common_tail.diff) -le 2
+'
+
+test_expect_success ' context does not include preceding empty lines' '
+ test "$(first_context_line <long_common_tail.diff.diff)" != " "
+'
+
+check_diff changed_hello_appended 'changed function plus appended function'
+
+test_expect_success ' context includes begin' '
+ grep "^ .*Begin of hello" changed_hello_appended.diff &&
+ grep "^[+].*Begin of first part" changed_hello_appended.diff
+'
+
+test_expect_success ' context includes end' '
+ grep "^ .*End of hello" changed_hello_appended.diff &&
+ grep "^[+].*End of first part" changed_hello_appended.diff
+'
+
+test_expect_success ' context does not include other functions' '
+ test $(grep -c "^[ +-].*Begin" changed_hello_appended.diff) -le 2
+'
+
+test_done
diff --git a/t/t4051/appended1.c b/t/t4051/appended1.c
new file mode 100644
index 0000000..a9f56f1
--- /dev/null
+++ b/t/t4051/appended1.c
@@ -0,0 +1,15 @@
+
+int appended(void) // Begin of first part
+{
+ int i;
+ char *s = "a string";
+
+ printf("%s\n", s);
+
+ for (i = 99;
+ i >= 0;
+ i--) {
+ printf("%d bottles of beer on the wall\n", i);
+ }
+
+ printf("End of first part\n");
diff --git a/t/t4051/appended2.c b/t/t4051/appended2.c
new file mode 100644
index 0000000..e651f71
--- /dev/null
+++ b/t/t4051/appended2.c
@@ -0,0 +1,35 @@
+ printf("Begin of second part\n");
+
+ /*
+ * 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, 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, 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, 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.
+ *
+ */
+
+ return 0;
+} // End of second part
diff --git a/t/t4051/dummy.c b/t/t4051/dummy.c
new file mode 100644
index 0000000..a43016e
--- /dev/null
+++ b/t/t4051/dummy.c
@@ -0,0 +1,7 @@
+
+static int dummy(void) // Begin of dummy
+{
+ int rc = 0;
+
+ return rc;
+} // End of dummy
diff --git a/t/t4051/hello.c b/t/t4051/hello.c
new file mode 100644
index 0000000..63b1a1e
--- /dev/null
+++ b/t/t4051/hello.c
@@ -0,0 +1,21 @@
+
+static void hello(void) // Begin of hello
+{
+ /*
+ * Classic.
+ */
+ putchar('H');
+ putchar('e');
+ putchar('l');
+ putchar('l');
+ putchar('o');
+ putchar(' ');
+ /* delete me from hello */
+ putchar('w');
+ putchar('o');
+ putchar('r');
+ putchar('l');
+ putchar('d');
+ putchar('.');
+ putchar('\n');
+} // End of hello
diff --git a/t/t4051/includes.c b/t/t4051/includes.c
new file mode 100644
index 0000000..efc68f8
--- /dev/null
+++ b/t/t4051/includes.c
@@ -0,0 +1,20 @@
+#include <Begin.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdarg.h>
+/* delete me from includes */
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <time.h>
+#include <signal.h>
+#include <assert.h>
+#include <regex.h>
+#include <utime.h>
+#include <syslog.h>
+#include <End.h>
diff --git a/t/t4052-stat-output.sh b/t/t4052-stat-output.sh
new file mode 100755
index 0000000..9f563db
--- /dev/null
+++ b/t/t4052-stat-output.sh
@@ -0,0 +1,336 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Zbigniew Jędrzejewski-Szmek
+#
+
+test_description='test --stat output of various commands'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
+
+# 120 character name
+name=aaaaaaaaaa
+name=$name$name$name$name$name$name$name$name$name$name$name$name
+test_expect_success 'preparation' '
+ >"$name" &&
+ git add "$name" &&
+ git commit -m message &&
+ echo a >"$name" &&
+ git commit -m message "$name"
+'
+
+while read cmd args
+do
+ cat >expect <<-'EOF'
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
+ EOF
+ test_expect_success "$cmd: small change with long name gives more space to the name" '
+ git $cmd $args >output &&
+ grep " | " output >actual &&
+ test_cmp expect actual
+ '
+
+ cat >expect <<-'EOF'
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
+ EOF
+ test_expect_success "$cmd --stat=width: a long name is given more room when the bar is short" '
+ git $cmd $args --stat=40 >output &&
+ grep " | " output >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "$cmd --stat-width=width with long name" '
+ git $cmd $args --stat-width=40 >output &&
+ grep " | " output >actual &&
+ test_cmp expect actual
+ '
+
+ cat >expect <<-'EOF'
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
+ EOF
+ test_expect_success "$cmd --stat=...,name-width with long name" '
+ git $cmd $args --stat=60,30 >output &&
+ grep " | " output >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "$cmd --stat-name-width with long name" '
+ git $cmd $args --stat-name-width=30 >output &&
+ grep " | " output >actual &&
+ test_cmp expect actual
+ '
+done <<\EOF
+format-patch -1 --stdout
+diff HEAD^ HEAD --stat
+show --stat
+log -1 --stat
+EOF
+
+
+test_expect_success 'preparation for big change tests' '
+ >abcd &&
+ git add abcd &&
+ git commit -m message &&
+ i=0 &&
+ while test $i -lt 1000
+ do
+ echo $i && i=$(($i + 1))
+ done >abcd &&
+ git commit -m message abcd
+'
+
+cat >expect80 <<'EOF'
+ abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+cat >expect80-graph <<'EOF'
+| abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+cat >expect200 <<'EOF'
+ abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+cat >expect200-graph <<'EOF'
+| abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+while read verb expect cmd args
+do
+ test_expect_success "$cmd $verb COLUMNS (big change)" '
+ COLUMNS=200 git $cmd $args >output &&
+ grep " | " output >actual &&
+ test_cmp "$expect" actual
+ '
+
+ case "$cmd" in diff|show) continue;; esac
+
+ test_expect_success "$cmd --graph $verb COLUMNS (big change)" '
+ COLUMNS=200 git $cmd $args --graph >output &&
+ grep " | " output >actual &&
+ test_cmp "$expect-graph" actual
+ '
+done <<\EOF
+ignores expect80 format-patch -1 --stdout
+respects expect200 diff HEAD^ HEAD --stat
+respects expect200 show --stat
+respects expect200 log -1 --stat
+EOF
+
+cat >expect40 <<'EOF'
+ abcd | 1000 ++++++++++++++++++++++++++
+EOF
+cat >expect40-graph <<'EOF'
+| abcd | 1000 ++++++++++++++++++++++++
+EOF
+while read verb expect cmd args
+do
+ test_expect_success "$cmd $verb not enough COLUMNS (big change)" '
+ COLUMNS=40 git $cmd $args >output &&
+ grep " | " output >actual &&
+ test_cmp "$expect" actual
+ '
+
+ case "$cmd" in diff|show) continue;; esac
+
+ test_expect_success "$cmd --graph $verb not enough COLUMNS (big change)" '
+ COLUMNS=40 git $cmd $args --graph >output &&
+ grep " | " output >actual &&
+ test_cmp "$expect-graph" actual
+ '
+done <<\EOF
+ignores expect80 format-patch -1 --stdout
+respects expect40 diff HEAD^ HEAD --stat
+respects expect40 show --stat
+respects expect40 log -1 --stat
+EOF
+
+cat >expect40 <<'EOF'
+ abcd | 1000 ++++++++++++++++++++++++++
+EOF
+cat >expect40-graph <<'EOF'
+| abcd | 1000 ++++++++++++++++++++++++++
+EOF
+while read verb expect cmd args
+do
+ test_expect_success "$cmd $verb statGraphWidth config" '
+ git -c diff.statGraphWidth=26 $cmd $args >output &&
+ grep " | " output >actual &&
+ test_cmp "$expect" actual
+ '
+
+ case "$cmd" in diff|show) continue;; esac
+
+ test_expect_success "$cmd --graph $verb statGraphWidth config" '
+ git -c diff.statGraphWidth=26 $cmd $args --graph >output &&
+ grep " | " output >actual &&
+ test_cmp "$expect-graph" actual
+ '
+done <<\EOF
+ignores expect80 format-patch -1 --stdout
+respects expect40 diff HEAD^ HEAD --stat
+respects expect40 show --stat
+respects expect40 log -1 --stat
+EOF
+
+
+cat >expect <<'EOF'
+ abcd | 1000 ++++++++++++++++++++++++++
+EOF
+cat >expect-graph <<'EOF'
+| abcd | 1000 ++++++++++++++++++++++++++
+EOF
+while read cmd args
+do
+ test_expect_success "$cmd --stat=width with big change" '
+ git $cmd $args --stat=40 >output &&
+ grep " | " output >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "$cmd --stat-width=width with big change" '
+ git $cmd $args --stat-width=40 >output &&
+ grep " | " output >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "$cmd --stat-graph-width with big change" '
+ git $cmd $args --stat-graph-width=26 >output &&
+ grep " | " output >actual &&
+ test_cmp expect actual
+ '
+
+ case "$cmd" in diff|show) continue;; esac
+
+ test_expect_success "$cmd --stat-width=width --graph with big change" '
+ git $cmd $args --stat-width=40 --graph >output &&
+ grep " | " output >actual &&
+ test_cmp expect-graph actual
+ '
+
+ test_expect_success "$cmd --stat-graph-width --graph with big change" '
+ git $cmd $args --stat-graph-width=26 --graph >output &&
+ grep " | " output >actual &&
+ test_cmp expect-graph actual
+ '
+done <<\EOF
+format-patch -1 --stdout
+diff HEAD^ HEAD --stat
+show --stat
+log -1 --stat
+EOF
+
+test_expect_success 'preparation for long filename tests' '
+ cp abcd aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
+ git add aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
+ git commit -m message
+'
+
+cat >expect <<'EOF'
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++
+EOF
+cat >expect-graph <<'EOF'
+| ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++
+EOF
+while read cmd args
+do
+ test_expect_success "$cmd --stat=width with big change is more balanced" '
+ git $cmd $args --stat-width=60 >output &&
+ grep " | " output >actual &&
+ test_cmp expect actual
+ '
+
+ case "$cmd" in diff|show) continue;; esac
+
+ test_expect_success "$cmd --stat=width --graph with big change is balanced" '
+ git $cmd $args --stat-width=60 --graph >output &&
+ grep " | " output >actual &&
+ test_cmp expect-graph actual
+ '
+done <<\EOF
+format-patch -1 --stdout
+diff HEAD^ HEAD --stat
+show --stat
+log -1 --stat
+EOF
+
+cat >expect80 <<'EOF'
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++
+EOF
+cat >expect80-graph <<'EOF'
+| ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++
+EOF
+cat >expect200 <<'EOF'
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+cat >expect200-graph <<'EOF'
+| aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+while read verb expect cmd args
+do
+ test_expect_success "$cmd $verb COLUMNS (long filename)" '
+ COLUMNS=200 git $cmd $args >output &&
+ grep " | " output >actual &&
+ test_cmp "$expect" actual
+ '
+
+ case "$cmd" in diff|show) continue;; esac
+
+ test_expect_success "$cmd --graph $verb COLUMNS (long filename)" '
+ COLUMNS=200 git $cmd $args --graph >output &&
+ grep " | " output >actual &&
+ test_cmp "$expect-graph" actual
+ '
+done <<\EOF
+ignores expect80 format-patch -1 --stdout
+respects expect200 diff HEAD^ HEAD --stat
+respects expect200 show --stat
+respects expect200 log -1 --stat
+EOF
+
+cat >expect1 <<'EOF'
+ ...aaaaaaa | 1000 ++++++
+EOF
+cat >expect1-graph <<'EOF'
+| ...aaaaaaa | 1000 ++++++
+EOF
+while read verb expect cmd args
+do
+ test_expect_success COLUMNS_CAN_BE_1 \
+ "$cmd $verb prefix greater than COLUMNS (big change)" '
+ COLUMNS=1 git $cmd $args >output &&
+ grep " | " output >actual &&
+ test_cmp "$expect" actual
+ '
+
+ case "$cmd" in diff|show) continue;; esac
+
+ test_expect_success COLUMNS_CAN_BE_1 \
+ "$cmd --graph $verb prefix greater than COLUMNS (big change)" '
+ COLUMNS=1 git $cmd $args --graph >output &&
+ grep " | " output >actual &&
+ test_cmp "$expect-graph" actual
+ '
+done <<\EOF
+ignores expect80 format-patch -1 --stdout
+respects expect1 diff HEAD^ HEAD --stat
+respects expect1 show --stat
+respects expect1 log -1 --stat
+EOF
+
+cat >expect <<'EOF'
+ abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+test_expect_success 'merge --stat respects COLUMNS (big change)' '
+ git checkout -b branch HEAD^^ &&
+ COLUMNS=100 git merge --stat --no-ff master^ >output &&
+ grep " | " output >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++
+EOF
+test_expect_success 'merge --stat respects COLUMNS (long filename)' '
+ COLUMNS=100 git merge --stat --no-ff master >output &&
+ grep " | " output >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh
new file mode 100755
index 0000000..6eb8321
--- /dev/null
+++ b/t/t4053-diff-no-index.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='diff --no-index'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ mkdir a &&
+ mkdir b &&
+ echo 1 >a/1 &&
+ echo 2 >a/2 &&
+ git init repo &&
+ echo 1 >repo/a &&
+ mkdir -p non/git &&
+ echo 1 >non/git/a &&
+ echo 1 >non/git/b
+'
+
+test_expect_success 'git diff --no-index directories' '
+ test_expect_code 1 git diff --no-index a b >cnt &&
+ test_line_count = 14 cnt
+'
+
+test_expect_success 'git diff --no-index relative path outside repo' '
+ (
+ cd repo &&
+ test_expect_code 0 git diff --no-index a ../non/git/a &&
+ test_expect_code 0 git diff --no-index ../non/git/a ../non/git/b
+ )
+'
+
+test_expect_success 'git diff --no-index with broken index' '
+ (
+ cd repo &&
+ echo broken >.git/index &&
+ git diff --no-index a ../non/git/a
+ )
+'
+
+test_expect_success 'git diff outside repo with broken index' '
+ (
+ cd repo &&
+ git diff ../non/git/a ../non/git/b
+ )
+'
+
+test_expect_success 'git diff --no-index executed outside repo gives correct error message' '
+ (
+ GIT_CEILING_DIRECTORIES=$TRASH_DIRECTORY/non &&
+ export GIT_CEILING_DIRECTORIES &&
+ cd non/git &&
+ test_must_fail git diff --no-index a 2>actual.err &&
+ echo "usage: git diff --no-index <path> <path>" >expect.err &&
+ test_cmp expect.err actual.err
+ )
+'
+
+test_expect_success 'diff D F and diff F D' '
+ (
+ cd repo &&
+ echo in-repo >a &&
+ echo non-repo >../non/git/a &&
+ mkdir sub &&
+ echo sub-repo >sub/a &&
+
+ test_must_fail git diff --no-index sub/a ../non/git/a >expect &&
+ test_must_fail git diff --no-index sub/a ../non/git/ >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git diff --no-index a ../non/git/a >expect &&
+ test_must_fail git diff --no-index a ../non/git/ >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git diff --no-index ../non/git/a a >expect &&
+ test_must_fail git diff --no-index ../non/git a >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'turning a file into a directory' '
+ (
+ cd non/git &&
+ mkdir d e e/sub &&
+ echo 1 >d/sub &&
+ echo 2 >e/sub/file &&
+ printf "D\td/sub\nA\te/sub/file\n" >expect &&
+ test_must_fail git diff --no-index --name-status d e >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_done
diff --git a/t/t4054-diff-bogus-tree.sh b/t/t4054-diff-bogus-tree.sh
new file mode 100755
index 0000000..1d6efab
--- /dev/null
+++ b/t/t4054-diff-bogus-tree.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+test_description='test diff with a bogus tree containing the null sha1'
+. ./test-lib.sh
+
+empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+
+test_expect_success 'create bogus tree' '
+ bogus_tree=$(
+ printf "100644 fooQQQQQQQQQQQQQQQQQQQQQ" |
+ q_to_nul |
+ git hash-object -w --stdin -t tree
+ )
+'
+
+test_expect_success 'create tree with matching file' '
+ echo bar >foo &&
+ git add foo &&
+ good_tree=$(git write-tree) &&
+ blob=$(git rev-parse :foo)
+'
+
+test_expect_success 'raw diff shows null sha1 (addition)' '
+ echo ":000000 100644 $_z40 $_z40 A foo" >expect &&
+ git diff-tree $empty_tree $bogus_tree >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'raw diff shows null sha1 (removal)' '
+ echo ":100644 000000 $_z40 $_z40 D foo" >expect &&
+ git diff-tree $bogus_tree $empty_tree >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'raw diff shows null sha1 (modification)' '
+ echo ":100644 100644 $blob $_z40 M foo" >expect &&
+ git diff-tree $good_tree $bogus_tree >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'raw diff shows null sha1 (other direction)' '
+ echo ":100644 100644 $_z40 $blob M foo" >expect &&
+ git diff-tree $bogus_tree $good_tree >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'raw diff shows null sha1 (reverse)' '
+ echo ":100644 100644 $_z40 $blob M foo" >expect &&
+ git diff-tree -R $good_tree $bogus_tree >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'raw diff shows null sha1 (index)' '
+ echo ":100644 100644 $_z40 $blob M foo" >expect &&
+ git diff-index $bogus_tree >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'patch fails due to bogus sha1 (addition)' '
+ test_must_fail git diff-tree -p $empty_tree $bogus_tree
+'
+
+test_expect_success 'patch fails due to bogus sha1 (removal)' '
+ test_must_fail git diff-tree -p $bogus_tree $empty_tree
+'
+
+test_expect_success 'patch fails due to bogus sha1 (modification)' '
+ test_must_fail git diff-tree -p $good_tree $bogus_tree
+'
+
+test_expect_success 'patch fails due to bogus sha1 (other direction)' '
+ test_must_fail git diff-tree -p $bogus_tree $good_tree
+'
+
+test_expect_success 'patch fails due to bogus sha1 (reverse)' '
+ test_must_fail git diff-tree -R -p $good_tree $bogus_tree
+'
+
+test_expect_success 'patch fails due to bogus sha1 (index)' '
+ test_must_fail git diff-index -p $bogus_tree
+'
+
+test_done
diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
new file mode 100755
index 0000000..741e080
--- /dev/null
+++ b/t/t4055-diff-context.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Mozilla Foundation
+#
+
+test_description='diff.context configuration'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ cat >template <<-\EOF &&
+ firstline
+ b
+ c
+ d
+ e
+ f
+ preline
+ TARGET
+ postline
+ i
+ j
+ k
+ l
+ m
+ n
+ EOF
+ sed "/TARGET/d" >x <template &&
+ git update-index --add x &&
+ git commit -m initial &&
+
+ sed "s/TARGET/ADDED/" >x <template &&
+ git update-index --add x &&
+ git commit -m next &&
+
+ sed "s/TARGET/MODIFIED/" >x <template
+'
+
+test_expect_success 'the default number of context lines is 3' '
+ git diff >output &&
+ ! grep "^ d" output &&
+ grep "^ e" output &&
+ grep "^ j" output &&
+ ! grep "^ k" output
+'
+
+test_expect_success 'diff.context honored by "log"' '
+ git log -1 -p >output &&
+ ! grep firstline output &&
+ git config diff.context 8 &&
+ git log -1 -p >output &&
+ grep "^ firstline" output
+'
+
+test_expect_success 'The -U option overrides diff.context' '
+ git config diff.context 8 &&
+ git log -U4 -1 >output &&
+ ! grep "^ firstline" output
+'
+
+test_expect_success 'diff.context honored by "diff"' '
+ git config diff.context 8 &&
+ git diff >output &&
+ grep "^ firstline" output
+'
+
+test_expect_success 'plumbing not affected' '
+ git config diff.context 8 &&
+ git diff-files -p >output &&
+ ! grep "^ firstline" output
+'
+
+test_expect_success 'non-integer config parsing' '
+ git config diff.context no &&
+ test_must_fail git diff 2>output &&
+ test_i18ngrep "bad numeric config value" output
+'
+
+test_expect_success 'negative integer config parsing' '
+ git config diff.context -1 &&
+ test_must_fail git diff 2>output &&
+ test_i18ngrep "bad config variable" output
+'
+
+test_expect_success '-U0 is valid, so is diff.context=0' '
+ git config diff.context 0 &&
+ git diff >output &&
+ grep "^-ADDED" output &&
+ grep "^+MODIFIED" output
+'
+
+test_done
diff --git a/t/t4056-diff-order.sh b/t/t4056-diff-order.sh
new file mode 100755
index 0000000..43dd474
--- /dev/null
+++ b/t/t4056-diff-order.sh
@@ -0,0 +1,127 @@
+#!/bin/sh
+
+test_description='diff order'
+
+. ./test-lib.sh
+
+create_files () {
+ echo "$1" >a.h &&
+ echo "$1" >b.c &&
+ echo "$1" >c/Makefile &&
+ echo "$1" >d.txt &&
+ git add a.h b.c c/Makefile d.txt &&
+ git commit -m"$1"
+}
+
+test_expect_success 'setup' '
+ mkdir c &&
+ create_files 1 &&
+ create_files 2 &&
+
+ cat >order_file_1 <<-\EOF &&
+ *Makefile
+ *.txt
+ *.h
+ EOF
+
+ cat >order_file_2 <<-\EOF &&
+ *Makefile
+ *.h
+ *.c
+ EOF
+
+ cat >expect_none <<-\EOF &&
+ a.h
+ b.c
+ c/Makefile
+ d.txt
+ EOF
+
+ cat >expect_1 <<-\EOF &&
+ c/Makefile
+ d.txt
+ a.h
+ b.c
+ EOF
+
+ cat >expect_2 <<-\EOF
+ c/Makefile
+ a.h
+ b.c
+ d.txt
+ EOF
+'
+
+test_expect_success "no order (=tree object order)" '
+ git diff --name-only HEAD^..HEAD >actual &&
+ test_cmp expect_none actual
+'
+
+test_expect_success 'missing orderfile' '
+ rm -f bogus_file &&
+ test_must_fail git diff -Obogus_file --name-only HEAD^..HEAD
+'
+
+test_expect_success POSIXPERM,SANITY 'unreadable orderfile' '
+ >unreadable_file &&
+ chmod -r unreadable_file &&
+ test_must_fail git diff -Ounreadable_file --name-only HEAD^..HEAD
+'
+
+test_expect_success "orderfile using option from subdir with --output" '
+ mkdir subdir &&
+ git -C subdir diff -O../order_file_1 --output ../actual --name-only HEAD^..HEAD &&
+ test_cmp expect_1 actual
+'
+
+for i in 1 2
+do
+ test_expect_success "orderfile using option ($i)" '
+ git diff -Oorder_file_$i --name-only HEAD^..HEAD >actual &&
+ test_cmp expect_$i actual
+ '
+
+ test_expect_success PIPE "orderfile is fifo ($i)" '
+ rm -f order_fifo &&
+ mkfifo order_fifo &&
+ {
+ cat order_file_$i >order_fifo &
+ } &&
+ git diff -O order_fifo --name-only HEAD^..HEAD >actual &&
+ wait &&
+ test_cmp expect_$i actual
+ '
+
+ test_expect_success "orderfile using config ($i)" '
+ git -c diff.orderfile=order_file_$i diff --name-only HEAD^..HEAD >actual &&
+ test_cmp expect_$i actual
+ '
+
+ test_expect_success "cancelling configured orderfile ($i)" '
+ git -c diff.orderfile=order_file_$i diff -O/dev/null --name-only HEAD^..HEAD >actual &&
+ test_cmp expect_none actual
+ '
+done
+
+test_expect_success 'setup for testing combine-diff order' '
+ git checkout -b tmp HEAD~ &&
+ create_files 3 &&
+ git checkout master &&
+ git merge --no-commit -s ours tmp &&
+ create_files 5
+'
+
+test_expect_success "combine-diff: no order (=tree object order)" '
+ git diff --name-only HEAD HEAD^ HEAD^2 >actual &&
+ test_cmp expect_none actual
+'
+
+for i in 1 2
+do
+ test_expect_success "combine-diff: orderfile using option ($i)" '
+ git diff -Oorder_file_$i --name-only HEAD HEAD^ HEAD^2 >actual &&
+ test_cmp expect_$i actual
+ '
+done
+
+test_done
diff --git a/t/t4057-diff-combined-paths.sh b/t/t4057-diff-combined-paths.sh
new file mode 100755
index 0000000..dff36b7
--- /dev/null
+++ b/t/t4057-diff-combined-paths.sh
@@ -0,0 +1,106 @@
+#!/bin/sh
+
+test_description='combined diff show only paths that are different to all parents'
+
+. ./test-lib.sh
+
+# verify that diffc.expect matches output of
+# $(git diff -c --name-only HEAD HEAD^ HEAD^2)
+diffc_verify () {
+ git diff -c --name-only HEAD HEAD^ HEAD^2 >diffc.actual &&
+ test_cmp diffc.expect diffc.actual
+}
+
+test_expect_success 'trivial merge - combine-diff empty' '
+ for i in $(test_seq 1 9)
+ do
+ echo $i >$i.txt &&
+ git add $i.txt
+ done &&
+ git commit -m "init" &&
+ git checkout -b side &&
+ for i in $(test_seq 2 9)
+ do
+ echo $i/2 >>$i.txt
+ done &&
+ git commit -a -m "side 2-9" &&
+ git checkout master &&
+ echo 1/2 >1.txt &&
+ git commit -a -m "master 1" &&
+ git merge side &&
+ >diffc.expect &&
+ diffc_verify
+'
+
+
+test_expect_success 'only one trully conflicting path' '
+ git checkout side &&
+ for i in $(test_seq 2 9)
+ do
+ echo $i/3 >>$i.txt
+ done &&
+ echo "4side" >>4.txt &&
+ git commit -a -m "side 2-9 +4" &&
+ git checkout master &&
+ for i in $(test_seq 1 9)
+ do
+ echo $i/3 >>$i.txt
+ done &&
+ echo "4master" >>4.txt &&
+ git commit -a -m "master 1-9 +4" &&
+ test_must_fail git merge side &&
+ cat <<-\EOF >4.txt &&
+ 4
+ 4/2
+ 4/3
+ 4master
+ 4side
+ EOF
+ git add 4.txt &&
+ git commit -m "merge side (2)" &&
+ echo 4.txt >diffc.expect &&
+ diffc_verify
+'
+
+test_expect_success 'merge introduces new file' '
+ git checkout side &&
+ for i in $(test_seq 5 9)
+ do
+ echo $i/4 >>$i.txt
+ done &&
+ git commit -a -m "side 5-9" &&
+ git checkout master &&
+ for i in $(test_seq 1 3)
+ do
+ echo $i/4 >>$i.txt
+ done &&
+ git commit -a -m "master 1-3 +4hello" &&
+ git merge side &&
+ echo "Hello World" >4hello.txt &&
+ git add 4hello.txt &&
+ git commit --amend &&
+ echo 4hello.txt >diffc.expect &&
+ diffc_verify
+'
+
+test_expect_success 'merge removed a file' '
+ git checkout side &&
+ for i in $(test_seq 5 9)
+ do
+ echo $i/5 >>$i.txt
+ done &&
+ git commit -a -m "side 5-9" &&
+ git checkout master &&
+ for i in $(test_seq 1 3)
+ do
+ echo $i/4 >>$i.txt
+ done &&
+ git commit -a -m "master 1-3" &&
+ git merge side &&
+ git rm 4.txt &&
+ git commit --amend &&
+ echo 4.txt >diffc.expect &&
+ diffc_verify
+'
+
+test_done
diff --git a/t/t4058-diff-duplicates.sh b/t/t4058-diff-duplicates.sh
new file mode 100755
index 0000000..0a23242
--- /dev/null
+++ b/t/t4058-diff-duplicates.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+test_description='test tree diff when trees have duplicate entries'
+. ./test-lib.sh
+
+# make_tree_entry <mode> <mode> <sha1>
+#
+# We have to rely on perl here because not all printfs understand
+# hex escapes (only octal), and xxd is not portable.
+make_tree_entry () {
+ printf '%s %s\0' "$1" "$2" &&
+ perl -e 'print chr(hex($_)) for ($ARGV[0] =~ /../g)' "$3"
+}
+
+# Like git-mktree, but without all of the pesky sanity checking.
+# Arguments come in groups of three, each group specifying a single
+# tree entry (see make_tree_entry above).
+make_tree () {
+ while test $# -gt 2; do
+ make_tree_entry "$1" "$2" "$3"
+ shift; shift; shift
+ done |
+ git hash-object -w -t tree --stdin
+}
+
+# this is kind of a convoluted setup, but matches
+# a real-world case. Each tree contains four entries
+# for the given path, one with one sha1, and three with
+# the other. The first tree has them split across
+# two subtrees (which are themselves duplicate entries in
+# the root tree), and the second has them all in a single subtree.
+test_expect_success 'create trees with duplicate entries' '
+ blob_one=$(echo one | git hash-object -w --stdin) &&
+ blob_two=$(echo two | git hash-object -w --stdin) &&
+ inner_one_a=$(make_tree \
+ 100644 inner $blob_one
+ ) &&
+ inner_one_b=$(make_tree \
+ 100644 inner $blob_two \
+ 100644 inner $blob_two \
+ 100644 inner $blob_two
+ ) &&
+ outer_one=$(make_tree \
+ 040000 outer $inner_one_a \
+ 040000 outer $inner_one_b
+ ) &&
+ inner_two=$(make_tree \
+ 100644 inner $blob_one \
+ 100644 inner $blob_two \
+ 100644 inner $blob_two \
+ 100644 inner $blob_two
+ ) &&
+ outer_two=$(make_tree \
+ 040000 outer $inner_two
+ ) &&
+ git tag one $outer_one &&
+ git tag two $outer_two
+'
+
+test_expect_success 'diff-tree between trees' '
+ {
+ printf ":000000 100644 $_z40 $blob_two A\touter/inner\n" &&
+ printf ":000000 100644 $_z40 $blob_two A\touter/inner\n" &&
+ printf ":000000 100644 $_z40 $blob_two A\touter/inner\n" &&
+ printf ":100644 000000 $blob_two $_z40 D\touter/inner\n" &&
+ printf ":100644 000000 $blob_two $_z40 D\touter/inner\n" &&
+ printf ":100644 000000 $blob_two $_z40 D\touter/inner\n"
+ } >expect &&
+ git diff-tree -r --no-abbrev one two >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'diff-tree with renames' '
+ # same expectation as above, since we disable rename detection
+ git diff-tree -M -r --no-abbrev one two >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh
index 9b433de..744b8e5 100755
--- a/t/t4100-apply-stat.sh
+++ b/t/t4100-apply-stat.sh
@@ -17,13 +17,13 @@ do
test_expect_success "$title" '
git apply --stat --summary \
<"$TEST_DIRECTORY/t4100/t-apply-$num.patch" >current &&
- test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
+ test_i18ncmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
'
test_expect_success "$title with recount" '
sed -e "$UNC" <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" |
git apply --recount --stat --summary >current &&
- test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
+ test_i18ncmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
'
done <<\EOF
rename
diff --git a/t/t4100/t-apply-8.expect b/t/t4100/t-apply-8.expect
index eef7f2e..55a55c3 100644
--- a/t/t4100/t-apply-8.expect
+++ b/t/t4100/t-apply-8.expect
@@ -1,2 +1,2 @@
t/t4100-apply-stat.sh | 2 +-
- 1 files changed, 1 insertions(+), 1 deletions(-)
+ 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/t/t4100/t-apply-9.expect b/t/t4100/t-apply-9.expect
index eef7f2e..55a55c3 100644
--- a/t/t4100/t-apply-9.expect
+++ b/t/t4100/t-apply-9.expect
@@ -1,2 +1,2 @@
t/t4100-apply-stat.sh | 2 +-
- 1 files changed, 1 insertions(+), 1 deletions(-)
+ 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/t/t4102-apply-rename.sh b/t/t4102-apply-rename.sh
index 1597965..fae3059 100755
--- a/t/t4102-apply-rename.sh
+++ b/t/t4102-apply-rename.sh
@@ -31,13 +31,6 @@ test_expect_success setup \
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......"'
@@ -59,6 +52,6 @@ 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 "$(cat bar)" = "This is bar" && test "$(cat foo)" = "This is foo"'
test_done
diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh
index 9692f16..1b420e3 100755
--- a/t/t4103-apply-binary.sh
+++ b/t/t4103-apply-binary.sh
@@ -8,44 +8,59 @@ test_description='git apply handling binary patches
'
. ./test-lib.sh
-# setup
-
-cat >file1 <<EOF
-A quick brown fox jumps over the lazy dog.
-A tiny little penguin runs around in circles.
-There is a flag with Linux written on it.
-A slow black-and-white panda just sits there,
-munching on his bamboo.
-EOF
-cat file1 >file2
-cat file1 >file4
-
-test_expect_success 'setup' "
+test_expect_success 'setup' '
+ cat >file1 <<-\EOF &&
+ A quick brown fox jumps over the lazy dog.
+ A tiny little penguin runs around in circles.
+ There is a flag with Linux written on it.
+ A slow black-and-white panda just sits there,
+ munching on his bamboo.
+ EOF
+ cat file1 >file2 &&
+ cat file1 >file4 &&
+
git update-index --add --remove file1 file2 file4 &&
- git commit -m 'Initial Version' 2>/dev/null &&
+ git commit -m "Initial Version" 2>/dev/null &&
git checkout -b binary &&
- perl -pe 'y/x/\000/' <file1 >file3 &&
+ perl -pe "y/x/\000/" <file1 >file3 &&
cat file3 >file4 &&
git add file2 &&
- perl -pe 'y/\000/v/' <file3 >file1 &&
+ perl -pe "y/\000/v/" <file3 >file1 &&
rm -f file2 &&
git update-index --add --remove file1 file2 file3 file4 &&
- git commit -m 'Second Version' &&
+ git commit -m "Second Version" &&
git diff-tree -p master binary >B.diff &&
git diff-tree -p -C master binary >C.diff &&
git diff-tree -p --binary master binary >BF.diff &&
- git diff-tree -p --binary -C master binary >CF.diff
-"
+ git diff-tree -p --binary -C master binary >CF.diff &&
+
+ git diff-tree -p --full-index master binary >B-index.diff &&
+ git diff-tree -p -C --full-index master binary >C-index.diff &&
+
+ git diff-tree -p --binary --no-prefix master binary -- file3 >B0.diff &&
+
+ git init other-repo &&
+ (
+ cd other-repo &&
+ git fetch .. master &&
+ git reset --hard FETCH_HEAD
+ )
+'
test_expect_success 'stat binary diff -- should not fail.' \
- 'git checkout master
+ 'git checkout master &&
git apply --stat --summary B.diff'
+test_expect_success 'stat binary -p0 diff -- should not fail.' '
+ git checkout master &&
+ git apply --stat -p0 B0.diff
+'
+
test_expect_success 'stat binary diff (copy) -- should not fail.' \
- 'git checkout master
+ 'git checkout master &&
git apply --stat --summary C.diff'
test_expect_success 'check binary diff -- should fail.' \
@@ -69,11 +84,11 @@ test_expect_success \
'
test_expect_success 'check binary diff with replacement.' \
- 'git checkout master
+ 'git checkout master &&
git apply --check --allow-binary-replacement BF.diff'
test_expect_success 'check binary diff with replacement (copy).' \
- 'git checkout master
+ 'git checkout master &&
git apply --check --allow-binary-replacement CF.diff'
# Now we start applying them.
@@ -100,6 +115,22 @@ 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 with full-index' '
+ do_reset &&
+ git apply B-index.diff
+'
+
+test_expect_success 'apply binary diff with full-index (copy)' '
+ do_reset &&
+ git apply C-index.diff
+'
+
+test_expect_success 'apply full-index binary diff in new repo' '
+ (cd other-repo &&
+ do_reset &&
+ test_must_fail git apply ../B-index.diff)
+'
+
test_expect_success 'apply binary diff without replacement.' \
'do_reset &&
git apply BF.diff'
@@ -118,4 +149,10 @@ test_expect_success 'apply binary diff (copy).' \
git apply --allow-binary-replacement --index CF.diff &&
test -z "$(git diff --name-status binary)"'
+test_expect_success 'apply binary -p0 diff' '
+ do_reset &&
+ git apply -p0 --index B0.diff &&
+ test -z "$(git diff --name-status binary -- file3)"
+'
+
test_done
diff --git a/t/t4104-apply-boundary.sh b/t/t4104-apply-boundary.sh
index c617c2a..32e3b0e 100755
--- a/t/t4104-apply-boundary.sh
+++ b/t/t4104-apply-boundary.sh
@@ -18,7 +18,7 @@ test_expect_success setup '
cat victim >original &&
git update-index --add victim &&
- : add to the head
+ # add to the head
for i in a b '"$L"' y
do
echo $i
@@ -27,7 +27,7 @@ test_expect_success setup '
git diff victim >add-a-patch.with &&
git diff --unified=0 >add-a-patch.without &&
- : insert at line two
+ # insert at line two
for i in b a '"$L"' y
do
echo $i
@@ -36,7 +36,7 @@ test_expect_success setup '
git diff victim >insert-a-patch.with &&
git diff --unified=0 >insert-a-patch.without &&
- : modify at the head
+ # modify at the head
for i in a '"$L"' y
do
echo $i
@@ -45,16 +45,16 @@ test_expect_success setup '
git diff victim >mod-a-patch.with &&
git diff --unified=0 >mod-a-patch.without &&
- : remove from the head
+ # 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 victim >del-a-patch.with &&
git diff --unified=0 >del-a-patch.without &&
- : add to the tail
+ # add to the tail
for i in b '"$L"' y z
do
echo $i
@@ -63,7 +63,7 @@ test_expect_success setup '
git diff victim >add-z-patch.with &&
git diff --unified=0 >add-z-patch.without &&
- : modify at the tail
+ # modify at the tail
for i in b '"$L"' z
do
echo $i
@@ -72,16 +72,16 @@ test_expect_success setup '
git diff victim >mod-z-patch.with &&
git diff --unified=0 >mod-z-patch.without &&
- : remove from the tail
+ # 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 &&
+ git diff victim >del-z-patch.with &&
+ git diff --unified=0 >del-z-patch.without
- : done
+ # done
'
for with in with without
@@ -95,10 +95,7 @@ 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)
- } &&
+ git apply --index '"$u$kind-patch.$with"' &&
test_cmp '"$kind"'-expect victim
'
done
@@ -113,10 +110,7 @@ do
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)
- } &&
+ git apply --unidiff-zero --index '"$kind-ng.without"' &&
test_cmp '"$kind"'-expect victim
'
done
diff --git a/t/t4107-apply-ignore-whitespace.sh b/t/t4107-apply-ignore-whitespace.sh
index b04fc8f..9e29b52 100755
--- a/t/t4107-apply-ignore-whitespace.sh
+++ b/t/t4107-apply-ignore-whitespace.sh
@@ -111,7 +111,6 @@ 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() {
@@ -154,7 +153,8 @@ test_expect_success 'patch2 reverse applies with --ignore-space-change' '
git config apply.ignorewhitespace change
test_expect_success 'patch2 applies (apply.ignorewhitespace = change)' '
- git apply patch2.patch
+ git apply patch2.patch &&
+ test_cmp main.c.final main.c
'
test_expect_success 'patch3 fails (missing string at EOL)' '
@@ -165,12 +165,8 @@ 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 'patch5 fails (leading whitespace differences matter)' '
+ test_must_fail git apply patch5.patch
'
test_expect_success 're-create file (with --ignore-whitespace)' '
diff --git a/t/t4108-apply-threeway.sh b/t/t4108-apply-threeway.sh
new file mode 100755
index 0000000..fa5d4ef
--- /dev/null
+++ b/t/t4108-apply-threeway.sh
@@ -0,0 +1,157 @@
+#!/bin/sh
+
+test_description='git apply --3way'
+
+. ./test-lib.sh
+
+create_file () {
+ for i
+ do
+ echo "$i"
+ done
+}
+
+sanitize_conflicted_diff () {
+ sed -e '
+ /^index /d
+ s/^\(+[<>][<>][<>][<>]*\) .*/\1/
+ '
+}
+
+test_expect_success setup '
+ test_tick &&
+ create_file >one 1 2 3 4 5 6 7 &&
+ cat one >two &&
+ git add one two &&
+ git commit -m initial &&
+
+ git branch side &&
+
+ test_tick &&
+ create_file >one 1 two 3 4 5 six 7 &&
+ create_file >two 1 two 3 4 5 6 7 &&
+ git commit -a -m master &&
+
+ git checkout side &&
+ create_file >one 1 2 3 4 five 6 7 &&
+ create_file >two 1 2 3 4 five 6 7 &&
+ git commit -a -m side &&
+
+ git checkout master
+'
+
+test_expect_success 'apply without --3way' '
+ git diff side^ side >P.diff &&
+
+ # should fail to apply
+ git reset --hard &&
+ git checkout master^0 &&
+ test_must_fail git apply --index P.diff &&
+ # should leave things intact
+ git diff-files --exit-code &&
+ git diff-index --exit-code --cached HEAD
+'
+
+test_expect_success 'apply with --3way' '
+ # Merging side should be similar to applying this patch
+ git diff ...side >P.diff &&
+
+ # The corresponding conflicted merge
+ git reset --hard &&
+ git checkout master^0 &&
+ test_must_fail git merge --no-commit side &&
+ git ls-files -s >expect.ls &&
+ git diff HEAD | sanitize_conflicted_diff >expect.diff &&
+
+ # should fail to apply
+ git reset --hard &&
+ git checkout master^0 &&
+ test_must_fail git apply --index --3way P.diff &&
+ git ls-files -s >actual.ls &&
+ git diff HEAD | sanitize_conflicted_diff >actual.diff &&
+
+ # The result should resemble the corresponding merge
+ test_cmp expect.ls actual.ls &&
+ test_cmp expect.diff actual.diff
+'
+
+test_expect_success 'apply with --3way with rerere enabled' '
+ git config rerere.enabled true &&
+
+ # Merging side should be similar to applying this patch
+ git diff ...side >P.diff &&
+
+ # The corresponding conflicted merge
+ git reset --hard &&
+ git checkout master^0 &&
+ test_must_fail git merge --no-commit side &&
+
+ # Manually resolve and record the resolution
+ create_file 1 two 3 4 five six 7 >one &&
+ git rerere &&
+ cat one >expect &&
+
+ # should fail to apply
+ git reset --hard &&
+ git checkout master^0 &&
+ test_must_fail git apply --index --3way P.diff &&
+
+ # but rerere should have replayed the recorded resolution
+ test_cmp expect one
+'
+
+test_expect_success 'apply -3 with add/add conflict setup' '
+ git reset --hard &&
+
+ git checkout -b adder &&
+ create_file 1 2 3 4 5 6 7 >three &&
+ create_file 1 2 3 4 5 6 7 >four &&
+ git add three four &&
+ git commit -m "add three and four" &&
+
+ git checkout -b another adder^ &&
+ create_file 1 2 3 4 5 6 7 >three &&
+ create_file 1 2 3 four 5 6 7 >four &&
+ git add three four &&
+ git commit -m "add three and four" &&
+
+ # Merging another should be similar to applying this patch
+ git diff adder...another >P.diff &&
+
+ git checkout adder^0 &&
+ test_must_fail git merge --no-commit another &&
+ git ls-files -s >expect.ls &&
+ git diff HEAD | sanitize_conflicted_diff >expect.diff
+'
+
+test_expect_success 'apply -3 with add/add conflict' '
+ # should fail to apply ...
+ git reset --hard &&
+ git checkout adder^0 &&
+ test_must_fail git apply --index --3way P.diff &&
+ # ... and leave conflicts in the index and in the working tree
+ git ls-files -s >actual.ls &&
+ git diff HEAD | sanitize_conflicted_diff >actual.diff &&
+
+ # The result should resemble the corresponding merge
+ test_cmp expect.ls actual.ls &&
+ test_cmp expect.diff actual.diff
+'
+
+test_expect_success 'apply -3 with add/add conflict (dirty working tree)' '
+ # should fail to apply ...
+ git reset --hard &&
+ git checkout adder^0 &&
+ echo >>four &&
+ cat four >four.save &&
+ cat three >three.save &&
+ git ls-files -s >expect.ls &&
+ test_must_fail git apply --index --3way P.diff &&
+ # ... and should not touch anything
+ git ls-files -s >actual.ls &&
+ test_cmp expect.ls actual.ls &&
+ test_cmp four.save four &&
+ test_cmp three.save three
+'
+
+test_done
diff --git a/t/t4111-apply-subdir.sh b/t/t4111-apply-subdir.sh
new file mode 100755
index 0000000..1618a6d
--- /dev/null
+++ b/t/t4111-apply-subdir.sh
@@ -0,0 +1,156 @@
+#!/bin/sh
+
+test_description='patching from inconvenient places'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ cat >patch <<-\EOF &&
+ diff file.orig file
+ --- a/file.orig
+ +++ b/file
+ @@ -1 +1,2 @@
+ 1
+ +2
+ EOF
+ patch="$(pwd)/patch" &&
+
+ echo 1 >preimage &&
+ printf "%s\n" 1 2 >postimage &&
+ echo 3 >other &&
+
+ test_tick &&
+ git commit --allow-empty -m basis
+'
+
+test_expect_success 'setup: subdir' '
+ reset_subdir() {
+ git reset &&
+ mkdir -p sub/dir/b &&
+ mkdir -p objects &&
+ cp "$1" file &&
+ cp "$1" objects/file &&
+ cp "$1" sub/dir/file &&
+ cp "$1" sub/dir/b/file &&
+ git add file sub/dir/file sub/dir/b/file objects/file &&
+ cp "$2" file &&
+ cp "$2" sub/dir/file &&
+ cp "$2" sub/dir/b/file &&
+ cp "$2" objects/file &&
+ test_might_fail git update-index --refresh -q
+ }
+'
+
+test_expect_success 'apply from subdir of toplevel' '
+ cp postimage expected &&
+ reset_subdir other preimage &&
+ (
+ cd sub/dir &&
+ git apply "$patch"
+ ) &&
+ test_cmp expected sub/dir/file
+'
+
+test_expect_success 'apply --cached from subdir of toplevel' '
+ cp postimage expected &&
+ cp other expected.working &&
+ reset_subdir preimage other &&
+ (
+ cd sub/dir &&
+ git apply --cached "$patch"
+ ) &&
+ git show :sub/dir/file >actual &&
+ test_cmp expected actual &&
+ test_cmp expected.working sub/dir/file
+'
+
+test_expect_success 'apply --index from subdir of toplevel' '
+ cp postimage expected &&
+ reset_subdir preimage other &&
+ (
+ cd sub/dir &&
+ test_must_fail git apply --index "$patch"
+ ) &&
+ reset_subdir other preimage &&
+ (
+ cd sub/dir &&
+ test_must_fail git apply --index "$patch"
+ ) &&
+ reset_subdir preimage preimage &&
+ (
+ cd sub/dir &&
+ git apply --index "$patch"
+ ) &&
+ git show :sub/dir/file >actual &&
+ test_cmp expected actual &&
+ test_cmp expected sub/dir/file
+'
+
+test_expect_success 'apply half-broken patch from subdir of toplevel' '
+ (
+ cd sub/dir &&
+ test_must_fail git apply <<-EOF
+ --- sub/dir/file
+ +++ sub/dir/file
+ @@ -1,0 +1,0 @@
+ --- file_in_root
+ +++ file_in_root
+ @@ -1,0 +1,0 @@
+ EOF
+ )
+'
+
+test_expect_success 'apply from .git dir' '
+ cp postimage expected &&
+ cp preimage .git/file &&
+ cp preimage .git/objects/file &&
+ (
+ cd .git &&
+ git apply "$patch"
+ ) &&
+ test_cmp expected .git/file
+'
+
+test_expect_success 'apply from subdir of .git dir' '
+ cp postimage expected &&
+ cp preimage .git/file &&
+ cp preimage .git/objects/file &&
+ (
+ cd .git/objects &&
+ git apply "$patch"
+ ) &&
+ test_cmp expected .git/objects/file
+'
+
+test_expect_success 'apply --cached from .git dir' '
+ cp postimage expected &&
+ cp other expected.working &&
+ cp other .git/file &&
+ reset_subdir preimage other &&
+ (
+ cd .git &&
+ git apply --cached "$patch"
+ ) &&
+ git show :file >actual &&
+ test_cmp expected actual &&
+ test_cmp expected.working file &&
+ test_cmp expected.working .git/file
+'
+
+test_expect_success 'apply --cached from subdir of .git dir' '
+ cp postimage expected &&
+ cp preimage expected.subdir &&
+ cp other .git/file &&
+ cp other .git/objects/file &&
+ reset_subdir preimage other &&
+ (
+ cd .git/objects &&
+ git apply --cached "$patch"
+ ) &&
+ git show :file >actual &&
+ git show :objects/file >actual.subdir &&
+ test_cmp expected actual &&
+ test_cmp expected.subdir actual.subdir
+'
+
+test_done
diff --git a/t/t4114-apply-typechange.sh b/t/t4114-apply-typechange.sh
index 164d58c..ebadbc3 100755
--- a/t/t4114-apply-typechange.sh
+++ b/t/t4114-apply-typechange.sh
@@ -9,12 +9,6 @@ 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 &&
@@ -22,13 +16,12 @@ test_expect_success 'setup repository and commits' '
git commit -m initial &&
git branch initial &&
rm -f foo &&
- ln -s bar foo &&
- git update-index foo &&
+ test_ln_s_add bar foo &&
git commit -m "foo symlinked to bar" &&
git branch foo-symlinked-to-bar &&
- rm -f foo &&
+ git rm -f foo &&
echo "how far is the sun?" > foo &&
- git update-index foo &&
+ git update-index --add foo &&
git commit -m "foo back to file" &&
git branch foo-back-to-file &&
printf "\0" > foo &&
diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh
index aff4348..872fcda 100755
--- a/t/t4115-apply-symlink.sh
+++ b/t/t4115-apply-symlink.sh
@@ -9,24 +9,16 @@ 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? &&
+ test_ln_s_add path1/path2/path3/path4/path5 link1 &&
git commit -m initial &&
git branch side &&
rm -f link? &&
- ln -s htap6 link1 &&
- git update-index link? &&
+ test_ln_s_add htap6 link1 &&
git commit -m second &&
git diff-tree -p HEAD^ HEAD >patch &&
@@ -34,7 +26,7 @@ test_expect_success setup '
'
-test_expect_success 'apply symlink patch' '
+test_expect_success SYMLINKS 'apply symlink patch' '
git checkout side &&
git apply patch &&
diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh
index 2298ece..ce8567f 100755
--- a/t/t4116-apply-reverse.sh
+++ b/t/t4116-apply-reverse.sh
@@ -30,10 +30,10 @@ test_expect_success setup '
test_expect_success 'apply in forward' '
- T0=`git rev-parse "second^{tree}"` &&
+ T0=$(git rev-parse "second^{tree}") &&
git reset --hard initial &&
git apply --index --binary patch &&
- T1=`git write-tree` &&
+ T1=$(git write-tree) &&
test "$T0" = "$T1"
'
@@ -48,12 +48,12 @@ test_expect_success 'apply in reverse' '
test_expect_success 'setup separate repository lacking postimage' '
- git tar-tree initial initial | $TAR xf - &&
+ git archive --format=tar --prefix=initial/ initial | $TAR xf - &&
(
cd initial && git init && git add .
) &&
- git tar-tree second second | $TAR xf - &&
+ git archive --format=tar --prefix=second/ second | $TAR xf - &&
(
cd second && git init && git add .
)
@@ -62,22 +62,22 @@ test_expect_success 'setup separate repository lacking postimage' '
test_expect_success 'apply in forward without postimage' '
- T0=`git rev-parse "second^{tree}"` &&
+ T0=$(git rev-parse "second^{tree}") &&
(
cd initial &&
git apply --index --binary ../patch &&
- T1=`git write-tree` &&
+ T1=$(git write-tree) &&
test "$T0" = "$T1"
)
'
test_expect_success 'apply in reverse without postimage' '
- T0=`git rev-parse "initial^{tree}"` &&
+ T0=$(git rev-parse "initial^{tree}") &&
(
cd second &&
git apply --index --binary --reverse ../patch &&
- T1=`git write-tree` &&
+ T1=$(git write-tree) &&
test "$T0" = "$T1"
)
'
diff --git a/t/t4117-apply-reject.sh b/t/t4117-apply-reject.sh
index e9ccd16..d80187d 100755
--- a/t/t4117-apply-reject.sh
+++ b/t/t4117-apply-reject.sh
@@ -46,25 +46,23 @@ test_expect_success setup '
cat file1 >saved.file1
'
-test_expect_success 'apply without --reject should fail' '
+test_expect_success 'apply --reject is incompatible with --3way' '
+ test_when_finished "cat saved.file1 >file1" &&
+ git diff >patch.0 &&
+ git checkout file1 &&
+ test_must_fail git apply --reject --3way patch.0 &&
+ git diff --exit-code
+'
- if git apply patch.1
- then
- echo "Eh? Why?"
- exit 1
- fi
+test_expect_success 'apply without --reject should fail' '
+ test_must_fail git apply patch.1 &&
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_must_fail git apply --verbose patch.1 &&
test_cmp file1 saved.file1
'
@@ -73,21 +71,11 @@ 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_must_fail git apply --reject patch.1 &&
test_cmp file1 expected &&
cat file1.rej &&
-
- if test -f file2.rej
- then
- echo "file2 should not have been touched"
- exit 1
- fi
+ test_path_is_missing file2.rej
'
test_expect_success 'apply with --reject should fail but update the file' '
@@ -95,25 +83,12 @@ 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_must_fail git apply --reject patch.2 >rejects &&
+ test_path_is_missing file1 &&
test_cmp file2 expected &&
cat file2.rej &&
-
- if test -f file1.rej
- then
- echo "file2 should not have been touched"
- exit 1
- fi
+ test_path_is_missing file1.rej
'
@@ -122,25 +97,12 @@ 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_must_fail git apply --reject --verbose patch.2 >rejects &&
+ test_path_is_missing file1 &&
test_cmp file2 expected &&
cat file2.rej &&
-
- if test -f file1.rej
- then
- echo "file2 should not have been touched"
- exit 1
- fi
+ test_path_is_missing file1.rej
'
diff --git a/t/t4119-apply-config.sh b/t/t4119-apply-config.sh
index 3c73a78..a9a0583 100755
--- a/t/t4119-apply-config.sh
+++ b/t/t4119-apply-config.sh
@@ -68,12 +68,12 @@ test_expect_success 'apply --whitespace=strip from config' '
check_result sub/file1
'
-D=`pwd`
+D=$(pwd)
test_expect_success 'apply --whitespace=strip in subdir' '
cd "$D" &&
- git config --unset-all apply.whitespace
+ git config --unset-all apply.whitespace &&
rm -f sub/file1 &&
cp saved sub/file1 &&
git update-index --refresh &&
@@ -159,4 +159,21 @@ test_expect_success 'same but with traditional patch input of depth 2' '
check_result sub/file1
'
+test_expect_success 'in subdir with traditional patch input' '
+ cd "$D" &&
+ git config apply.whitespace strip &&
+ cat >.gitattributes <<-EOF &&
+ /* whitespace=blank-at-eol
+ sub/* whitespace=-blank-at-eol
+ EOF
+ rm -f sub/file1 &&
+ cp saved sub/file1 &&
+ git update-index --refresh &&
+
+ cd sub &&
+ git apply ../gpatch.file &&
+ echo "B " >expect &&
+ test_cmp expect file1
+'
+
test_done
diff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh
index b463b4f..497b628 100755
--- a/t/t4120-apply-popt.sh
+++ b/t/t4120-apply-popt.sh
@@ -10,21 +10,80 @@ test_description='git apply -p handling.'
test_expect_success setup '
mkdir sub &&
echo A >sub/file1 &&
- cp sub/file1 file1 &&
+ cp sub/file1 file1.saved &&
git add sub/file1 &&
echo B >sub/file1 &&
git diff >patch.file &&
- rm sub/file1 &&
- rmdir sub
+ git checkout -- sub/file1 &&
+ git mv sub süb &&
+ echo B >süb/file1 &&
+ git diff >patch.escaped &&
+ grep "[\]" patch.escaped &&
+ rm süb/file1 &&
+ rmdir süb
'
test_expect_success 'apply git diff with -p2' '
+ cp file1.saved file1 &&
git apply -p2 patch.file
'
test_expect_success 'apply with too large -p' '
+ cp file1.saved file1 &&
test_must_fail git apply --stat -p3 patch.file 2>err &&
- grep "removing 3 leading" err
+ test_i18ngrep "removing 3 leading" err
+'
+
+test_expect_success 'apply (-p2) traditional diff with funny filenames' '
+ cat >patch.quotes <<-\EOF &&
+ diff -u "a/"sub/file1 "b/"sub/file1
+ --- "a/"sub/file1
+ +++ "b/"sub/file1
+ @@ -1 +1 @@
+ -A
+ +B
+ EOF
+ echo B >expected &&
+
+ cp file1.saved file1 &&
+ git apply -p2 patch.quotes &&
+ test_cmp expected file1
+'
+
+test_expect_success 'apply with too large -p and fancy filename' '
+ cp file1.saved file1 &&
+ test_must_fail git apply --stat -p3 patch.escaped 2>err &&
+ test_i18ngrep "removing 3 leading" err
+'
+
+test_expect_success 'apply (-p2) diff, mode change only' '
+ cat >patch.chmod <<-\EOF &&
+ diff --git a/sub/file1 b/sub/file1
+ old mode 100644
+ new mode 100755
+ EOF
+ test_chmod -x file1 &&
+ git apply --index -p2 patch.chmod &&
+ case $(git ls-files -s file1) in 100755*) : good;; *) false;; esac
+'
+
+test_expect_success FILEMODE 'file mode was changed' '
+ test -x file1
+'
+
+test_expect_success 'apply (-p2) diff, rename' '
+ cat >patch.rename <<-\EOF &&
+ diff --git a/sub/file1 b/sub/file2
+ similarity index 100%
+ rename from sub/file1
+ rename to sub/file2
+ EOF
+ echo A >expected &&
+
+ cp file1.saved file1 &&
+ rm -f file2 &&
+ git apply -p2 patch.rename &&
+ test_cmp expected file2
'
test_done
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
index 923fcab..4acb3f3 100755
--- a/t/t4122-apply-symlink-inside.sh
+++ b/t/t4122-apply-symlink-inside.sh
@@ -3,24 +3,11 @@
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 &&
+ test_write_lines 1 2 3 4 5 >arch/i386/boot/Makefile &&
+ test_ln_s_add ../i386/boot arch/x86_64/boot &&
git add . &&
test_tick &&
git commit -m initial &&
@@ -28,7 +15,7 @@ test_expect_success setup '
rm arch/x86_64/boot &&
mkdir arch/x86_64/boot &&
- lecho 2 3 4 5 6 >arch/x86_64/boot/Makefile &&
+ test_write_lines 2 3 4 5 6 >arch/x86_64/boot/Makefile &&
git add . &&
test_tick &&
git commit -a -m second &&
@@ -58,4 +45,110 @@ test_expect_success 'check result' '
'
+test_expect_success SYMLINKS 'do not read from beyond symbolic link' '
+ git reset --hard &&
+ mkdir -p arch/x86_64/dir &&
+ >arch/x86_64/dir/file &&
+ git add arch/x86_64/dir/file &&
+ echo line >arch/x86_64/dir/file &&
+ git diff >patch &&
+ git reset --hard &&
+
+ mkdir arch/i386/dir &&
+ >arch/i386/dir/file &&
+ ln -s ../i386/dir arch/x86_64/dir &&
+
+ test_must_fail git apply patch &&
+ test_must_fail git apply --cached patch &&
+ test_must_fail git apply --index patch
+
+'
+
+test_expect_success SYMLINKS 'do not follow symbolic link (setup)' '
+
+ rm -rf arch/i386/dir arch/x86_64/dir &&
+ git reset --hard &&
+ ln -s ../i386/dir arch/x86_64/dir &&
+ git add arch/x86_64/dir &&
+ git diff HEAD >add_symlink.patch &&
+ git reset --hard &&
+
+ mkdir arch/x86_64/dir &&
+ >arch/x86_64/dir/file &&
+ git add arch/x86_64/dir/file &&
+ git diff HEAD >add_file.patch &&
+ git diff -R HEAD >del_file.patch &&
+ git reset --hard &&
+ rm -fr arch/x86_64/dir &&
+
+ cat add_symlink.patch add_file.patch >patch &&
+ cat add_symlink.patch del_file.patch >tricky_del &&
+
+ mkdir arch/i386/dir
+'
+
+test_expect_success SYMLINKS 'do not follow symbolic link (same input)' '
+
+ # same input creates a confusing symbolic link
+ test_must_fail git apply patch 2>error-wt &&
+ test_i18ngrep "beyond a symbolic link" error-wt &&
+ test_path_is_missing arch/x86_64/dir &&
+ test_path_is_missing arch/i386/dir/file &&
+
+ test_must_fail git apply --index patch 2>error-ix &&
+ test_i18ngrep "beyond a symbolic link" error-ix &&
+ test_path_is_missing arch/x86_64/dir &&
+ test_path_is_missing arch/i386/dir/file &&
+ test_must_fail git ls-files --error-unmatch arch/x86_64/dir &&
+ test_must_fail git ls-files --error-unmatch arch/i386/dir &&
+
+ test_must_fail git apply --cached patch 2>error-ct &&
+ test_i18ngrep "beyond a symbolic link" error-ct &&
+ test_must_fail git ls-files --error-unmatch arch/x86_64/dir &&
+ test_must_fail git ls-files --error-unmatch arch/i386/dir &&
+
+ >arch/i386/dir/file &&
+ git add arch/i386/dir/file &&
+
+ test_must_fail git apply tricky_del &&
+ test_path_is_file arch/i386/dir/file &&
+
+ test_must_fail git apply --index tricky_del &&
+ test_path_is_file arch/i386/dir/file &&
+ test_must_fail git ls-files --error-unmatch arch/x86_64/dir &&
+ git ls-files --error-unmatch arch/i386/dir &&
+
+ test_must_fail git apply --cached tricky_del &&
+ test_must_fail git ls-files --error-unmatch arch/x86_64/dir &&
+ git ls-files --error-unmatch arch/i386/dir
+'
+
+test_expect_success SYMLINKS 'do not follow symbolic link (existing)' '
+
+ # existing symbolic link
+ git reset --hard &&
+ ln -s ../i386/dir arch/x86_64/dir &&
+ git add arch/x86_64/dir &&
+
+ test_must_fail git apply add_file.patch 2>error-wt-add &&
+ test_i18ngrep "beyond a symbolic link" error-wt-add &&
+ test_path_is_missing arch/i386/dir/file &&
+
+ mkdir arch/i386/dir &&
+ >arch/i386/dir/file &&
+ test_must_fail git apply del_file.patch 2>error-wt-del &&
+ test_i18ngrep "beyond a symbolic link" error-wt-del &&
+ test_path_is_file arch/i386/dir/file &&
+ rm arch/i386/dir/file &&
+
+ test_must_fail git apply --index add_file.patch 2>error-ix-add &&
+ test_i18ngrep "beyond a symbolic link" error-ix-add &&
+ test_path_is_missing arch/i386/dir/file &&
+ test_must_fail git ls-files --error-unmatch arch/i386/dir &&
+
+ test_must_fail git apply --cached add_file.patch 2>error-ct-file &&
+ test_i18ngrep "beyond a symbolic link" error-ct-file &&
+ test_must_fail git ls-files --error-unmatch arch/i386/dir
+'
+
test_done
diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh
index 8a676a5..d350065 100755
--- a/t/t4124-apply-ws-rule.sh
+++ b/t/t4124-apply-ws-rule.sh
@@ -10,7 +10,8 @@ prepare_test_file () {
# X RULE
# ! trailing-space
# @ space-before-tab
- # # indent-with-non-tab
+ # # indent-with-non-tab (default tab width 8)
+ # = indent-with-non-tab,tabwidth=16
# % tab-in-indent
sed -e "s/_/ /g" -e "s/>/ /" <<-\EOF
An_SP in an ordinary line>and a HT.
@@ -25,8 +26,8 @@ prepare_test_file () {
________>_Eight SP, a HT and a SP (@#%).
_______________Fifteen SP (#).
_______________>Fifteen SP and a HT (@#%).
- ________________Sixteen SP (#).
- ________________>Sixteen 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>
@@ -46,7 +47,7 @@ test_fix () {
# find touched lines
$DIFF file target | sed -n -e "s/^> //p" >fixed
- # the changed lines are all expeced to change
+ # the changed lines are all expected to change
fixed_cnt=$(wc -l <fixed)
case "$1" in
'') expect_cnt=$fixed_cnt ;;
@@ -98,9 +99,8 @@ test_expect_success 'whitespace=warn, default rule' '
test_expect_success 'whitespace=error-all, default rule' '
- apply_patch --whitespace=error-all && return 1
- test -s target && return 1
- : happy
+ test_must_fail apply_patch --whitespace=error-all &&
+ ! test -s target
'
@@ -121,6 +121,34 @@ test_expect_success 'whitespace=error-all, no rule (attribute)' '
'
+test_expect_success 'spaces inserted by tab-in-indent' '
+
+ git config core.whitespace -trailing,-space,-indent,tab &&
+ rm -f .gitattributes &&
+ test_fix % &&
+ sed -e "s/_/ /g" -e "s/>/ /" <<-\EOF >expect &&
+ 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
+ test_cmp expect target
+
+'
+
for t in - ''
do
case "$t" in '') tt='!' ;; *) tt= ;; esac
@@ -129,7 +157,7 @@ do
case "$s" in '') ts='@' ;; *) ts= ;; esac
for i in - ''
do
- case "$i" in '') ti='#' ;; *) ti= ;; esac
+ case "$i" in '') ti='#' ti16='=';; *) ti= ti16= ;; esac
for h in - ''
do
[ -z "$h$i" ] && continue
@@ -142,12 +170,22 @@ do
test_fix "$tt$ts$ti$th"
'
+ test_expect_success "rule=$rule,tabwidth=16" '
+ git config core.whitespace "$rule,tabwidth=16" &&
+ test_fix "$tt$ts$ti16$th"
+ '
+
test_expect_success "rule=$rule (attributes)" '
git config --unset core.whitespace &&
echo "target whitespace=$rule" >.gitattributes &&
test_fix "$tt$ts$ti$th"
'
+ test_expect_success "rule=$rule,tabwidth=16 (attributes)" '
+ echo "target whitespace=$rule,tabwidth=16" >.gitattributes &&
+ test_fix "$tt$ts$ti16$th"
+ '
+
done
done
done
@@ -176,9 +214,8 @@ test_expect_success 'trailing whitespace & no newline at the end of file' '
'
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
+ test_might_fail git config --unset core.whitespace &&
+ rm -f .gitattributes &&
{ echo a; echo b; echo c; } >one &&
git add one &&
@@ -368,7 +405,7 @@ test_expect_success 'missing blanks at EOF must only match blank lines' '
git diff -- one >patch &&
echo a >one &&
- test_must_fail git apply patch
+ 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
'
@@ -419,7 +456,7 @@ test_expect_success 'same, but with CR-LF line endings && cr-at-eol set' '
printf "b\r\n" >>one &&
printf "c\r\n" >>one &&
cp one save-one &&
- printf " \r\n" >>one
+ printf " \r\n" >>one &&
git add one &&
printf "d\r\n" >>one &&
cp one expect &&
@@ -436,7 +473,7 @@ test_expect_success 'same, but with CR-LF line endings && cr-at-eol unset' '
printf "b\r\n" >>one &&
printf "c\r\n" >>one &&
cp one save-one &&
- printf " \r\n" >>one
+ printf " \r\n" >>one &&
git add one &&
cp one expect &&
printf "d\r\n" >>one &&
@@ -448,4 +485,41 @@ test_expect_success 'same, but with CR-LF line endings && cr-at-eol unset' '
test_cmp one expect
'
+test_expect_success 'whitespace=fix to expand' '
+ qz_to_tab_space >preimage <<-\EOF &&
+ QQa
+ QQb
+ QQc
+ ZZZZZZZZZZZZZZZZd
+ QQe
+ QQf
+ QQg
+ EOF
+ qz_to_tab_space >patch <<-\EOF &&
+ diff --git a/preimage b/preimage
+ --- a/preimage
+ +++ b/preimage
+ @@ -1,7 +1,6 @@
+ QQa
+ QQb
+ QQc
+ -QQd
+ QQe
+ QQf
+ QQg
+ EOF
+ git -c core.whitespace=tab-in-indent apply --whitespace=fix patch
+'
+
+test_expect_success 'whitespace check skipped for excluded paths' '
+ git config core.whitespace blank-at-eol &&
+ >used &&
+ >unused &&
+ git add used unused &&
+ echo "used" >used &&
+ echo "unused " >unused &&
+ git diff-files -p used unused >patch &&
+ git apply --include=used --stat --whitespace=error <patch
+'
+
test_done
diff --git a/t/t4127-apply-same-fn.sh b/t/t4127-apply-same-fn.sh
index 77200c0..972946c 100755
--- a/t/t4127-apply-same-fn.sh
+++ b/t/t4127-apply-same-fn.sh
@@ -31,7 +31,7 @@ test_expect_success 'apply same filename with independent changes' '
'
test_expect_success 'apply same filename with overlapping changes' '
- git reset --hard
+ git reset --hard &&
modify "s/^d/z/" same_fn &&
git diff > patch0 &&
git add same_fn &&
@@ -44,8 +44,8 @@ test_expect_success 'apply same filename with overlapping changes' '
'
test_expect_success 'apply same new filename after rename' '
- git reset --hard
- git mv same_fn new_fn
+ git reset --hard &&
+ git mv same_fn new_fn &&
modify "s/^d/z/" new_fn &&
git add new_fn &&
git diff -M --cached > patch1 &&
@@ -58,12 +58,12 @@ test_expect_success 'apply same new filename after rename' '
'
test_expect_success 'apply same old filename after rename -- should fail.' '
- git reset --hard
- git mv same_fn new_fn
+ 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
+ git mv new_fn same_fn &&
modify "s/^e/y/" same_fn &&
git diff >> patch1 &&
git reset --hard &&
@@ -71,13 +71,13 @@ test_expect_success 'apply same old filename after rename -- should fail.' '
'
test_expect_success 'apply A->B (rename), C->A (rename), A->A -- should pass.' '
- git reset --hard
- git mv same_fn new_fn
+ 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
+ git mv other_fn same_fn &&
modify "s/^e/y/" same_fn &&
git add same_fn &&
git diff -M --cached >> patch1 &&
diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh
index fc7af04..c268298 100755
--- a/t/t4129-apply-samemode.sh
+++ b/t/t4129-apply-samemode.sh
@@ -4,13 +4,6 @@ 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 &&
diff --git a/t/t4130-apply-criss-cross-rename.sh b/t/t4130-apply-criss-cross-rename.sh
index 7cfa2d6..d173acd 100755
--- a/t/t4130-apply-criss-cross-rename.sh
+++ b/t/t4130-apply-criss-cross-rename.sh
@@ -44,7 +44,7 @@ test_expect_success 'criss-cross rename' '
git reset --hard &&
mv file1 tmp &&
mv file2 file1 &&
- mv file3 file2
+ mv file3 file2 &&
mv tmp file3 &&
cp file1 file1-swapped &&
cp file2 file2-swapped &&
diff --git a/t/t4131-apply-fake-ancestor.sh b/t/t4131-apply-fake-ancestor.sh
index 94373ca..b1361ce 100755
--- a/t/t4131-apply-fake-ancestor.sh
+++ b/t/t4131-apply-fake-ancestor.sh
@@ -11,7 +11,7 @@ test_expect_success 'setup' '
test_commit 1 &&
test_commit 2 &&
mkdir sub &&
- test_commit 3 sub/3 &&
+ test_commit 3 sub/3.t &&
test_commit 4
'
diff --git a/t/t4132-apply-removal.sh b/t/t4132-apply-removal.sh
index bb1ffe3..a2bc1cd 100755
--- a/t/t4132-apply-removal.sh
+++ b/t/t4132-apply-removal.sh
@@ -30,6 +30,7 @@ test_expect_success setup '
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" &&
+ epocWest2="1969-12-31 16:00:00 -08:00" &&
sed -e "s/TS0/$epocWest/" -e "s/TS1/$timeWest/" <c >createWest.patch &&
sed -e "s/TS0/$epocEast/" -e "s/TS1/$timeEast/" <c >createEast.patch &&
@@ -46,6 +47,7 @@ test_expect_success setup '
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 &&
+ sed -e "s/TS0/$timeWest/" -e "s/TS1/$epocWest2/" <d >removeWest2.patch &&
echo something >something &&
>empty
diff --git a/t/t4133-apply-filenames.sh b/t/t4133-apply-filenames.sh
index 3421807..2ecb421 100755
--- a/t/t4133-apply-filenames.sh
+++ b/t/t4133-apply-filenames.sh
@@ -8,7 +8,7 @@ test_description='git apply filename consistency check'
. ./test-lib.sh
test_expect_success setup '
- cat > bad1.patch <<EOF
+ cat > bad1.patch <<EOF &&
diff --git a/f b/f
new file mode 100644
index 0000000..d00491f
@@ -29,10 +29,10 @@ 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_must_fail git apply bad1.patch 2>err &&
+ test_i18ngrep "inconsistent new filename" err &&
+ test_must_fail git apply bad2.patch 2>err &&
+ test_i18ngrep "inconsistent old filename" err
'
test_done
diff --git a/t/t4134-apply-submodule.sh b/t/t4134-apply-submodule.sh
index 1b82f93..0043930 100755
--- a/t/t4134-apply-submodule.sh
+++ b/t/t4134-apply-submodule.sh
@@ -8,7 +8,7 @@ test_description='git apply submodule tests'
. ./test-lib.sh
test_expect_success setup '
- cat > create-sm.patch <<EOF
+ cat > create-sm.patch <<EOF &&
diff --git a/dir/sm b/dir/sm
new file mode 160000
index 0000000..0123456
diff --git a/t/t4135-apply-weird-filenames.sh b/t/t4135-apply-weird-filenames.sh
new file mode 100755
index 0000000..27cb000
--- /dev/null
+++ b/t/t4135-apply-weird-filenames.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='git apply with weird postimage filenames'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ vector=$TEST_DIRECTORY/t4135 &&
+
+ test_tick &&
+ git commit --allow-empty -m preimage &&
+ git tag preimage &&
+
+ reset_preimage() {
+ git checkout -f preimage^0 &&
+ git read-tree -u --reset HEAD &&
+ git update-index --refresh
+ } &&
+
+ test_when_finished "rm -f \"tab embedded.txt\"" &&
+ test_when_finished "rm -f '\''\"quoteembedded\".txt'\''" &&
+ if test_have_prereq !MINGW &&
+ touch -- "tab embedded.txt" '\''"quoteembedded".txt'\''
+ then
+ test_set_prereq FUNNYNAMES
+ fi
+'
+
+try_filename() {
+ desc=$1
+ postimage=$2
+ prereq=${3:-}
+ exp1=${4:-success}
+ exp2=${5:-success}
+ exp3=${6:-success}
+
+ test_expect_$exp1 $prereq "$desc, git-style file creation patch" "
+ echo postimage >expected &&
+ reset_preimage &&
+ rm -f '$postimage' &&
+ git apply -v \"\$vector\"/'git-$desc.diff' &&
+ test_cmp expected '$postimage'
+ "
+
+ test_expect_$exp2 $prereq "$desc, traditional patch" "
+ echo postimage >expected &&
+ reset_preimage &&
+ echo preimage >'$postimage' &&
+ git apply -v \"\$vector\"/'diff-$desc.diff' &&
+ test_cmp expected '$postimage'
+ "
+
+ test_expect_$exp3 $prereq "$desc, traditional file creation patch" "
+ echo postimage >expected &&
+ reset_preimage &&
+ rm -f '$postimage' &&
+ git apply -v \"\$vector\"/'add-$desc.diff' &&
+ test_cmp expected '$postimage'
+ "
+}
+
+try_filename 'plain' 'postimage.txt'
+try_filename 'with spaces' 'post image.txt'
+try_filename 'with tab' 'post image.txt' FUNNYNAMES
+try_filename 'with backslash' 'post\image.txt' BSLASHPSPEC
+try_filename 'with quote' '"postimage".txt' FUNNYNAMES success failure success
+
+test_expect_success 'whitespace-damaged traditional patch' '
+ echo postimage >expected &&
+ reset_preimage &&
+ rm -f postimage.txt &&
+ git apply -v "$vector/damaged.diff" &&
+ test_cmp expected postimage.txt
+'
+
+test_expect_success 'traditional patch with colon in timezone' '
+ echo postimage >expected &&
+ reset_preimage &&
+ rm -f "post image.txt" &&
+ git apply "$vector/funny-tz.diff" &&
+ test_cmp expected "post image.txt"
+'
+
+test_expect_success 'traditional, whitespace-damaged, colon in timezone' '
+ echo postimage >expected &&
+ reset_preimage &&
+ rm -f "post image.txt" &&
+ git apply "$vector/damaged-tz.diff" &&
+ test_cmp expected "post image.txt"
+'
+
+test_done
diff --git a/t/t4135/.gitignore b/t/t4135/.gitignore
new file mode 100644
index 0000000..3e58e65
--- /dev/null
+++ b/t/t4135/.gitignore
@@ -0,0 +1,3 @@
+/file-creation/
+/trad-creation/
+/trad-modification/
diff --git a/t/t4135/add-plain.diff b/t/t4135/add-plain.diff
new file mode 100644
index 0000000..cf5970a
--- /dev/null
+++ b/t/t4135/add-plain.diff
@@ -0,0 +1,5 @@
+diff -pruN a/postimage.txt b/postimage.txt
+--- a/postimage.txt 1969-12-31 18:00:00.000000000 -0600
++++ b/postimage.txt 2010-08-18 20:13:31.484002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/add-with backslash.diff b/t/t4135/add-with backslash.diff
new file mode 100644
index 0000000..c6861e1
--- /dev/null
+++ b/t/t4135/add-with backslash.diff
@@ -0,0 +1,5 @@
+diff -pruN a/post\image.txt b/post\image.txt
+--- a/post\image.txt 1969-12-31 18:00:00.000000000 -0600
++++ b/post\image.txt 2010-08-18 20:13:31.692002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/add-with quote.diff b/t/t4135/add-with quote.diff
new file mode 100644
index 0000000..866de78
--- /dev/null
+++ b/t/t4135/add-with quote.diff
@@ -0,0 +1,5 @@
+diff -pruN a/"postimage".txt b/"postimage".txt
+--- a/"postimage".txt 1969-12-31 18:00:00.000000000 -0600
++++ b/"postimage".txt 2010-08-18 20:13:31.756002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/add-with spaces.diff b/t/t4135/add-with spaces.diff
new file mode 100644
index 0000000..a9a1212
--- /dev/null
+++ b/t/t4135/add-with spaces.diff
@@ -0,0 +1,5 @@
+diff -pruN a/post image.txt b/post image.txt
+--- a/post image.txt 1969-12-31 18:00:00.000000000 -0600
++++ b/post image.txt 2010-08-18 20:13:31.556002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/add-with tab.diff b/t/t4135/add-with tab.diff
new file mode 100644
index 0000000..bb67cb7
--- /dev/null
+++ b/t/t4135/add-with tab.diff
@@ -0,0 +1,5 @@
+diff -pruN a/post image.txt b/post image.txt
+--- a/post image.txt 1969-12-31 18:00:00.000000000 -0600
++++ b/post image.txt 2010-08-18 20:13:31.628002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/damaged-tz.diff b/t/t4135/damaged-tz.diff
new file mode 100644
index 0000000..07aaf08
--- /dev/null
+++ b/t/t4135/damaged-tz.diff
@@ -0,0 +1,5 @@
+diff -urN -X /usr/people/jes/exclude-linux linux-2.6.12-rc2-mm3-vanilla/post image.txt linux-2.6.12-rc2-mm3/post image.txt
+--- linux-2.6.12-rc2-mm3-vanilla/post image.txt 1969-12-31 16:00:00 -08:00
++++ linux-2.6.12-rc2-mm3/post image.txt 2005-04-12 02:14:06 -07:00
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/damaged.diff b/t/t4135/damaged.diff
new file mode 100644
index 0000000..68f7ede
--- /dev/null
+++ b/t/t4135/damaged.diff
@@ -0,0 +1,5 @@
+diff -pruN a/postimage.txt b/postimage.txt
+--- a/postimage.txt 1969-12-31 18:00:00.000000000 -0600
++++ b/postimage.txt 2010-08-18 20:13:31.484002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/diff-plain.diff b/t/t4135/diff-plain.diff
new file mode 100644
index 0000000..acedcfa
--- /dev/null
+++ b/t/t4135/diff-plain.diff
@@ -0,0 +1,5 @@
+--- postimage.txt.orig 2010-08-18 20:13:31.432002255 -0500
++++ postimage.txt 2010-08-18 20:13:31.432002255 -0500
+@@ -1 +1 @@
+-preimage
++postimage
diff --git a/t/t4135/diff-with backslash.diff b/t/t4135/diff-with backslash.diff
new file mode 100644
index 0000000..9068a61
--- /dev/null
+++ b/t/t4135/diff-with backslash.diff
@@ -0,0 +1,5 @@
+--- post\image.txt.orig 2010-08-18 20:13:31.680002255 -0500
++++ post\image.txt 2010-08-18 20:13:31.680002255 -0500
+@@ -1 +1 @@
+-preimage
++postimage
diff --git a/t/t4135/diff-with quote.diff b/t/t4135/diff-with quote.diff
new file mode 100644
index 0000000..c8e8cc1
--- /dev/null
+++ b/t/t4135/diff-with quote.diff
@@ -0,0 +1,5 @@
+--- "postimage".txt.orig 2010-08-18 20:13:31.744002255 -0500
++++ "postimage".txt 2010-08-18 20:13:31.744002255 -0500
+@@ -1 +1 @@
+-preimage
++postimage
diff --git a/t/t4135/diff-with spaces.diff b/t/t4135/diff-with spaces.diff
new file mode 100644
index 0000000..3512056
--- /dev/null
+++ b/t/t4135/diff-with spaces.diff
@@ -0,0 +1,5 @@
+--- post image.txt.orig 2010-08-18 20:13:31.544002255 -0500
++++ post image.txt 2010-08-18 20:13:31.544002255 -0500
+@@ -1 +1 @@
+-preimage
++postimage
diff --git a/t/t4135/diff-with tab.diff b/t/t4135/diff-with tab.diff
new file mode 100644
index 0000000..4e6d9b2
--- /dev/null
+++ b/t/t4135/diff-with tab.diff
@@ -0,0 +1,5 @@
+--- post image.txt.orig 2010-08-18 20:13:31.616002255 -0500
++++ post image.txt 2010-08-18 20:13:31.616002255 -0500
+@@ -1 +1 @@
+-preimage
++postimage
diff --git a/t/t4135/funny-tz.diff b/t/t4135/funny-tz.diff
new file mode 100644
index 0000000..998e3a8
--- /dev/null
+++ b/t/t4135/funny-tz.diff
@@ -0,0 +1,5 @@
+diff -urN -X /usr/people/jes/exclude-linux linux-2.6.12-rc2-mm3-vanilla/post image.txt linux-2.6.12-rc2-mm3/post image.txt
+--- linux-2.6.12-rc2-mm3-vanilla/post image.txt 1969-12-31 16:00:00 -08:00
++++ linux-2.6.12-rc2-mm3/post image.txt 2005-04-12 02:14:06 -07:00
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/git-plain.diff b/t/t4135/git-plain.diff
new file mode 100644
index 0000000..db47d1a
--- /dev/null
+++ b/t/t4135/git-plain.diff
@@ -0,0 +1,7 @@
+diff --git a/postimage.txt b/postimage.txt
+new file mode 100644
+index 0000000..eff0c54
+--- /dev/null
++++ b/postimage.txt
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/git-with backslash.diff b/t/t4135/git-with backslash.diff
new file mode 100644
index 0000000..0e84a10
--- /dev/null
+++ b/t/t4135/git-with backslash.diff
@@ -0,0 +1,7 @@
+diff --git "a/post\\image.txt" "b/post\\image.txt"
+new file mode 100644
+index 0000000..eff0c54
+--- /dev/null
++++ "b/post\\image.txt"
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/git-with quote.diff b/t/t4135/git-with quote.diff
new file mode 100644
index 0000000..bdbea8a
--- /dev/null
+++ b/t/t4135/git-with quote.diff
@@ -0,0 +1,7 @@
+diff --git "a/\"postimage\".txt" "b/\"postimage\".txt"
+new file mode 100644
+index 0000000..eff0c54
+--- /dev/null
++++ "b/\"postimage\".txt"
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/git-with spaces.diff b/t/t4135/git-with spaces.diff
new file mode 100644
index 0000000..baaa810
--- /dev/null
+++ b/t/t4135/git-with spaces.diff
@@ -0,0 +1,7 @@
+diff --git a/post image.txt b/post image.txt
+new file mode 100644
+index 0000000..eff0c54
+--- /dev/null
++++ b/post image.txt
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/git-with tab.diff b/t/t4135/git-with tab.diff
new file mode 100644
index 0000000..cca3c92
--- /dev/null
+++ b/t/t4135/git-with tab.diff
@@ -0,0 +1,7 @@
+diff --git "a/post\timage.txt" "b/post\timage.txt"
+new file mode 100644
+index 0000000..eff0c54
+--- /dev/null
++++ "b/post\timage.txt"
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/make-patches b/t/t4135/make-patches
new file mode 100755
index 0000000..f5f45dd
--- /dev/null
+++ b/t/t4135/make-patches
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+do_filename() {
+ desc=$1
+ postimage=$2
+
+ rm -fr file-creation &&
+ git init file-creation &&
+ (
+ cd file-creation &&
+ git commit --allow-empty -m init &&
+ echo postimage >"$postimage" &&
+ git add -N "$postimage" &&
+ git diff HEAD >"../git-$desc.diff"
+ ) &&
+
+ rm -fr trad-modification &&
+ mkdir trad-modification &&
+ (
+ cd trad-modification &&
+ echo preimage >"$postimage.orig" &&
+ echo postimage >"$postimage" &&
+ ! diff -u "$postimage.orig" "$postimage" >"../diff-$desc.diff"
+ ) &&
+
+ rm -fr trad-creation &&
+ mkdir trad-creation &&
+ (
+ cd trad-creation &&
+ mkdir a b &&
+ echo postimage >"b/$postimage" &&
+ ! diff -pruN a b >"../add-$desc.diff"
+ )
+}
+
+do_filename plain postimage.txt &&
+do_filename 'with spaces' 'post image.txt' &&
+do_filename 'with tab' 'post image.txt' &&
+do_filename 'with backslash' 'post\image.txt' &&
+do_filename 'with quote' '"postimage".txt' &&
+expand add-plain.diff >damaged.diff ||
+{
+ echo >&2 Failed. &&
+ exit 1
+}
diff --git a/t/t4136-apply-check.sh b/t/t4136-apply-check.sh
new file mode 100755
index 0000000..4b0a374
--- /dev/null
+++ b/t/t4136-apply-check.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='git apply should exit non-zero with unrecognized input.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit 1
+'
+
+test_expect_success 'apply --check exits non-zero with unrecognized input' '
+ test_must_fail git apply --check - <<-\EOF
+ I am not a patch
+ I look nothing like a patch
+ git apply must fail
+ EOF
+'
+
+test_expect_success 'apply exits non-zero with no-op patch' '
+ cat >input <<-\EOF &&
+ diff --get a/1 b/1
+ index 6696ea4..606eddd 100644
+ --- a/1
+ +++ b/1
+ @@ -1,1 +1,1 @@
+ 1
+ EOF
+ test_must_fail git apply --stat input &&
+ test_must_fail git apply --check input
+'
+
+test_done
diff --git a/t/t4137-apply-submodule.sh b/t/t4137-apply-submodule.sh
new file mode 100755
index 0000000..a9bd40a
--- /dev/null
+++ b/t/t4137-apply-submodule.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+test_description='git apply handling submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+apply_index () {
+ git diff --ignore-submodules=dirty "..$1" | git apply --index -
+}
+
+test_submodule_switch "apply_index"
+
+apply_3way () {
+ git diff --ignore-submodules=dirty "..$1" | git apply --3way -
+}
+
+test_submodule_switch "apply_3way"
+
+test_done
diff --git a/t/t4138-apply-ws-expansion.sh b/t/t4138-apply-ws-expansion.sh
new file mode 100755
index 0000000..0ffe33f
--- /dev/null
+++ b/t/t4138-apply-ws-expansion.sh
@@ -0,0 +1,121 @@
+#!/bin/sh
+#
+# Copyright (C) 2015 Kyle J. McKay
+#
+
+test_description='git apply test patches with whitespace expansion.'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ #
+ ## create test-N, patchN.patch, expect-N files
+ #
+
+ # test 1
+ printf "\t%s\n" 1 2 3 4 5 6 >before &&
+ printf "\t%s\n" 1 2 3 >after &&
+ printf "%64s\n" a b c >>after &&
+ printf "\t%s\n" 4 5 6 >>after &&
+ git diff --no-index before after |
+ sed -e "s/before/test-1/" -e "s/after/test-1/" >patch1.patch &&
+ printf "%64s\n" 1 2 3 4 5 6 >test-1 &&
+ printf "%64s\n" 1 2 3 a b c 4 5 6 >expect-1 &&
+
+ # test 2
+ printf "\t%s\n" a b c d e f >before &&
+ printf "\t%s\n" a b c >after &&
+ n=10 &&
+ x=1 &&
+ while test $x -lt $n
+ do
+ printf "%63s%d\n" "" $x >>after
+ x=$(( $x + 1 ))
+ done &&
+ printf "\t%s\n" d e f >>after &&
+ git diff --no-index before after |
+ sed -e "s/before/test-2/" -e "s/after/test-2/" >patch2.patch &&
+ printf "%64s\n" a b c d e f >test-2 &&
+ printf "%64s\n" a b c >expect-2 &&
+ x=1 &&
+ while test $x -lt $n
+ do
+ printf "%63s%d\n" "" $x >>expect-2
+ x=$(( $x + 1 ))
+ done &&
+ printf "%64s\n" d e f >>expect-2 &&
+
+ # test 3
+ printf "\t%s\n" a b c d e f >before &&
+ printf "\t%s\n" a b c >after &&
+ n=100 &&
+ x=0 &&
+ while test $x -lt $n
+ do
+ printf "%63s%02d\n" "" $x >>after
+ x=$(( $x + 1 ))
+ done &&
+ printf "\t%s\n" d e f >>after &&
+ git diff --no-index before after |
+ sed -e "s/before/test-3/" -e "s/after/test-3/" >patch3.patch &&
+ printf "%64s\n" a b c d e f >test-3 &&
+ printf "%64s\n" a b c >expect-3 &&
+ x=0 &&
+ while test $x -lt $n
+ do
+ printf "%63s%02d\n" "" $x >>expect-3
+ x=$(( $x + 1 ))
+ done &&
+ printf "%64s\n" d e f >>expect-3 &&
+
+ # test 4
+ >before &&
+ x=0 &&
+ while test $x -lt 50
+ do
+ printf "\t%02d\n" $x >>before
+ x=$(( $x + 1 ))
+ done &&
+ cat before >after &&
+ printf "%64s\n" a b c >>after &&
+ while test $x -lt 100
+ do
+ printf "\t%02d\n" $x >>before
+ printf "\t%02d\n" $x >>after
+ x=$(( $x + 1 ))
+ done &&
+ git diff --no-index before after |
+ sed -e "s/before/test-4/" -e "s/after/test-4/" >patch4.patch &&
+ >test-4 &&
+ x=0 &&
+ while test $x -lt 50
+ do
+ printf "%63s%02d\n" "" $x >>test-4
+ x=$(( $x + 1 ))
+ done &&
+ cat test-4 >expect-4 &&
+ printf "%64s\n" a b c >>expect-4 &&
+ while test $x -lt 100
+ do
+ printf "%63s%02d\n" "" $x >>test-4
+ printf "%63s%02d\n" "" $x >>expect-4
+ x=$(( $x + 1 ))
+ done &&
+
+ git config core.whitespace tab-in-indent,tabwidth=63 &&
+ git config apply.whitespace fix
+
+'
+
+# Note that `patch` can successfully apply all patches when run
+# with the --ignore-whitespace option.
+
+for t in 1 2 3 4
+do
+ test_expect_success 'apply with ws expansion (t=$t)' '
+ git apply patch$t.patch &&
+ test_cmp test-$t expect-$t
+ '
+done
+
+test_done
diff --git a/t/t4139-apply-escape.sh b/t/t4139-apply-escape.sh
new file mode 100755
index 0000000..45b5660
--- /dev/null
+++ b/t/t4139-apply-escape.sh
@@ -0,0 +1,141 @@
+#!/bin/sh
+
+test_description='paths written by git-apply cannot escape the working tree'
+. ./test-lib.sh
+
+# tests will try to write to ../foo, and we do not
+# want them to escape the trash directory when they
+# fail
+test_expect_success 'bump git repo one level down' '
+ mkdir inside &&
+ mv .git inside/ &&
+ cd inside
+'
+
+# $1 = name of file
+# $2 = current path to file (if different)
+mkpatch_add () {
+ rm -f "${2:-$1}" &&
+ cat <<-EOF
+ diff --git a/$1 b/$1
+ new file mode 100644
+ index 0000000..53c74cd
+ --- /dev/null
+ +++ b/$1
+ @@ -0,0 +1 @@
+ +evil
+ EOF
+}
+
+mkpatch_del () {
+ echo evil >"${2:-$1}" &&
+ cat <<-EOF
+ diff --git a/$1 b/$1
+ deleted file mode 100644
+ index 53c74cd..0000000
+ --- a/$1
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -evil
+ EOF
+}
+
+# $1 = name of file
+# $2 = content of symlink
+mkpatch_symlink () {
+ rm -f "$1" &&
+ cat <<-EOF
+ diff --git a/$1 b/$1
+ new file mode 120000
+ index 0000000..$(printf "%s" "$2" | git hash-object --stdin)
+ --- /dev/null
+ +++ b/$1
+ @@ -0,0 +1 @@
+ +$2
+ \ No newline at end of file
+ EOF
+}
+
+test_expect_success 'cannot create file containing ..' '
+ mkpatch_add ../foo >patch &&
+ test_must_fail git apply patch &&
+ test_path_is_missing ../foo
+'
+
+test_expect_success 'can create file containing .. with --unsafe-paths' '
+ mkpatch_add ../foo >patch &&
+ git apply --unsafe-paths patch &&
+ test_path_is_file ../foo
+'
+
+test_expect_success 'cannot create file containing .. (index)' '
+ mkpatch_add ../foo >patch &&
+ test_must_fail git apply --index patch &&
+ test_path_is_missing ../foo
+'
+
+test_expect_success 'cannot create file containing .. with --unsafe-paths (index)' '
+ mkpatch_add ../foo >patch &&
+ test_must_fail git apply --index --unsafe-paths patch &&
+ test_path_is_missing ../foo
+'
+
+test_expect_success 'cannot delete file containing ..' '
+ mkpatch_del ../foo >patch &&
+ test_must_fail git apply patch &&
+ test_path_is_file ../foo
+'
+
+test_expect_success 'can delete file containing .. with --unsafe-paths' '
+ mkpatch_del ../foo >patch &&
+ git apply --unsafe-paths patch &&
+ test_path_is_missing ../foo
+'
+
+test_expect_success 'cannot delete file containing .. (index)' '
+ mkpatch_del ../foo >patch &&
+ test_must_fail git apply --index patch &&
+ test_path_is_file ../foo
+'
+
+test_expect_success SYMLINKS 'symlink escape via ..' '
+ {
+ mkpatch_symlink tmp .. &&
+ mkpatch_add tmp/foo ../foo
+ } >patch &&
+ test_must_fail git apply patch &&
+ test_path_is_missing tmp &&
+ test_path_is_missing ../foo
+'
+
+test_expect_success SYMLINKS 'symlink escape via .. (index)' '
+ {
+ mkpatch_symlink tmp .. &&
+ mkpatch_add tmp/foo ../foo
+ } >patch &&
+ test_must_fail git apply --index patch &&
+ test_path_is_missing tmp &&
+ test_path_is_missing ../foo
+'
+
+test_expect_success SYMLINKS 'symlink escape via absolute path' '
+ {
+ mkpatch_symlink tmp "$(pwd)" &&
+ mkpatch_add tmp/foo ../foo
+ } >patch &&
+ test_must_fail git apply patch &&
+ test_path_is_missing tmp &&
+ test_path_is_missing ../foo
+'
+
+test_expect_success SYMLINKS 'symlink escape via absolute path (index)' '
+ {
+ mkpatch_symlink tmp "$(pwd)" &&
+ mkpatch_add tmp/foo ../foo
+ } >patch &&
+ test_must_fail git apply --index patch &&
+ test_path_is_missing tmp &&
+ test_path_is_missing ../foo
+'
+
+test_done
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 810b04b..b41bd17 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -4,66 +4,84 @@ 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: messages' '
+ 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.
+
+ EOF
+ qz_to_tab_space <<-\EOF >>msg &&
+ QDuis autem vel eum iriure dolor in hendrerit in vulputate velit
+ Qesse molestie consequat, vel illum dolore eu feugiat nulla facilisis
+ Qat vero eros et accumsan et iusto odio dignissim qui blandit
+ Qpraesent luptatum zzril delenit augue duis dolore te feugait nulla
+ Qfacilisi.
+ EOF
+ cat >>msg <<-\EOF &&
+
+ 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
+
+ cat >scissors-msg <<-\EOF &&
+ Test git-am with scissors line
+
+ This line should be included in the commit message.
+ EOF
+
+ cat - scissors-msg >no-scissors-msg <<-\EOF &&
+ This line should not be included in the commit message with --scissors enabled.
+
+ - - >8 - - remove everything above this line - - >8 - -
+
+ EOF
+
+ signoff="Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+'
test_expect_success setup '
echo hello >file &&
@@ -71,13 +89,16 @@ test_expect_success setup '
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 "Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>" &&
echo "X-Fake-Field: Line One" &&
echo "X-Fake-Field: Line Two" &&
echo "X-Fake-Field: Line Three" &&
@@ -89,74 +110,355 @@ test_expect_success setup '
echo "X-Fake-Field: Line Three" &&
git format-patch --stdout first | sed -e "1d"
} | append_cr >patch1-crlf.eml &&
+ {
+ printf "%255s\\n" ""
+ 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-ws.eml &&
+ {
+ sed -ne "1p" msg &&
+ echo &&
+ echo "From: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>" &&
+ echo "Date: $GIT_AUTHOR_DATE" &&
+ echo &&
+ sed -e "1,2d" msg &&
+ echo &&
+ echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" &&
+ echo "---" &&
+ git diff-tree --no-commit-id --stat -p second
+ } >patch1-stgit.eml &&
+ mkdir stgit-series &&
+ cp patch1-stgit.eml stgit-series/patch &&
+ {
+ echo "# This series applies on GIT commit $(git rev-parse first)" &&
+ echo "patch"
+ } >stgit-series/series &&
+ {
+ echo "# HG changeset patch" &&
+ echo "# User $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>" &&
+ echo "# Date $test_tick 25200" &&
+ echo "# $(git show --pretty="%aD" -s second)" &&
+ echo "# Node ID $_z40" &&
+ echo "# Parent $_z40" &&
+ cat msg &&
+ echo &&
+ echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" &&
+ echo &&
+ git diff-tree --no-commit-id -p second
+ } >patch1-hg.eml &&
+
+
+ echo scissors-file >scissors-file &&
+ git add scissors-file &&
+ git commit -F scissors-msg &&
+ git tag scissors &&
+ git format-patch --stdout scissors^ >scissors-patch.eml &&
+ git reset --hard HEAD^ &&
+
+ echo no-scissors-file >no-scissors-file &&
+ git add no-scissors-file &&
+ git commit -F no-scissors-msg &&
+ git tag no-scissors &&
+ git format-patch --stdout no-scissors^ >no-scissors-patch.eml &&
+ git reset --hard HEAD^ &&
+
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
+ git format-patch --stdout master >lorem-move.patch &&
+ git format-patch --no-prefix --stdout master >lorem-zero.patch &&
+
+ git checkout -b rename &&
+ git mv file renamed &&
+ git commit -m "renamed a file" &&
+
+ git format-patch -M --stdout lorem >rename.patch &&
+
+ git reset --soft lorem^ &&
+ git commit -m "renamed a file and added another" &&
+
+ git format-patch -M --stdout lorem^ >rename-add.patch &&
+
+ # reset time
+ sane_unset test_tick &&
+ test_tick
+'
test_expect_success 'am applies patch correctly' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
test_tick &&
git am <patch1 &&
- ! test -d .git/rebase-apply &&
- test -z "$(git diff second)" &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code second &&
test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
'
+test_expect_success 'am fails if index is dirty' '
+ test_when_finished "rm -f dirtyfile" &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout first &&
+ echo dirtyfile >dirtyfile &&
+ git add dirtyfile &&
+ test_must_fail git am patch1 &&
+ test_path_is_dir .git/rebase-apply &&
+ test_cmp_rev first HEAD
+'
+
test_expect_success 'am applies patch e-mail not in a mbox' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
git am patch1.eml &&
- ! test -d .git/rebase-apply &&
- test -z "$(git diff second)" &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code second &&
test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
'
test_expect_success 'am applies patch e-mail not in a mbox with CRLF' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
git am patch1-crlf.eml &&
- ! test -d .git/rebase-apply &&
- test -z "$(git diff second)" &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code second &&
test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
'
-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
+test_expect_success 'am applies patch e-mail with preceding whitespace' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout first &&
+ git am patch1-ws.eml &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code second &&
+ test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
+ test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
+'
+
+test_expect_success 'am applies stgit patch' '
+ rm -fr .git/rebase-apply &&
+ git checkout -f first &&
+ git am patch1-stgit.eml &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code second &&
+ test_cmp_rev second HEAD &&
+ test_cmp_rev second^ HEAD^
+'
+
+test_expect_success 'am --patch-format=stgit applies stgit patch' '
+ rm -fr .git/rebase-apply &&
+ git checkout -f first &&
+ git am --patch-format=stgit <patch1-stgit.eml &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code second &&
+ test_cmp_rev second HEAD &&
+ test_cmp_rev second^ HEAD^
+'
+
+test_expect_success 'am applies stgit series' '
+ rm -fr .git/rebase-apply &&
+ git checkout -f first &&
+ git am stgit-series/series &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code second &&
+ test_cmp_rev second HEAD &&
+ test_cmp_rev second^ HEAD^
+'
+
+test_expect_success 'am applies hg patch' '
+ rm -fr .git/rebase-apply &&
+ git checkout -f first &&
+ git am patch1-hg.eml &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code second &&
+ test_cmp_rev second HEAD &&
+ test_cmp_rev second^ HEAD^
+'
+
+test_expect_success 'am --patch-format=hg applies hg patch' '
+ rm -fr .git/rebase-apply &&
+ git checkout -f first &&
+ git am --patch-format=hg <patch1-hg.eml &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code second &&
+ test_cmp_rev second HEAD &&
+ test_cmp_rev second^ HEAD^
+'
+
+test_expect_success 'am with applypatch-msg hook' '
+ test_when_finished "rm -f .git/hooks/applypatch-msg" &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout first &&
+ mkdir -p .git/hooks &&
+ write_script .git/hooks/applypatch-msg <<-\EOF &&
+ cat "$1" >actual-msg &&
+ echo hook-message >"$1"
+ EOF
+ git am patch1 &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code second &&
+ echo hook-message >expected &&
+ git log -1 --format=format:%B >actual &&
+ test_cmp expected actual &&
+ git log -1 --format=format:%B second >expected &&
+ test_cmp expected actual-msg
+'
+
+test_expect_success 'am with failing applypatch-msg hook' '
+ test_when_finished "rm -f .git/hooks/applypatch-msg" &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout first &&
+ mkdir -p .git/hooks &&
+ write_script .git/hooks/applypatch-msg <<-\EOF &&
+ exit 1
+ EOF
+ test_must_fail git am patch1 &&
+ test_path_is_dir .git/rebase-apply &&
+ git diff --exit-code first &&
+ test_cmp_rev first HEAD
+'
+
+test_expect_success 'am with pre-applypatch hook' '
+ test_when_finished "rm -f .git/hooks/pre-applypatch" &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout first &&
+ mkdir -p .git/hooks &&
+ write_script .git/hooks/pre-applypatch <<-\EOF &&
+ git diff first >diff.actual
+ exit 0
+ EOF
+ git am patch1 &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code second &&
+ test_cmp_rev second HEAD &&
+ git diff first..second >diff.expected &&
+ test_cmp diff.expected diff.actual
+'
+
+test_expect_success 'am with failing pre-applypatch hook' '
+ test_when_finished "rm -f .git/hooks/pre-applypatch" &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout first &&
+ mkdir -p .git/hooks &&
+ write_script .git/hooks/pre-applypatch <<-\EOF &&
+ exit 1
+ EOF
+ test_must_fail git am patch1 &&
+ test_path_is_dir .git/rebase-apply &&
+ git diff --exit-code second &&
+ test_cmp_rev first HEAD
+'
+
+test_expect_success 'am with post-applypatch hook' '
+ test_when_finished "rm -f .git/hooks/post-applypatch" &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout first &&
+ mkdir -p .git/hooks &&
+ write_script .git/hooks/post-applypatch <<-\EOF &&
+ git rev-parse HEAD >head.actual
+ git diff second >diff.actual
+ exit 0
+ EOF
+ git am patch1 &&
+ test_path_is_missing .git/rebase-apply &&
+ test_cmp_rev second HEAD &&
+ git rev-parse second >head.expected &&
+ test_cmp head.expected head.actual &&
+ git diff second >diff.expected &&
+ test_cmp diff.expected diff.actual
+'
+
+test_expect_success 'am with failing post-applypatch hook' '
+ test_when_finished "rm -f .git/hooks/post-applypatch" &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout first &&
+ mkdir -p .git/hooks &&
+ write_script .git/hooks/post-applypatch <<-\EOF &&
+ git rev-parse HEAD >head.actual
+ exit 1
+ EOF
+ git am patch1 &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code second &&
+ test_cmp_rev second HEAD &&
+ git rev-parse second >head.expected &&
+ test_cmp head.expected head.actual
+'
+
+test_expect_success 'am --scissors cuts the message at the scissors line' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout second &&
+ git am --scissors scissors-patch.eml &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code scissors &&
+ test_cmp_rev scissors HEAD
+'
+
+test_expect_success 'am --no-scissors overrides mailinfo.scissors' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout second &&
+ test_config mailinfo.scissors true &&
+ git am --no-scissors no-scissors-patch.eml &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code no-scissors &&
+ test_cmp_rev no-scissors HEAD
+'
+
+test_expect_success 'setup: new author and committer' '
+ 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 ")"
+ a=$(git cat-file commit "$2" | grep "^$1 ") &&
+ b=$(git cat-file commit "$3" | grep "^$1 ") &&
+ test "$a" = "$b"
}
test_expect_success 'am changes committer and keeps author' '
test_tick &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
git am patch2 &&
- ! test -d .git/rebase-apply &&
+ test_path_is_missing .git/rebase-apply &&
test "$(git rev-parse master^^)" = "$(git rev-parse HEAD^^)" &&
- test -z "$(git diff master..HEAD)" &&
- test -z "$(git diff master^..HEAD^)" &&
+ git diff --exit-code master..HEAD &&
+ git diff --exit-code master^..HEAD^ &&
compare author master HEAD &&
compare author master^ HEAD^ &&
test "$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" = \
@@ -164,130 +466,282 @@ test_expect_success 'am changes committer and keeps author' '
'
test_expect_success 'am --signoff adds Signed-off-by: line' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout -b master2 first &&
git am --signoff <patch2 &&
+ printf "%s\n" "$signoff" >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_cmp expected actual &&
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_cmp expected actual
'
test_expect_success 'am stays in branch' '
- test "refs/heads/master2" = "$(git symbolic-ref HEAD)"
+ echo refs/heads/master2 >expected &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expected actual
'
test_expect_success 'am --signoff does not add Signed-off-by: line if already there' '
git format-patch --stdout HEAD^ >patch3 &&
- sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2," patch3 >patch4
+ sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2] [foo," patch3 >patch4 &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout HEAD^ &&
git am --signoff patch4 &&
- test "$(git cat-file commit HEAD | grep -c "^Signed-off-by:")" -eq 1
+ git cat-file commit HEAD >actual &&
+ test $(grep -c "^Signed-off-by:" actual) -eq 1
'
test_expect_success 'am without --keep removes Re: and [PATCH] stuff' '
- test "$(git rev-parse HEAD)" = "$(git rev-parse master2)"
+ git rev-parse HEAD >expected &&
+ git rev-parse master2 >actual &&
+ test_cmp expected actual
'
test_expect_success 'am --keep really keeps the subject' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
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_path_is_missing .git/rebase-apply &&
+ git cat-file commit HEAD >actual &&
+ grep "Re: Re: Re: \[PATCH 1/5 v2\] \[foo\] third" actual
'
-test_expect_success 'am -3 falls back to 3-way merge' '
- git checkout -b lorem2 master2 &&
+test_expect_success 'am --keep-non-patch really keeps the non-patch part' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout HEAD^ &&
+ git am --keep-non-patch patch4 &&
+ test_path_is_missing .git/rebase-apply &&
+ git cat-file commit HEAD >actual &&
+ grep "^\[foo\] third" actual
+'
+
+test_expect_success 'setup am -3' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout -b base3way master2 &&
sed -n -e "3,\$p" msg >file &&
head -n 9 msg >>file &&
git add file &&
test_tick &&
- git commit -m "copied stuff" &&
+ git commit -m "copied stuff"
+'
+
+test_expect_success 'am -3 falls back to 3-way merge' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout -b lorem2 base3way &&
git am -3 lorem-move.patch &&
- ! test -d .git/rebase-apply &&
- test -z "$(git diff lorem)"
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code lorem
+'
+
+test_expect_success 'am -3 -p0 can read --no-prefix patch' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout -b lorem3 base3way &&
+ git am -3 -p0 lorem-zero.patch &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code lorem
+'
+
+test_expect_success 'am with config am.threeWay falls back to 3-way merge' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout -b lorem4 base3way &&
+ test_config am.threeWay 1 &&
+ git am lorem-move.patch &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code lorem
+'
+
+test_expect_success 'am with config am.threeWay overridden by --no-3way' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout -b lorem5 base3way &&
+ test_config am.threeWay 1 &&
+ test_must_fail git am --no-3way lorem-move.patch &&
+ test_path_is_dir .git/rebase-apply
+'
+
+test_expect_success 'am can rename a file' '
+ grep "^rename from" rename.patch &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout lorem^0 &&
+ git am rename.patch &&
+ test_path_is_missing .git/rebase-apply &&
+ git update-index --refresh &&
+ git diff --exit-code rename
+'
+
+test_expect_success 'am -3 can rename a file' '
+ grep "^rename from" rename.patch &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout lorem^0 &&
+ git am -3 rename.patch &&
+ test_path_is_missing .git/rebase-apply &&
+ git update-index --refresh &&
+ git diff --exit-code rename
+'
+
+test_expect_success 'am -3 can rename a file after falling back to 3-way merge' '
+ grep "^rename from" rename-add.patch &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout lorem^0 &&
+ git am -3 rename-add.patch &&
+ test_path_is_missing .git/rebase-apply &&
+ git update-index --refresh &&
+ git diff --exit-code rename
'
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 &&
+ rm -fr .git/rebase-apply &&
+ git checkout -f lorem2 &&
+ git reset base3way --hard &&
+ git am -3 -q lorem-move.patch >output.out 2>&1 &&
! test -s output.out
'
test_expect_success 'am pauses on conflict' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout lorem2^^ &&
test_must_fail git am lorem-move.patch &&
test -d .git/rebase-apply
'
test_expect_success 'am --skip works' '
+ echo goodbye >expected &&
git am --skip &&
- ! test -d .git/rebase-apply &&
- test -z "$(git diff lorem2^^ -- file)" &&
- test goodbye = "$(cat another)"
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code lorem2^^ -- file &&
+ test_cmp expected another
+'
+
+test_expect_success 'am --abort removes a stray directory' '
+ mkdir .git/rebase-apply &&
+ git am --abort &&
+ test_path_is_missing .git/rebase-apply
+'
+
+test_expect_success 'am refuses patches when paused' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout lorem2^^ &&
+
+ test_must_fail git am lorem-move.patch &&
+ test_path_is_dir .git/rebase-apply &&
+ test_cmp_rev lorem2^^ HEAD &&
+
+ test_must_fail git am <lorem-move.patch &&
+ test_path_is_dir .git/rebase-apply &&
+ test_cmp_rev lorem2^^ HEAD
'
test_expect_success 'am --resolved works' '
+ echo goodbye >expected &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
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_path_is_missing .git/rebase-apply &&
+ test_cmp expected another
+'
+
+test_expect_success 'am --resolved fails if index has no changes' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout lorem2^^ &&
+ test_must_fail git am lorem-move.patch &&
+ test_path_is_dir .git/rebase-apply &&
+ test_cmp_rev lorem2^^ HEAD &&
+ test_must_fail git am --resolved &&
+ test_path_is_dir .git/rebase-apply &&
+ test_cmp_rev lorem2^^ HEAD
+'
+
+test_expect_success 'am --resolved fails if index has unmerged entries' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout second &&
+ test_must_fail git am -3 lorem-move.patch &&
+ test_path_is_dir .git/rebase-apply &&
+ test_cmp_rev second HEAD &&
+ test_must_fail git am --resolved >err &&
+ test_path_is_dir .git/rebase-apply &&
+ test_cmp_rev second HEAD &&
+ test_i18ngrep "still have unmerged paths" err
'
test_expect_success 'am takes patches from a Pine mailbox' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
cat pine patch1 | git am &&
- ! test -d .git/rebase-apply &&
- test -z "$(git diff master^..HEAD)"
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code master^..HEAD
'
test_expect_success 'am fails on mail without patch' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
test_must_fail git am <failmail &&
- rm -r .git/rebase-apply/
+ git am --abort &&
+ test_path_is_missing .git/rebase-apply
'
test_expect_success 'am fails on empty patch' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
echo "---" >>failmail &&
test_must_fail git am <failmail &&
git am --skip &&
- ! test -d .git/rebase-apply
+ test_path_is_missing .git/rebase-apply
'
test_expect_success 'am works from stdin in subdirectory' '
rm -fr subdir &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
(
mkdir -p subdir &&
cd subdir &&
git am <../patch1
) &&
- test -z "$(git diff second)"
+ git diff --exit-code second
'
test_expect_success 'am works from file (relative path given) in subdirectory' '
rm -fr subdir &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
(
mkdir -p subdir &&
cd subdir &&
git am ../patch1
) &&
- test -z "$(git diff second)"
+ git diff --exit-code second
'
test_expect_success 'am works from file (absolute path given) in subdirectory' '
rm -fr subdir &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
P=$(pwd) &&
(
@@ -295,27 +749,31 @@ test_expect_success 'am works from file (absolute path given) in subdirectory' '
cd subdir &&
git am "$P/patch1"
) &&
- test -z "$(git diff second)"
+ git diff --exit-code second
'
test_expect_success 'am --committer-date-is-author-date' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
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"
+ sed -ne "/^author /s/.*> //p" head1 >at &&
+ sed -ne "/^committer /s/.*> //p" head1 >ct &&
+ test_cmp at ct
'
test_expect_success 'am without --committer-date-is-author-date' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
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"
+ sed -ne "/^author /s/.*> //p" head1 >at &&
+ sed -ne "/^committer /s/.*> //p" head1 >ct &&
+ ! test_cmp at ct
'
# This checks for +0000 because TZ is set to UTC and that should
@@ -323,42 +781,180 @@ test_expect_success 'am without --committer-date-is-author-date' '
# 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' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
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"
+ sed -ne "/^author /s/.*> //p" head1 >at &&
+ grep "+0000" at
'
test_expect_success 'am into an unborn branch' '
+ git rev-parse first^{tree} >expected &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
rm -fr subdir &&
- mkdir -p subdir &&
+ mkdir 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}
+ (
+ cd subdir &&
+ git rev-parse HEAD^{tree} >../actual
) &&
- test "z$result" = "z$(git rev-parse first^{tree})"
+ test_cmp expected actual
'
test_expect_success 'am newline in subject' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
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
+ sed -e "s/second/second \\\n foo/" patch1 >patchnl &&
+ git am <patchnl >output.out 2>&1 &&
+ test_i18ngrep "^Applying: second \\\n foo$" output.out
'
test_expect_success 'am -q is quiet' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
test_tick &&
- git am -q < patch1 > output.out 2>&1 &&
+ git am -q <patch1 >output.out 2>&1 &&
! test -s output.out
'
+test_expect_success 'am empty-file does not infloop' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ touch empty-file &&
+ test_tick &&
+ test_must_fail git am empty-file 2>actual &&
+ echo Patch format detection failed. >expected &&
+ test_i18ncmp expected actual
+'
+
+test_expect_success 'am --message-id really adds the message id' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout HEAD^ &&
+ git am --message-id patch1.eml &&
+ test_path_is_missing .git/rebase-apply &&
+ git cat-file commit HEAD | tail -n1 >actual &&
+ grep Message-Id patch1.eml >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'am.messageid really adds the message id' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout HEAD^ &&
+ test_config am.messageid true &&
+ git am patch1.eml &&
+ test_path_is_missing .git/rebase-apply &&
+ git cat-file commit HEAD | tail -n1 >actual &&
+ grep Message-Id patch1.eml >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'am --message-id -s signs off after the message id' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout HEAD^ &&
+ git am -s --message-id patch1.eml &&
+ test_path_is_missing .git/rebase-apply &&
+ git cat-file commit HEAD | tail -n2 | head -n1 >actual &&
+ grep Message-Id patch1.eml >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'am -3 works with rerere' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+
+ # make patches one->two and two->three...
+ test_commit one file &&
+ test_commit two file &&
+ test_commit three file &&
+ git format-patch -2 --stdout >seq.patch &&
+
+ # and create a situation that conflicts...
+ git reset --hard one &&
+ test_commit other file &&
+
+ # enable rerere...
+ test_config rerere.enabled true &&
+ test_when_finished "rm -rf .git/rr-cache" &&
+
+ # ...and apply. Our resolution is to skip the first
+ # patch, and the rerere the second one.
+ test_must_fail git am -3 seq.patch &&
+ test_must_fail git am --skip &&
+ echo resolved >file &&
+ git add file &&
+ git am --resolved &&
+
+ # now apply again, and confirm that rerere engaged (we still
+ # expect failure from am because rerere does not auto-commit
+ # for us).
+ git reset --hard other &&
+ test_must_fail git am -3 seq.patch &&
+ test_must_fail git am --skip &&
+ echo resolved >expect &&
+ test_cmp expect file
+'
+
+test_expect_success 'am -s unexpected trailer block' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ echo signed >file &&
+ git add file &&
+ cat >msg <<-EOF &&
+ subject here
+
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+ [jc: tweaked log message]
+ Signed-off-by: J C H <j@c.h>
+ EOF
+ git commit -F msg &&
+ git cat-file commit HEAD | sed -e '1,/^$/d' >original &&
+ git format-patch --stdout -1 >patch &&
+
+ git reset --hard HEAD^ &&
+ git am -s patch &&
+ (
+ cat original &&
+ echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+ ) >expect &&
+ git cat-file commit HEAD | sed -e '1,/^$/d' >actual &&
+ test_cmp expect actual &&
+
+ cat >msg <<-\EOF &&
+ subject here
+
+ We make sure that there is a blank line between the log
+ message proper and Signed-off-by: line added.
+ EOF
+ git reset HEAD^ &&
+ git commit -F msg file &&
+ git cat-file commit HEAD | sed -e '1,/^$/d' >original &&
+ git format-patch --stdout -1 >patch &&
+
+ git reset --hard HEAD^ &&
+ git am -s patch &&
+
+ (
+ cat original &&
+ echo &&
+ echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+ ) >expect &&
+ git cat-file commit HEAD | sed -e '1,/^$/d' >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh
index b55c411..9473c27 100755
--- a/t/t4151-am-abort.sh
+++ b/t/t4151-am-abort.sh
@@ -14,13 +14,14 @@ test_expect_success setup '
git add file-1 file-2 &&
git commit -m initial &&
git tag initial &&
+ git format-patch --stdout --root initial >initial.patch &&
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
+ git commit -a -m $i || return 1
done &&
git format-patch --no-numbered initial &&
git checkout -b side initial &&
@@ -45,8 +46,9 @@ do
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_i18ngrep "^Applying" output >output.applying &&
+ test_i18ngrep "^Applying: 6$" output.applying &&
+ test_i18ncmp file-2-expect file-2 &&
test ! -f .git/MERGE_RR
'
@@ -62,4 +64,132 @@ do
done
+test_expect_success 'am -3 --skip removes otherfile-4' '
+ git reset --hard initial &&
+ test_must_fail git am -3 0003-*.patch &&
+ test 3 -eq $(git ls-files -u | wc -l) &&
+ test 4 = "$(cat otherfile-4)" &&
+ git am --skip &&
+ test_cmp_rev initial HEAD &&
+ test -z "$(git ls-files -u)" &&
+ test_path_is_missing otherfile-4
+'
+
+test_expect_success 'am -3 --abort removes otherfile-4' '
+ git reset --hard initial &&
+ test_must_fail git am -3 0003-*.patch &&
+ test 3 -eq $(git ls-files -u | wc -l) &&
+ test 4 = "$(cat otherfile-4)" &&
+ git am --abort &&
+ test_cmp_rev initial HEAD &&
+ test -z "$(git ls-files -u)" &&
+ test_path_is_missing otherfile-4
+'
+
+test_expect_success 'am --abort will keep the local commits intact' '
+ test_must_fail git am 0004-*.patch &&
+ test_commit unrelated &&
+ git rev-parse HEAD >expect &&
+ git am --abort &&
+ git rev-parse HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'am --abort will keep dirty index intact' '
+ git reset --hard initial &&
+ echo dirtyfile >dirtyfile &&
+ cp dirtyfile dirtyfile.expected &&
+ git add dirtyfile &&
+ test_must_fail git am 0001-*.patch &&
+ test_cmp_rev initial HEAD &&
+ test_path_is_file dirtyfile &&
+ test_cmp dirtyfile.expected dirtyfile &&
+ git am --abort &&
+ test_cmp_rev initial HEAD &&
+ test_path_is_file dirtyfile &&
+ test_cmp dirtyfile.expected dirtyfile
+'
+
+test_expect_success 'am -3 stops on conflict on unborn branch' '
+ git checkout -f --orphan orphan &&
+ git reset &&
+ rm -f otherfile-4 &&
+ test_must_fail git am -3 0003-*.patch &&
+ test 2 -eq $(git ls-files -u | wc -l) &&
+ test 4 = "$(cat otherfile-4)"
+'
+
+test_expect_success 'am -3 --skip clears index on unborn branch' '
+ test_path_is_dir .git/rebase-apply &&
+ echo tmpfile >tmpfile &&
+ git add tmpfile &&
+ git am --skip &&
+ test -z "$(git ls-files)" &&
+ test_path_is_missing otherfile-4 &&
+ test_path_is_missing tmpfile
+'
+
+test_expect_success 'am -3 --abort removes otherfile-4 on unborn branch' '
+ git checkout -f --orphan orphan &&
+ git reset &&
+ rm -f otherfile-4 file-1 &&
+ test_must_fail git am -3 0003-*.patch &&
+ test 2 -eq $(git ls-files -u | wc -l) &&
+ test 4 = "$(cat otherfile-4)" &&
+ git am --abort &&
+ test -z "$(git ls-files -u)" &&
+ test_path_is_missing otherfile-4
+'
+
+test_expect_success 'am -3 --abort on unborn branch removes applied commits' '
+ git checkout -f --orphan orphan &&
+ git reset &&
+ rm -f otherfile-4 otherfile-2 file-1 file-2 &&
+ test_must_fail git am -3 initial.patch 0003-*.patch &&
+ test 3 -eq $(git ls-files -u | wc -l) &&
+ test 4 = "$(cat otherfile-4)" &&
+ git am --abort &&
+ test -z "$(git ls-files -u)" &&
+ test_path_is_missing otherfile-4 &&
+ test_path_is_missing file-1 &&
+ test_path_is_missing file-2 &&
+ test 0 -eq $(git log --oneline 2>/dev/null | wc -l) &&
+ test refs/heads/orphan = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success 'am --abort on unborn branch will keep local commits intact' '
+ git checkout -f --orphan orphan &&
+ git reset &&
+ test_must_fail git am 0004-*.patch &&
+ test_commit unrelated2 &&
+ git rev-parse HEAD >expect &&
+ git am --abort &&
+ git rev-parse HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'am --skip leaves index stat info alone' '
+ git checkout -f --orphan skip-stat-info &&
+ git reset &&
+ test_commit skip-should-be-untouched &&
+ test-chmtime =0 skip-should-be-untouched.t &&
+ git update-index --refresh &&
+ git diff-files --exit-code --quiet &&
+ test_must_fail git am 0001-*.patch &&
+ git am --skip &&
+ git diff-files --exit-code --quiet
+'
+
+test_expect_success 'am --abort leaves index stat info alone' '
+ git checkout -f --orphan abort-stat-info &&
+ git reset &&
+ test_commit abort-should-be-untouched &&
+ test-chmtime =0 abort-should-be-untouched.t &&
+ git update-index --refresh &&
+ git diff-files --exit-code --quiet &&
+ test_must_fail git am 0001-*.patch &&
+ git am --abort &&
+ git diff-files --exit-code --quiet
+'
+
test_done
diff --git a/t/t4152-am-subjects.sh b/t/t4152-am-subjects.sh
new file mode 100755
index 0000000..4c68245
--- /dev/null
+++ b/t/t4152-am-subjects.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+test_description='test subject preservation with format-patch | am'
+. ./test-lib.sh
+
+make_patches() {
+ type=$1
+ subject=$2
+ test_expect_success "create patches with $type subject" '
+ git reset --hard baseline &&
+ echo $type >file &&
+ git commit -a -m "$subject" &&
+ git format-patch -1 --stdout >$type.patch &&
+ git format-patch -1 --stdout -k >$type-k.patch
+ '
+}
+
+check_subject() {
+ git reset --hard baseline &&
+ git am $2 $1.patch &&
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'setup baseline commit' '
+ test_commit baseline file
+'
+
+SHORT_SUBJECT='short subject'
+make_patches short "$SHORT_SUBJECT"
+
+LONG_SUBJECT1='this is a long subject that is virtually guaranteed'
+LONG_SUBJECT2='to require wrapping via format-patch if it is all'
+LONG_SUBJECT3='going to appear on a single line'
+LONG_SUBJECT="$LONG_SUBJECT1 $LONG_SUBJECT2 $LONG_SUBJECT3"
+make_patches long "$LONG_SUBJECT"
+
+MULTILINE_SUBJECT="$LONG_SUBJECT1
+$LONG_SUBJECT2
+$LONG_SUBJECT3"
+make_patches multiline "$MULTILINE_SUBJECT"
+
+echo "$SHORT_SUBJECT" >expect
+test_expect_success 'short subject preserved (format-patch | am)' '
+ check_subject short
+'
+test_expect_success 'short subject preserved (format-patch -k | am)' '
+ check_subject short-k
+'
+test_expect_success 'short subject preserved (format-patch -k | am -k)' '
+ check_subject short-k -k
+'
+
+echo "$LONG_SUBJECT" >expect
+test_expect_success 'long subject preserved (format-patch | am)' '
+ check_subject long
+'
+test_expect_success 'long subject preserved (format-patch -k | am)' '
+ check_subject long-k
+'
+test_expect_success 'long subject preserved (format-patch -k | am -k)' '
+ check_subject long-k -k
+'
+
+echo "$LONG_SUBJECT" >expect
+test_expect_success 'multiline subject unwrapped (format-patch | am)' '
+ check_subject multiline
+'
+test_expect_success 'multiline subject unwrapped (format-patch -k | am)' '
+ check_subject multiline-k
+'
+echo "$MULTILINE_SUBJECT" >expect
+test_expect_success 'multiline subject preserved (format-patch -k | am -k)' '
+ check_subject multiline-k -k
+'
+
+test_done
diff --git a/t/t4153-am-resume-override-opts.sh b/t/t4153-am-resume-override-opts.sh
new file mode 100755
index 0000000..7c013d8
--- /dev/null
+++ b/t/t4153-am-resume-override-opts.sh
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+test_description='git-am command-line options override saved options'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
+
+format_patch () {
+ git format-patch --stdout -1 "$1" >"$1".eml
+}
+
+test_expect_success 'setup' '
+ test_commit initial file &&
+ test_commit first file &&
+
+ git checkout initial &&
+ git mv file file2 &&
+ test_tick &&
+ git commit -m renamed-file &&
+ git tag renamed-file &&
+
+ git checkout -b side initial &&
+ test_commit side1 file &&
+ test_commit side2 file &&
+
+ format_patch side1 &&
+ format_patch side2
+'
+
+test_expect_success TTY '--3way overrides --no-3way' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout renamed-file &&
+
+ # Applying side1 will fail as the file has been renamed.
+ test_must_fail git am --no-3way side[12].eml &&
+ test_path_is_dir .git/rebase-apply &&
+ test_cmp_rev renamed-file HEAD &&
+ test -z "$(git ls-files -u)" &&
+
+ # Applying side1 with am --3way will succeed due to the threeway-merge.
+ # Applying side2 will fail as --3way does not apply to it.
+ test_must_fail test_terminal git am --3way </dev/zero &&
+ test_path_is_dir .git/rebase-apply &&
+ test side1 = "$(cat file2)"
+'
+
+test_expect_success '--no-quiet overrides --quiet' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout first &&
+
+ # Applying side1 will be quiet.
+ test_must_fail git am --quiet side[123].eml >out &&
+ test_path_is_dir .git/rebase-apply &&
+ ! test_i18ngrep "^Applying: " out &&
+ echo side1 >file &&
+ git add file &&
+
+ # Applying side1 will not be quiet.
+ # Applying side2 will be quiet.
+ git am --no-quiet --continue >out &&
+ echo "Applying: side1" >expected &&
+ test_i18ncmp expected out
+'
+
+test_expect_success '--signoff overrides --no-signoff' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout first &&
+
+ test_must_fail git am --no-signoff side[12].eml &&
+ test_path_is_dir .git/rebase-apply &&
+ echo side1 >file &&
+ git add file &&
+ git am --signoff --continue &&
+
+ # Applied side1 will be signed off
+ echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected &&
+ git cat-file commit HEAD^ | grep "Signed-off-by:" >actual &&
+ test_cmp expected actual &&
+
+ # Applied side2 will not be signed off
+ test $(git cat-file commit HEAD | grep -c "Signed-off-by:") -eq 0
+'
+
+test_expect_success TTY '--reject overrides --no-reject' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout first &&
+ rm -f file.rej &&
+
+ test_must_fail git am --no-reject side1.eml &&
+ test_path_is_dir .git/rebase-apply &&
+ test_path_is_missing file.rej &&
+
+ test_must_fail test_terminal git am --reject </dev/zero &&
+ test_path_is_dir .git/rebase-apply &&
+ test_path_is_file file.rej
+'
+
+test_done
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index 70856d0..1a080e7 100755
--- a/t/t4200-rerere.sh
+++ b/t/t4200-rerere.sh
@@ -4,237 +4,559 @@
#
test_description='git rerere
+
+! [fifth] version1
+ ! [first] first
+ ! [fourth] version1
+ ! [master] initial
+ ! [second] prefer first over second
+ ! [third] version2
+------
+ + [third] version2
++ [fifth] version1
+ + [fourth] version1
++ + + [third^] third
+ - [second] prefer first over second
+ + + [first] first
+ + [second^] second
+++++++ [master] initial
'
. ./test-lib.sh
-test_expect_success 'setup' "
- cat > a1 <<- EOF &&
+test_expect_success 'setup' '
+ cat >a1 <<-\EOF &&
Some title
==========
- Whether 'tis nobler in the mind to suffer
+ 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.
+ That flesh is heir to, '\''tis a consummation
+ Devoutly to be wish'\''d.
EOF
git add a1 &&
+ test_tick &&
git commit -q -a -m initial &&
- git checkout -b first &&
- cat >> a1 <<- EOF &&
+ cat >>a1 <<-\EOF &&
Some title
==========
To die, to sleep;
- To sleep: perchance to dream: ay, there's the rub;
+ 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
+ Must give us pause: there'\''s the respect
That makes calamity of so long life;
EOF
+
+ git checkout -b first &&
+ test_tick &&
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 &&
+ sed -e "s/To die, t/To die! T/" -e "s/Some title/Some Title/" >a1 &&
+ echo "* END *" >>a1 &&
+ test_tick &&
git commit -q -a -m second
-"
+'
test_expect_success 'nothing recorded without rerere' '
- (rm -rf .git/rr-cache; git config rerere.enabled false) &&
+ 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' '
+test_expect_success 'activate rerere, old style (conflicting merge)' '
git reset --hard &&
mkdir .git/rr-cache &&
- git config --unset rerere.enabled &&
- test_must_fail git merge first
-'
+ test_might_fail 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"
+ sha1=$(perl -pe "s/ .*//" .git/MERGE_RR) &&
+ rr=.git/rr-cache/$sha1 &&
+ grep "^=======\$" $rr/preimage &&
+ ! test -f $rr/postimage &&
+ ! test -f $rr/thisimage
+'
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 &&
+
+ sha1=$(perl -pe "s/ .*//" .git/MERGE_RR) &&
+ rr=.git/rr-cache/$sha1 &&
grep ^=======$ $rr/preimage
'
-test_expect_success 'no postimage or thisimage yet' \
- "test ! -f $rr/postimage -a ! -f $rr/thisimage"
+test_expect_success 'set up rr-cache' '
+ rm -rf .git/rr-cache &&
+ git config rerere.enabled true &&
+ git reset --hard &&
+ test_must_fail git merge first &&
+ sha1=$(perl -pe "s/ .*//" .git/MERGE_RR) &&
+ rr=.git/rr-cache/$sha1
+'
-test_expect_success 'preimage has right number of lines' '
+test_expect_success 'rr-cache looks sane' '
+ # no postimage or thisimage yet
+ ! test -f $rr/postimage &&
+ ! test -f $rr/thisimage &&
+ # preimage has right number of lines
cnt=$(sed -ne "/^<<<<<<</,/^>>>>>>>/p" $rr/preimage | wc -l) &&
+ echo $cnt &&
test $cnt = 13
+'
+test_expect_success 'rerere diff' '
+ 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_cmp expect out
'
-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
+test_expect_success 'rerere status' '
+ echo a1 >expect &&
+ git rerere status >out &&
+ test_cmp expect out
'
-git show first:a1 | sed 's/To die: t/To die! T/' > expect
-test_expect_success 'rerere kicked in' "! grep ^=======$ a1"
+test_expect_success 'first postimage wins' '
+ git show first:a1 | sed "s/To die: t/To die! T/" >expect &&
-test_expect_success 'rerere prefers first change' 'test_cmp a1 expect'
+ git commit -q -a -m "prefer first over second" &&
+ test -f $rr/postimage &&
-rm $rr/postimage
-echo "$sha1 a1" | perl -pe 'y/\012/\000/' > .git/MERGE_RR
+ oldmtimepost=$(test-chmtime -v -60 $rr/postimage | cut -f 1) &&
-test_expect_success 'rerere clear' 'git rerere clear'
+ 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_expect_success 'clear removed the directory' "test ! -d $rr"
+ test_must_fail git merge first &&
+ # rerere kicked in
+ ! grep "^=======\$" a1 &&
+ test_cmp expect a1
+'
-mkdir $rr
-echo Hello > $rr/preimage
-echo World > $rr/postimage
+test_expect_success 'rerere updates postimage timestamp' '
+ newmtimepost=$(test-chmtime -v +0 $rr/postimage | cut -f 1) &&
+ test $oldmtimepost -lt $newmtimepost
+'
+
+test_expect_success 'rerere clear' '
+ mv $rr/postimage .git/post-saved &&
+ echo "$sha1 a1" | perl -pe "y/\012/\000/" >.git/MERGE_RR &&
+ git rerere clear &&
+ ! test -d $rr
+'
-sha2=4000000000000000000000000000000000000000
-rr2=.git/rr-cache/$sha2
-mkdir $rr2
-echo Hello > $rr2/preimage
+test_expect_success 'leftover directory' '
+ git reset --hard &&
+ mkdir -p $rr &&
+ test_must_fail git merge first &&
+ test -f $rr/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_expect_success 'missing preimage' '
+ git reset --hard &&
+ mkdir -p $rr &&
+ cp .git/post-saved $rr/postimage &&
+ test_must_fail git merge first &&
+ test -f $rr/preimage
+'
-test-chmtime =$almost_60_days_ago $rr/preimage
-test-chmtime =$almost_15_days_ago $rr2/preimage
+test_expect_success 'set up for garbage collection tests' '
+ mkdir -p $rr &&
+ echo Hello >$rr/preimage &&
+ echo World >$rr/postimage &&
-test_expect_success 'garbage collection (part1)' 'git rerere gc'
+ sha2=4000000000000000000000000000000000000000 &&
+ rr2=.git/rr-cache/$sha2 &&
+ mkdir $rr2 &&
+ echo Hello >$rr2/preimage &&
-test_expect_success 'young records still live' \
- "test -f $rr/preimage && test -f $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 =$just_over_60_days_ago $rr/preimage
-test-chmtime =$just_over_15_days_ago $rr2/preimage
+ test-chmtime =$just_over_60_days_ago $rr/preimage &&
+ test-chmtime =$almost_60_days_ago $rr/postimage &&
+ test-chmtime =$almost_15_days_ago $rr2/preimage
+'
-test_expect_success 'garbage collection (part2)' 'git rerere gc'
+test_expect_success 'gc preserves young or recently used records' '
+ git rerere gc &&
+ test -f $rr/preimage &&
+ test -f $rr2/preimage
+'
-test_expect_success 'old records rest in peace' \
- "test ! -f $rr/preimage && test ! -f $rr2/preimage"
+test_expect_success 'old records rest in peace' '
+ test-chmtime =$just_over_60_days_ago $rr/postimage &&
+ test-chmtime =$just_over_15_days_ago $rr2/preimage &&
+ git rerere gc &&
+ ! test -f $rr/preimage &&
+ ! test -f $rr2/preimage
+'
-test_expect_success 'file2 added differently in two branches' '
+test_expect_success 'setup: file2 added differently in two branches' '
git reset --hard &&
+
git checkout -b fourth &&
- echo Hallo > file2 &&
+ echo Hallo >file2 &&
git add file2 &&
+ test_tick &&
git commit -m version1 &&
+
git checkout third &&
- echo Bello > file2 &&
+ echo Bello >file2 &&
git add file2 &&
+ test_tick &&
git commit -m version2 &&
+
test_must_fail git merge fourth &&
- echo Cello > file2 &&
+ echo Cello >file2 &&
git add file2 &&
git commit -m resolution
'
test_expect_success 'resolution was recorded properly' '
+ echo Cello >expected &&
+
git reset --hard HEAD~2 &&
git checkout -b fifth &&
- echo Hallo > file3 &&
+
+ echo Hallo >file3 &&
git add file3 &&
+ test_tick &&
git commit -m version1 &&
+
git checkout third &&
- echo Bello > file3 &&
+ echo Bello >file3 &&
git add file3 &&
+ test_tick &&
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_cmp expected file3 &&
+ test_must_fail git update-index --refresh
'
test_expect_success 'rerere.autoupdate' '
- git config rerere.autoupdate true
+ 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)
+ git update-index --refresh
'
test_expect_success 'merge --rerere-autoupdate' '
- git config --unset rerere.autoupdate
+ test_might_fail 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)
+ git update-index --refresh
'
test_expect_success 'merge --no-rerere-autoupdate' '
- git config rerere.autoupdate true
+ headblob=$(git rev-parse version2:file3) &&
+ mergeblob=$(git rev-parse fifth:file3) &&
+ cat >expected <<-EOF &&
+ 100644 $headblob 2 file3
+ 100644 $mergeblob 3 file3
+ EOF
+
+ 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)
+ git ls-files -u >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'set up an unresolved merge' '
+ headblob=$(git rev-parse version2:file3) &&
+ mergeblob=$(git rev-parse fifth:file3) &&
+ cat >expected.unresolved <<-EOF &&
+ 100644 $headblob 2 file3
+ 100644 $mergeblob 3 file3
+ EOF
+
+ test_might_fail git config --unset rerere.autoupdate &&
+ git reset --hard &&
+ git checkout version2 &&
+ fifth=$(git rev-parse fifth) &&
+ echo "$fifth branch 'fifth' of ." |
+ git fmt-merge-msg >msg &&
+ ancestor=$(git merge-base version2 fifth) &&
+ test_must_fail git merge-recursive "$ancestor" -- HEAD fifth &&
+
+ git ls-files --stage >failedmerge &&
+ cp file3 file3.conflict &&
+
+ git ls-files -u >actual &&
+ test_cmp expected.unresolved actual
+'
+
+test_expect_success 'explicit rerere' '
+ test_might_fail git config --unset rerere.autoupdate &&
+ git rm -fr --cached . &&
+ git update-index --index-info <failedmerge &&
+ cp file3.conflict file3 &&
+ test_must_fail git update-index --refresh -q &&
+
+ git rerere &&
+ git ls-files -u >actual &&
+ test_cmp expected.unresolved actual
+'
+
+test_expect_success 'explicit rerere with autoupdate' '
+ git config rerere.autoupdate true &&
+ git rm -fr --cached . &&
+ git update-index --index-info <failedmerge &&
+ cp file3.conflict file3 &&
+ test_must_fail git update-index --refresh -q &&
+
+ git rerere &&
+ git update-index --refresh
+'
+
+test_expect_success 'explicit rerere --rerere-autoupdate overrides' '
+ git config rerere.autoupdate false &&
+ git rm -fr --cached . &&
+ git update-index --index-info <failedmerge &&
+ cp file3.conflict file3 &&
+ git rerere &&
+ git ls-files -u >actual1 &&
+
+ git rm -fr --cached . &&
+ git update-index --index-info <failedmerge &&
+ cp file3.conflict file3 &&
+ git rerere --rerere-autoupdate &&
+ git update-index --refresh &&
+
+ git rm -fr --cached . &&
+ git update-index --index-info <failedmerge &&
+ cp file3.conflict file3 &&
+ git rerere --rerere-autoupdate --no-rerere-autoupdate &&
+ git ls-files -u >actual2 &&
+
+ git rm -fr --cached . &&
+ git update-index --index-info <failedmerge &&
+ cp file3.conflict file3 &&
+ git rerere --rerere-autoupdate --no-rerere-autoupdate --rerere-autoupdate &&
+ git update-index --refresh &&
+
+ test_cmp expected.unresolved actual1 &&
+ test_cmp expected.unresolved actual2
+'
+
+test_expect_success 'rerere --no-no-rerere-autoupdate' '
+ git rm -fr --cached . &&
+ git update-index --index-info <failedmerge &&
+ cp file3.conflict file3 &&
+ test_must_fail git rerere --no-no-rerere-autoupdate 2>err &&
+ test_i18ngrep [Uu]sage err &&
+ test_must_fail git update-index --refresh
+'
+
+test_expect_success 'rerere -h' '
+ test_must_fail git rerere -h >help &&
+ test_i18ngrep [Uu]sage help
+'
+
+concat_insert () {
+ last=$1
+ shift
+ cat early && printf "%s\n" "$@" && cat late "$last"
+}
+
+count_pre_post () {
+ find .git/rr-cache/ -type f -name "preimage*" >actual &&
+ test_line_count = "$1" actual &&
+ find .git/rr-cache/ -type f -name "postimage*" >actual &&
+ test_line_count = "$2" actual
+}
+
+test_expect_success 'rerere gc' '
+ find .git/rr-cache -type f >original &&
+ xargs test-chmtime -172800 <original &&
+
+ git -c gc.rerereresolved=5 -c gc.rerereunresolved=5 rerere gc &&
+ find .git/rr-cache -type f >actual &&
+ test_cmp original actual &&
+
+ git -c gc.rerereresolved=5 -c gc.rerereunresolved=0 rerere gc &&
+ find .git/rr-cache -type f >actual &&
+ test_cmp original actual &&
+
+ git -c gc.rerereresolved=0 -c gc.rerereunresolved=0 rerere gc &&
+ find .git/rr-cache -type f >actual &&
+ >expect &&
+ test_cmp expect actual
+'
+
+merge_conflict_resolve () {
+ git reset --hard &&
+ test_must_fail git merge six.1 &&
+ # Resolution is to replace 7 with 6.1 and 6.2 (i.e. take both)
+ concat_insert short 6.1 6.2 >file1 &&
+ concat_insert long 6.1 6.2 >file2
+}
+
+test_expect_success 'multiple identical conflicts' '
+ git reset --hard &&
+
+ test_seq 1 6 >early &&
+ >late &&
+ test_seq 11 15 >short &&
+ test_seq 111 120 >long &&
+ concat_insert short >file1 &&
+ concat_insert long >file2 &&
+ git add file1 file2 &&
+ git commit -m base &&
+ git tag base &&
+ git checkout -b six.1 &&
+ concat_insert short 6.1 >file1 &&
+ concat_insert long 6.1 >file2 &&
+ git add file1 file2 &&
+ git commit -m 6.1 &&
+ git checkout -b six.2 HEAD^ &&
+ concat_insert short 6.2 >file1 &&
+ concat_insert long 6.2 >file2 &&
+ git add file1 file2 &&
+ git commit -m 6.2 &&
+
+ # At this point, six.1 and six.2
+ # - derive from common ancestor that has two files
+ # 1...6 7 11..15 (file1) and 1...6 7 111..120 (file2)
+ # - six.1 replaces these 7s with 6.1
+ # - six.2 replaces these 7s with 6.2
+
+ merge_conflict_resolve &&
+
+ # Check that rerere knows that file1 and file2 have conflicts
+
+ printf "%s\n" file1 file2 >expect &&
+ git ls-files -u | sed -e "s/^.* //" | sort -u >actual &&
+ test_cmp expect actual &&
+
+ git rerere status | sort >actual &&
+ test_cmp expect actual &&
+
+ git rerere remaining >actual &&
+ test_cmp expect actual &&
+
+ count_pre_post 2 0 &&
+
+ # Pretend that the conflicts were made quite some time ago
+ find .git/rr-cache/ -type f | xargs test-chmtime -172800 &&
+
+ # Unresolved entries have not expired yet
+ git -c gc.rerereresolved=5 -c gc.rerereunresolved=5 rerere gc &&
+ count_pre_post 2 0 &&
+
+ # Unresolved entries have expired
+ git -c gc.rerereresolved=5 -c gc.rerereunresolved=1 rerere gc &&
+ count_pre_post 0 0 &&
+
+ # Recreate the conflicted state
+ merge_conflict_resolve &&
+ count_pre_post 2 0 &&
+
+ # Clear it
+ git rerere clear &&
+ count_pre_post 0 0 &&
+
+ # Recreate the conflicted state
+ merge_conflict_resolve &&
+ count_pre_post 2 0 &&
+
+ # We resolved file1 and file2
+ git rerere &&
+ >expect &&
+ git rerere remaining >actual &&
+ test_cmp expect actual &&
+
+ # We must have recorded both of them
+ count_pre_post 2 2 &&
+
+ # Now we should be able to resolve them both
+ git reset --hard &&
+ test_must_fail git merge six.1 &&
+ git rerere &&
+
+ >expect &&
+ git rerere remaining >actual &&
+ test_cmp expect actual &&
+
+ concat_insert short 6.1 6.2 >file1.expect &&
+ concat_insert long 6.1 6.2 >file2.expect &&
+ test_cmp file1.expect file1 &&
+ test_cmp file2.expect file2 &&
+
+ # Forget resolution for file2
+ git rerere forget file2 &&
+ echo file2 >expect &&
+ git rerere status >actual &&
+ test_cmp expect actual &&
+ count_pre_post 2 1 &&
+
+ # file2 already has correct resolution, so record it again
+ git rerere &&
+
+ # Pretend that the resolutions are old again
+ find .git/rr-cache/ -type f | xargs test-chmtime -172800 &&
+
+ # Resolved entries have not expired yet
+ git -c gc.rerereresolved=5 -c gc.rerereunresolved=5 rerere gc &&
+
+ count_pre_post 2 2 &&
+
+ # Resolved entries have expired
+ git -c gc.rerereresolved=1 -c gc.rerereunresolved=5 rerere gc &&
+ count_pre_post 0 0
'
test_done
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
index cdb70b4..a977365 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -35,7 +35,7 @@ test_expect_success 'setup' '
tr 1234 "\370\235\204\236")" a1 &&
echo 5 >a1 &&
- git commit --quiet -m "a 12 34 56 78" a1
+ git commit --quiet -m "a 12 34 56 78" a1 &&
echo 6 >a1 &&
git commit --quiet -m "Commit by someone else" \
@@ -93,7 +93,7 @@ test_expect_success 'output from user-defined format is re-wrapped' '
test_cmp expect log.predictable
'
-test_expect_success 'shortlog wrapping' '
+test_expect_success !MINGW 'shortlog wrapping' '
cat >expect <<\EOF &&
A U Thor (5):
Test
@@ -114,12 +114,42 @@ EOF
test_cmp expect out
'
-test_expect_success 'shortlog from non-git directory' '
- git log HEAD >log &&
+test_expect_success !MINGW 'shortlog from non-git directory' '
+ git log --no-expand-tabs HEAD >log &&
GIT_DIR=non-existing git shortlog -w <log >out &&
test_cmp expect out
'
+test_expect_success !MINGW 'shortlog can read --format=raw output' '
+ git log --format=raw HEAD >log &&
+ GIT_DIR=non-existing git shortlog -w <log >out &&
+ test_cmp expect out
+'
+
+test_expect_success 'shortlog should add newline when input line matches wraplen' '
+ cat >expect <<\EOF &&
+A U Thor (2):
+ bbbbbbbbbbbbbbbbbb: bbbbbbbb bbb bbbb bbbbbbb bb bbbb bbb bbbbb bbbbbb
+ aaaaaaaaaaaaaaaaaaaaaa: aaaaaa aaaaaaaaaa aaaa aaaaaaaa aa aaaa aa aaa
+
+EOF
+ git shortlog -w >out <<\EOF &&
+commit 0000000000000000000000000000000000000001
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:14:13 2005 -0700
+
+ aaaaaaaaaaaaaaaaaaaaaa: aaaaaa aaaaaaaaaa aaaa aaaaaaaa aa aaaa aa aaa
+
+commit 0000000000000000000000000000000000000002
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:14:13 2005 -0700
+
+ bbbbbbbbbbbbbbbbbb: bbbbbbbb bbb bbbb bbbbbbb bb bbbb bbb bbbbb bbbbbb
+
+EOF
+ test_cmp expect out
+'
+
iconvfromutf8toiso88591() {
printf "%s" "$*" | iconv -f UTF-8 -t ISO8859-1
}
@@ -135,7 +165,7 @@ $DSCHO (2):
EOF
-test_expect_success 'shortlog encoding' '
+test_expect_success !MINGW 'shortlog encoding' '
git reset --hard "$commit" &&
git config --unset i18n.commitencoding &&
echo 2 > a1 &&
@@ -148,4 +178,10 @@ test_expect_success 'shortlog encoding' '
git shortlog HEAD~2.. > out &&
test_cmp expect out'
+test_expect_success 'shortlog with revision pseudo options' '
+ git shortlog --all &&
+ git shortlog --branches &&
+ git shortlog --exclude=refs/heads/m* --all
+'
+
test_done
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 2230e60..128ba93 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -3,6 +3,7 @@
test_description='git log'
. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
test_expect_success setup '
@@ -72,9 +73,9 @@ cat > expect << EOF
commit.
EOF
-test_expect_success 'format %w(12,1,2)' '
+test_expect_success 'format %w(11,1,2)' '
- git log -2 --format="%w(12,1,2)This is the %s commit." > actual &&
+ git log -2 --format="%w(11,1,2)This is the %s commit." > actual &&
test_cmp expect actual
'
@@ -100,13 +101,11 @@ test_expect_success 'oneline' '
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
- }
+ git log --no-renames --pretty="format:%s" --diff-filter=A HEAD > actual &&
+ git log --no-renames --pretty="format:%s" --diff-filter A HEAD > actual-separate &&
+ printf "fifth\nfourth\nthird\ninitial" > expect &&
+ test_cmp expect actual &&
+ test_cmp expect actual-separate
'
@@ -114,23 +113,15 @@ 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
- }
+ verbose test "$actual" = "$expect"
'
test_expect_success 'diff-filter=D' '
- actual=$(git log --pretty="format:%s" --diff-filter=D HEAD) &&
+ actual=$(git log --no-renames --pretty="format:%s" --diff-filter=D HEAD) &&
expect=$(echo sixth ; echo third) &&
- test "$actual" = "$expect" || {
- echo Oops
- echo "Actual: $actual"
- false
- }
+ verbose test "$actual" = "$expect"
'
@@ -138,11 +129,7 @@ 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
- }
+ verbose test "$actual" = "$expect"
'
@@ -150,11 +137,7 @@ 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
- }
+ verbose test "$actual" = "$expect"
'
@@ -162,12 +145,31 @@ 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
- }
+ verbose test "$actual" = "$expect"
+'
+
+test_expect_success 'git config log.follow works like --follow' '
+ test_config log.follow true &&
+ actual=$(git log --pretty="format:%s" ichi) &&
+ expect=$(echo third ; echo second ; echo initial) &&
+ verbose test "$actual" = "$expect"
+'
+
+test_expect_success 'git config log.follow does not die with multiple paths' '
+ test_config log.follow true &&
+ git log --pretty="format:%s" ichi ein
+'
+
+test_expect_success 'git config log.follow does not die with no paths' '
+ test_config log.follow true &&
+ git log --
+'
+test_expect_success 'git config log.follow is overridden by --no-follow' '
+ test_config log.follow true &&
+ actual=$(git log --no-follow --pretty="format:%s" ichi) &&
+ expect="third" &&
+ verbose test "$actual" = "$expect"
'
cat > expect << EOF
@@ -180,11 +182,21 @@ test_expect_success 'git log --no-walk <commits> sorts by commit time' '
test_cmp expect actual
'
+test_expect_success 'git log --no-walk=sorted <commits> sorts by commit time' '
+ git log --no-walk=sorted --oneline 5d31159 804a787 394ef78 > actual &&
+ test_cmp expect actual
+'
+
cat > expect << EOF
5d31159 fourth
804a787 sixth
394ef78 fifth
EOF
+test_expect_success 'git log --no-walk=unsorted <commits> leaves list of commits as given' '
+ git log --no-walk=unsorted --oneline 5d31159 804a787 394ef78 > actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'git show <commits> leaves list of commits as given' '
git show --oneline -s 5d31159 804a787 394ef78 > actual &&
test_cmp expect actual
@@ -193,7 +205,7 @@ test_expect_success 'git show <commits> leaves list of commits as given' '
test_expect_success 'setup case sensitivity tests' '
echo case >one &&
test_tick &&
- git add one
+ git add one &&
git commit -a -m Second
'
@@ -203,6 +215,28 @@ test_expect_success 'log --grep' '
test_cmp expect actual
'
+cat > expect << EOF
+second
+initial
+EOF
+test_expect_success 'log --invert-grep --grep' '
+ git log --pretty="tformat:%s" --invert-grep --grep=th --grep=Sec >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --invert-grep --grep -i' '
+ echo initial >expect &&
+ git log --pretty="tformat:%s" --invert-grep -i --grep=th --grep=Sec >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --grep option parsing' '
+ echo second >expect &&
+ git log -1 --pretty="tformat:%s" --grep sec >actual &&
+ test_cmp expect actual &&
+ test_must_fail git log -1 --pretty="tformat:%s" --grep
+'
+
test_expect_success 'log -i --grep' '
echo Second >expect &&
git log -1 --pretty="tformat:%s" -i --grep=sec >actual &&
@@ -215,6 +249,12 @@ test_expect_success 'log --grep -i' '
test_cmp expect actual
'
+test_expect_success 'log -F -E --grep=<ere> uses ere' '
+ echo second >expect &&
+ git log -1 --pretty="tformat:%s" -F -E --grep=s.c.nd >actual &&
+ test_cmp expect actual
+'
+
cat > expect <<EOF
* Second
* sixth
@@ -259,6 +299,16 @@ test_expect_success 'log --graph with merge' '
test_cmp expect actual
'
+test_expect_success 'log --raw --graph -m with merge' '
+ git log --raw --graph --oneline -m master | head -n 500 >actual &&
+ grep "initial" actual
+'
+
+test_expect_success 'diff-tree --graph' '
+ git diff-tree --graph master^ | head -n 500 >actual &&
+ grep "one" actual
+'
+
cat > expect <<\EOF
* commit master
|\ Merge: A B
@@ -336,16 +386,16 @@ test_expect_success 'set up more tangled history' '
test_commit octopus-b &&
git checkout master &&
test_commit seventh &&
- git merge octopus-a octopus-b
+ git merge octopus-a octopus-b &&
git merge reach
'
cat > expect <<\EOF
-* Merge commit 'reach'
+* Merge tag 'reach'
|\
| \
| \
-*-. \ Merge commit 'octopus-a'; commit 'octopus-b'
+*-. \ Merge tags 'octopus-a' and 'octopus-b'
|\ \ \
* | | | seventh
| | * | octopus-b
@@ -388,8 +438,6 @@ test_expect_success 'log --graph with merge' '
'
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 &&
@@ -398,8 +446,7 @@ test_expect_success 'log.decorate configuration' '
git log --oneline >actual &&
test_cmp expect.short actual &&
- git config --unset-all log.decorate &&
- git config log.decorate true &&
+ test_config log.decorate true &&
git log --oneline >actual &&
test_cmp expect.short actual &&
git log --oneline --decorate=full >actual &&
@@ -407,8 +454,7 @@ test_expect_success 'log.decorate configuration' '
git log --oneline --decorate=no >actual &&
test_cmp expect.none actual &&
- git config --unset-all log.decorate &&
- git config log.decorate no &&
+ test_config log.decorate no &&
git log --oneline >actual &&
test_cmp expect.none actual &&
git log --oneline --decorate >actual &&
@@ -416,8 +462,15 @@ test_expect_success 'log.decorate configuration' '
git log --oneline --decorate=full >actual &&
test_cmp expect.full actual &&
- git config --unset-all log.decorate &&
- git config log.decorate short &&
+ test_config log.decorate 1 &&
+ 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 &&
+
+ test_config log.decorate short &&
git log --oneline >actual &&
test_cmp expect.short actual &&
git log --oneline --no-decorate >actual &&
@@ -425,16 +478,463 @@ test_expect_success 'log.decorate configuration' '
git log --oneline --decorate=full >actual &&
test_cmp expect.full actual &&
- git config --unset-all log.decorate &&
- git config log.decorate full &&
+ test_config log.decorate full &&
git log --oneline >actual &&
test_cmp expect.full actual &&
git log --oneline --no-decorate >actual &&
test_cmp expect.none actual &&
git log --oneline --decorate >actual &&
- test_cmp expect.short actual
+ test_cmp expect.short actual &&
+
+ test_unconfig log.decorate &&
+ git log --pretty=raw >expect.raw &&
+ test_config log.decorate full &&
+ git log --pretty=raw >actual &&
+ test_cmp expect.raw actual
'
-test_done
+test_expect_success 'reflog is expected format' '
+ git log -g --abbrev-commit --pretty=oneline >expect &&
+ git reflog >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'whatchanged is expected format' '
+ git log --no-merges --raw >expect &&
+ git whatchanged >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log.abbrevCommit configuration' '
+ git log --abbrev-commit >expect.log.abbrev &&
+ git log --no-abbrev-commit >expect.log.full &&
+ git log --pretty=raw >expect.log.raw &&
+ git reflog --abbrev-commit >expect.reflog.abbrev &&
+ git reflog --no-abbrev-commit >expect.reflog.full &&
+ git whatchanged --abbrev-commit >expect.whatchanged.abbrev &&
+ git whatchanged --no-abbrev-commit >expect.whatchanged.full &&
+
+ test_config log.abbrevCommit true &&
+
+ git log >actual &&
+ test_cmp expect.log.abbrev actual &&
+ git log --no-abbrev-commit >actual &&
+ test_cmp expect.log.full actual &&
+
+ git log --pretty=raw >actual &&
+ test_cmp expect.log.raw actual &&
+
+ git reflog >actual &&
+ test_cmp expect.reflog.abbrev actual &&
+ git reflog --no-abbrev-commit >actual &&
+ test_cmp expect.reflog.full actual &&
+
+ git whatchanged >actual &&
+ test_cmp expect.whatchanged.abbrev actual &&
+ git whatchanged --no-abbrev-commit >actual &&
+ test_cmp expect.whatchanged.full actual
+'
+
+test_expect_success 'show added path under "--follow -M"' '
+ # This tests for a regression introduced in v1.7.2-rc0~103^2~2
+ test_create_repo regression &&
+ (
+ cd regression &&
+ test_commit needs-another-commit &&
+ test_commit foo.bar &&
+ git log -M --follow -p foo.bar.t &&
+ git log -M --follow --stat foo.bar.t &&
+ git log -M --follow --name-only foo.bar.t
+ )
+'
+test_expect_success 'git log -c --follow' '
+ test_create_repo follow-c &&
+ (
+ cd follow-c &&
+ test_commit initial file original &&
+ git rm file &&
+ test_commit rename file2 original &&
+ git reset --hard initial &&
+ test_commit modify file foo &&
+ git merge -m merge rename &&
+ git log -c --follow file2
+ )
+'
+
+cat >expect <<\EOF
+* commit COMMIT_OBJECT_NAME
+|\ Merge: MERGE_PARENTS
+| | Author: A U Thor <author@example.com>
+| |
+| | Merge HEADS DESCRIPTION
+| |
+| * commit COMMIT_OBJECT_NAME
+| | Author: A U Thor <author@example.com>
+| |
+| | reach
+| | ---
+| | reach.t | 1 +
+| | 1 file changed, 1 insertion(+)
+| |
+| | diff --git a/reach.t b/reach.t
+| | new file mode 100644
+| | index 0000000..10c9591
+| | --- /dev/null
+| | +++ b/reach.t
+| | @@ -0,0 +1 @@
+| | +reach
+| |
+| \
+*-. \ commit COMMIT_OBJECT_NAME
+|\ \ \ Merge: MERGE_PARENTS
+| | | | Author: A U Thor <author@example.com>
+| | | |
+| | | | Merge HEADS DESCRIPTION
+| | | |
+| | * | commit COMMIT_OBJECT_NAME
+| | |/ Author: A U Thor <author@example.com>
+| | |
+| | | octopus-b
+| | | ---
+| | | octopus-b.t | 1 +
+| | | 1 file changed, 1 insertion(+)
+| | |
+| | | diff --git a/octopus-b.t b/octopus-b.t
+| | | new file mode 100644
+| | | index 0000000..d5fcad0
+| | | --- /dev/null
+| | | +++ b/octopus-b.t
+| | | @@ -0,0 +1 @@
+| | | +octopus-b
+| | |
+| * | commit COMMIT_OBJECT_NAME
+| |/ Author: A U Thor <author@example.com>
+| |
+| | octopus-a
+| | ---
+| | octopus-a.t | 1 +
+| | 1 file changed, 1 insertion(+)
+| |
+| | diff --git a/octopus-a.t b/octopus-a.t
+| | new file mode 100644
+| | index 0000000..11ee015
+| | --- /dev/null
+| | +++ b/octopus-a.t
+| | @@ -0,0 +1 @@
+| | +octopus-a
+| |
+* | commit COMMIT_OBJECT_NAME
+|/ Author: A U Thor <author@example.com>
+|
+| seventh
+| ---
+| seventh.t | 1 +
+| 1 file changed, 1 insertion(+)
+|
+| diff --git a/seventh.t b/seventh.t
+| new file mode 100644
+| index 0000000..9744ffc
+| --- /dev/null
+| +++ b/seventh.t
+| @@ -0,0 +1 @@
+| +seventh
+|
+* commit COMMIT_OBJECT_NAME
+|\ Merge: MERGE_PARENTS
+| | Author: A U Thor <author@example.com>
+| |
+| | Merge branch 'tangle'
+| |
+| * commit COMMIT_OBJECT_NAME
+| |\ Merge: MERGE_PARENTS
+| | | Author: A U Thor <author@example.com>
+| | |
+| | | Merge branch 'side' (early part) into tangle
+| | |
+| * | commit COMMIT_OBJECT_NAME
+| |\ \ Merge: MERGE_PARENTS
+| | | | Author: A U Thor <author@example.com>
+| | | |
+| | | | Merge branch 'master' (early part) into tangle
+| | | |
+| * | | commit COMMIT_OBJECT_NAME
+| | | | Author: A U Thor <author@example.com>
+| | | |
+| | | | tangle-a
+| | | | ---
+| | | | tangle-a | 1 +
+| | | | 1 file changed, 1 insertion(+)
+| | | |
+| | | | diff --git a/tangle-a b/tangle-a
+| | | | new file mode 100644
+| | | | index 0000000..7898192
+| | | | --- /dev/null
+| | | | +++ b/tangle-a
+| | | | @@ -0,0 +1 @@
+| | | | +a
+| | | |
+* | | | commit COMMIT_OBJECT_NAME
+|\ \ \ \ Merge: MERGE_PARENTS
+| | | | | Author: A U Thor <author@example.com>
+| | | | |
+| | | | | Merge branch 'side'
+| | | | |
+| * | | | commit COMMIT_OBJECT_NAME
+| | |_|/ Author: A U Thor <author@example.com>
+| |/| |
+| | | | side-2
+| | | | ---
+| | | | 2 | 1 +
+| | | | 1 file changed, 1 insertion(+)
+| | | |
+| | | | diff --git a/2 b/2
+| | | | new file mode 100644
+| | | | index 0000000..0cfbf08
+| | | | --- /dev/null
+| | | | +++ b/2
+| | | | @@ -0,0 +1 @@
+| | | | +2
+| | | |
+| * | | commit COMMIT_OBJECT_NAME
+| | | | Author: A U Thor <author@example.com>
+| | | |
+| | | | side-1
+| | | | ---
+| | | | 1 | 1 +
+| | | | 1 file changed, 1 insertion(+)
+| | | |
+| | | | diff --git a/1 b/1
+| | | | new file mode 100644
+| | | | index 0000000..d00491f
+| | | | --- /dev/null
+| | | | +++ b/1
+| | | | @@ -0,0 +1 @@
+| | | | +1
+| | | |
+* | | | commit COMMIT_OBJECT_NAME
+| | | | Author: A U Thor <author@example.com>
+| | | |
+| | | | Second
+| | | | ---
+| | | | one | 1 +
+| | | | 1 file changed, 1 insertion(+)
+| | | |
+| | | | diff --git a/one b/one
+| | | | new file mode 100644
+| | | | index 0000000..9a33383
+| | | | --- /dev/null
+| | | | +++ b/one
+| | | | @@ -0,0 +1 @@
+| | | | +case
+| | | |
+* | | | commit COMMIT_OBJECT_NAME
+| |_|/ Author: A U Thor <author@example.com>
+|/| |
+| | | sixth
+| | | ---
+| | | a/two | 1 -
+| | | 1 file changed, 1 deletion(-)
+| | |
+| | | diff --git a/a/two b/a/two
+| | | deleted file mode 100644
+| | | index 9245af5..0000000
+| | | --- a/a/two
+| | | +++ /dev/null
+| | | @@ -1 +0,0 @@
+| | | -ni
+| | |
+* | | commit COMMIT_OBJECT_NAME
+| | | Author: A U Thor <author@example.com>
+| | |
+| | | fifth
+| | | ---
+| | | a/two | 1 +
+| | | 1 file changed, 1 insertion(+)
+| | |
+| | | diff --git a/a/two b/a/two
+| | | new file mode 100644
+| | | index 0000000..9245af5
+| | | --- /dev/null
+| | | +++ b/a/two
+| | | @@ -0,0 +1 @@
+| | | +ni
+| | |
+* | | commit COMMIT_OBJECT_NAME
+|/ / Author: A U Thor <author@example.com>
+| |
+| | fourth
+| | ---
+| | ein | 1 +
+| | 1 file changed, 1 insertion(+)
+| |
+| | diff --git a/ein b/ein
+| | new file mode 100644
+| | index 0000000..9d7e69f
+| | --- /dev/null
+| | +++ b/ein
+| | @@ -0,0 +1 @@
+| | +ichi
+| |
+* | commit COMMIT_OBJECT_NAME
+|/ Author: A U Thor <author@example.com>
+|
+| third
+| ---
+| ichi | 1 +
+| one | 1 -
+| 2 files changed, 1 insertion(+), 1 deletion(-)
+|
+| diff --git a/ichi b/ichi
+| new file mode 100644
+| index 0000000..9d7e69f
+| --- /dev/null
+| +++ b/ichi
+| @@ -0,0 +1 @@
+| +ichi
+| diff --git a/one b/one
+| deleted file mode 100644
+| index 9d7e69f..0000000
+| --- a/one
+| +++ /dev/null
+| @@ -1 +0,0 @@
+| -ichi
+|
+* commit COMMIT_OBJECT_NAME
+| Author: A U Thor <author@example.com>
+|
+| second
+| ---
+| one | 2 +-
+| 1 file changed, 1 insertion(+), 1 deletion(-)
+|
+| diff --git a/one b/one
+| index 5626abf..9d7e69f 100644
+| --- a/one
+| +++ b/one
+| @@ -1 +1 @@
+| -one
+| +ichi
+|
+* commit COMMIT_OBJECT_NAME
+ Author: A U Thor <author@example.com>
+
+ initial
+ ---
+ one | 1 +
+ 1 file changed, 1 insertion(+)
+
+ diff --git a/one b/one
+ new file mode 100644
+ index 0000000..5626abf
+ --- /dev/null
+ +++ b/one
+ @@ -0,0 +1 @@
+ +one
+EOF
+
+sanitize_output () {
+ sed -e 's/ *$//' \
+ -e 's/commit [0-9a-f]*$/commit COMMIT_OBJECT_NAME/' \
+ -e 's/Merge: [ 0-9a-f]*$/Merge: MERGE_PARENTS/' \
+ -e 's/Merge tag.*/Merge HEADS DESCRIPTION/' \
+ -e 's/Merge commit.*/Merge HEADS DESCRIPTION/' \
+ -e 's/, 0 deletions(-)//' \
+ -e 's/, 0 insertions(+)//' \
+ -e 's/ 1 files changed, / 1 file changed, /' \
+ -e 's/, 1 deletions(-)/, 1 deletion(-)/' \
+ -e 's/, 1 insertions(+)/, 1 insertion(+)/'
+}
+
+test_expect_success 'log --graph with diff and stats' '
+ git log --no-renames --graph --pretty=short --stat -p >actual &&
+ sanitize_output >actual.sanitized <actual &&
+ test_i18ncmp expect actual.sanitized
+'
+
+test_expect_success 'dotdot is a parent directory' '
+ mkdir -p a/b &&
+ ( echo sixth && echo fifth ) >expect &&
+ ( cd a/b && git log --format=%s .. ) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'log --graph --show-signature' '
+ test_when_finished "git reset --hard && git checkout master" &&
+ git checkout -b signed master &&
+ echo foo >foo &&
+ git add foo &&
+ git commit -S -m signed_commit &&
+ git log --graph --show-signature -n1 signed >actual &&
+ grep "^| gpg: Signature made" actual &&
+ grep "^| gpg: Good signature" actual
+'
+
+test_expect_success GPG 'log --graph --show-signature for merged tag' '
+ test_when_finished "git reset --hard && git checkout master" &&
+ git checkout -b plain master &&
+ echo aaa >bar &&
+ git add bar &&
+ git commit -m bar_commit &&
+ git checkout -b tagged master &&
+ echo bbb >baz &&
+ git add baz &&
+ git commit -m baz_commit &&
+ git tag -s -m signed_tag_msg signed_tag &&
+ git checkout plain &&
+ git merge --no-ff -m msg signed_tag &&
+ git log --graph --show-signature -n1 plain >actual &&
+ grep "^|\\\ merged tag" actual &&
+ grep "^| | gpg: Signature made" actual &&
+ grep "^| | gpg: Good signature" actual
+'
+
+test_expect_success 'log --graph --no-walk is forbidden' '
+ test_must_fail git log --graph --no-walk
+'
+
+test_expect_success 'log diagnoses bogus HEAD' '
+ git init empty &&
+ test_must_fail git -C empty log 2>stderr &&
+ test_i18ngrep does.not.have.any.commits stderr &&
+ echo 1234abcd >empty/.git/refs/heads/master &&
+ test_must_fail git -C empty log 2>stderr &&
+ test_i18ngrep broken stderr &&
+ echo "ref: refs/heads/invalid.lock" >empty/.git/HEAD &&
+ test_must_fail git -C empty log 2>stderr &&
+ test_i18ngrep broken stderr &&
+ test_must_fail git -C empty log --default totally-bogus 2>stderr &&
+ test_i18ngrep broken stderr
+'
+
+test_expect_success 'set up --source tests' '
+ git checkout --orphan source-a &&
+ test_commit one &&
+ test_commit two &&
+ git checkout -b source-b HEAD^ &&
+ test_commit three
+'
+
+test_expect_success 'log --source paints branch names' '
+ cat >expect <<-\EOF &&
+ 09e12a9 source-b three
+ 8e393e1 source-a two
+ 1ac6c77 source-b one
+ EOF
+ git log --oneline --source source-a source-b >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --source paints tag names' '
+ git tag -m tagged source-tag &&
+ cat >expect <<-\EOF &&
+ 09e12a9 source-tag three
+ 8e393e1 source-a two
+ 1ac6c77 source-tag one
+ EOF
+ git log --oneline --source source-tag source-a >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index 9a7d1b4..0dd8b65 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -4,16 +4,68 @@ test_description='.mailmap configurations'
. ./test-lib.sh
+fuzz_blame () {
+ sed "
+ s/$_x05[0-9a-f][0-9a-f][0-9a-f]/OBJID/g
+ s/$_x05[0-9a-f][0-9a-f]/OBJI/g
+ s/[-0-9]\{10\} [:0-9]\{8\} [-+][0-9]\{4\}/DATE/g
+ " "$@"
+}
+
test_expect_success setup '
+ cat >contacts <<-\EOF &&
+ A U Thor <author@example.com>
+ nick1 <bugs@company.xx>
+ EOF
+
echo one >one &&
git add one &&
test_tick &&
git commit -m initial &&
echo two >>one &&
git add one &&
+ test_tick &&
git commit --author "nick1 <bugs@company.xx>" -m second
'
+test_expect_success 'check-mailmap no arguments' '
+ test_must_fail git check-mailmap
+'
+
+test_expect_success 'check-mailmap arguments' '
+ cat >expect <<-\EOF &&
+ A U Thor <author@example.com>
+ nick1 <bugs@company.xx>
+ EOF
+ git check-mailmap \
+ "A U Thor <author@example.com>" \
+ "nick1 <bugs@company.xx>" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check-mailmap --stdin' '
+ cat >expect <<-\EOF &&
+ A U Thor <author@example.com>
+ nick1 <bugs@company.xx>
+ EOF
+ git check-mailmap --stdin <contacts >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check-mailmap --stdin arguments' '
+ cat >expect <<-\EOF &&
+ Internal Guy <bugs@company.xy>
+ EOF
+ cat <contacts >>expect &&
+ git check-mailmap --stdin "Internal Guy <bugs@company.xy>" \
+ <contacts >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check-mailmap bogus contact' '
+ test_must_fail git check-mailmap bogus
+'
+
cat >expect <<\EOF
A U Thor (1):
initial
@@ -54,7 +106,7 @@ Repo Guy (1):
EOF
test_expect_success 'mailmap.file set' '
- mkdir internal_mailmap &&
+ mkdir -p internal_mailmap &&
echo "Internal Guy <bugs@company.xx>" > internal_mailmap/.mailmap &&
git config mailmap.file internal_mailmap/.mailmap &&
git shortlog HEAD >actual &&
@@ -85,7 +137,7 @@ nick1 (1):
EOF
-test_expect_success 'mailmap.file non-existant' '
+test_expect_success 'mailmap.file non-existent' '
rm internal_mailmap/.mailmap &&
rmdir internal_mailmap &&
git shortlog HEAD >actual &&
@@ -93,6 +145,40 @@ test_expect_success 'mailmap.file non-existant' '
'
cat >expect <<\EOF
+Internal Guy (1):
+ second
+
+Repo Guy (1):
+ initial
+
+EOF
+
+test_expect_success 'name entry after email entry' '
+ mkdir -p internal_mailmap &&
+ echo "<bugs@company.xy> <bugs@company.xx>" >internal_mailmap/.mailmap &&
+ echo "Internal Guy <bugs@company.xx>" >>internal_mailmap/.mailmap &&
+ git shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<\EOF
+Internal Guy (1):
+ second
+
+Repo Guy (1):
+ initial
+
+EOF
+
+test_expect_success 'name entry after email entry, case-insensitive' '
+ mkdir -p internal_mailmap &&
+ echo "<bugs@company.xy> <bugs@company.xx>" >internal_mailmap/.mailmap &&
+ echo "Internal Guy <BUGS@Company.xx>" >>internal_mailmap/.mailmap &&
+ git shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<\EOF
A U Thor (1):
initial
@@ -101,11 +187,141 @@ nick1 (1):
EOF
test_expect_success 'No mailmap files, but configured' '
- rm .mailmap &&
+ rm -f .mailmap internal_mailmap/.mailmap &&
git shortlog HEAD >actual &&
test_cmp expect actual
'
+test_expect_success 'setup mailmap blob tests' '
+ git checkout -b map &&
+ test_when_finished "git checkout master" &&
+ cat >just-bugs <<-\EOF &&
+ Blob Guy <bugs@company.xx>
+ EOF
+ cat >both <<-\EOF &&
+ Blob Guy <author@example.com>
+ Blob Guy <bugs@company.xx>
+ EOF
+ printf "Tricky Guy <author@example.com>" >no-newline &&
+ git add just-bugs both no-newline &&
+ git commit -m "my mailmaps" &&
+ echo "Repo Guy <author@example.com>" >.mailmap &&
+ echo "Internal Guy <author@example.com>" >internal.map
+'
+
+test_expect_success 'mailmap.blob set' '
+ cat >expect <<-\EOF &&
+ Blob Guy (1):
+ second
+
+ Repo Guy (1):
+ initial
+
+ EOF
+ git -c mailmap.blob=map:just-bugs shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'mailmap.blob overrides .mailmap' '
+ cat >expect <<-\EOF &&
+ Blob Guy (2):
+ initial
+ second
+
+ EOF
+ git -c mailmap.blob=map:both shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'mailmap.file overrides mailmap.blob' '
+ cat >expect <<-\EOF &&
+ Blob Guy (1):
+ second
+
+ Internal Guy (1):
+ initial
+
+ EOF
+ git \
+ -c mailmap.blob=map:both \
+ -c mailmap.file=internal.map \
+ shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'mailmap.blob can be missing' '
+ cat >expect <<-\EOF &&
+ Repo Guy (1):
+ initial
+
+ nick1 (1):
+ second
+
+ EOF
+ git -c mailmap.blob=map:nonexistent shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'mailmap.blob defaults to off in non-bare repo' '
+ git init non-bare &&
+ (
+ cd non-bare &&
+ test_commit one .mailmap "Fake Name <author@example.com>" &&
+ echo " 1 Fake Name" >expect &&
+ git shortlog -ns HEAD >actual &&
+ test_cmp expect actual &&
+ rm .mailmap &&
+ echo " 1 A U Thor" >expect &&
+ git shortlog -ns HEAD >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'mailmap.blob defaults to HEAD:.mailmap in bare repo' '
+ git clone --bare non-bare bare &&
+ (
+ cd bare &&
+ echo " 1 Fake Name" >expect &&
+ git shortlog -ns HEAD >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'mailmap.blob can handle blobs without trailing newline' '
+ cat >expect <<-\EOF &&
+ Tricky Guy (1):
+ initial
+
+ nick1 (1):
+ second
+
+ EOF
+ git -c mailmap.blob=map:no-newline shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cleanup after mailmap.blob tests' '
+ rm -f .mailmap
+'
+
+test_expect_success 'single-character name' '
+ echo " 1 A <author@example.com>" >expect &&
+ echo " 1 nick1 <bugs@company.xx>" >>expect &&
+ echo "A <author@example.com>" >.mailmap &&
+ test_when_finished "rm .mailmap" &&
+ git shortlog -es HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'preserve canonical email case' '
+ echo " 1 A U Thor <AUTHOR@example.com>" >expect &&
+ echo " 1 nick1 <bugs@company.xx>" >>expect &&
+ echo "<AUTHOR@example.com> <author@example.com>" >.mailmap &&
+ test_when_finished "rm .mailmap" &&
+ git shortlog -es HEAD >actual &&
+ test_cmp expect actual
+'
+
# Extended mailmap configurations should give us the following output for shortlog
cat >expect <<\EOF
A U Thor <author@example.com> (1):
@@ -153,7 +369,7 @@ test_expect_success 'Shortlog output (complex mapping)' '
test_tick &&
git commit --author "CTO <cto@coompany.xx>" -m seventh &&
- mkdir internal_mailmap &&
+ mkdir -p 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 &&
@@ -196,19 +412,86 @@ test_expect_success 'Log output (complex mapping)' '
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
+Author: CTO <cto@company.xx>
+Author: Santa Claus <santa.claus@northpole.xx>
+Author: Santa Claus <santa.claus@northpole.xx>
+Author: Other Author <other@author.xx>
+Author: Other Author <other@author.xx>
+Author: Some Dude <some@dude.xx>
+Author: A U Thor <author@example.com>
+EOF
+
+test_expect_success 'Log output with --use-mailmap' '
+ git log --use-mailmap | grep Author >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<\EOF
+Author: CTO <cto@company.xx>
+Author: Santa Claus <santa.claus@northpole.xx>
+Author: Santa Claus <santa.claus@northpole.xx>
+Author: Other Author <other@author.xx>
+Author: Other Author <other@author.xx>
+Author: Some Dude <some@dude.xx>
+Author: A U Thor <author@example.com>
+EOF
+
+test_expect_success 'Log output with log.mailmap' '
+ git -c log.mailmap=True log | grep Author >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<\EOF
+Author: Santa Claus <santa.claus@northpole.xx>
+Author: Santa Claus <santa.claus@northpole.xx>
EOF
+test_expect_success 'Grep author with --use-mailmap' '
+ git log --use-mailmap --author Santa | grep Author >actual &&
+ test_cmp expect actual
+'
+cat >expect <<\EOF
+Author: Santa Claus <santa.claus@northpole.xx>
+Author: Santa Claus <santa.claus@northpole.xx>
+EOF
+
+test_expect_success 'Grep author with log.mailmap' '
+ git -c log.mailmap=True log --author Santa | grep Author >actual &&
+ test_cmp expect actual
+'
+
+>expect
+
+test_expect_success 'Only grep replaced author with --use-mailmap' '
+ git log --use-mailmap --author "<cto@coompany.xx>" >actual &&
+ test_cmp expect actual
+'
+
+# git blame
+cat >expect <<\EOF
+^OBJI (A U Thor DATE 1) one
+OBJID (Some Dude DATE 2) two
+OBJID (Other Author DATE 3) three
+OBJID (Other Author DATE 4) four
+OBJID (Santa Claus DATE 5) five
+OBJID (Santa Claus DATE 6) six
+OBJID (CTO DATE 7) seven
+EOF
test_expect_success 'Blame output (complex mapping)' '
git blame one >actual &&
+ fuzz_blame actual >actual.fuzz &&
+ test_cmp expect actual.fuzz
+'
+
+cat >expect <<\EOF
+Some Dude <some@dude.xx>
+EOF
+
+test_expect_success 'commit --author honors mailmap' '
+ test_must_fail git commit --author "nick" --allow-empty -meight &&
+ git commit --author "Some Dude" --allow-empty -meight &&
+ git show --pretty=format:"%an <%ae>%n" >actual &&
test_cmp expect actual
'
diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh
index 68e2652..84a8096 100755
--- a/t/t4204-patch-id.sh
+++ b/t/t4204-patch-id.sh
@@ -5,27 +5,44 @@ 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
+ as="a a a a a a a a" && # eight a
+ test_write_lines $as >foo &&
+ test_write_lines $as >bar &&
+ git add foo bar &&
+ git commit -a -m initial &&
+ test_write_lines $as b >foo &&
+ test_write_lines $as b >bar &&
+ git commit -a -m first &&
+ git checkout -b same master &&
+ git commit --amend -m same-msg &&
+ git checkout -b notsame master &&
+ echo c >foo &&
+ echo c >bar &&
+ git commit --amend -a -m notsame-msg &&
+ test_write_lines bar foo >bar-then-foo &&
+ test_write_lines foo bar >foo-then-bar
'
test_expect_success 'patch-id output is well-formed' '
- git log -p -1 | git patch-id > output &&
+ git log -p -1 | git patch-id >output &&
grep "^[a-f0-9]\{40\} $(git rev-parse HEAD)$" output
'
+#calculate patch id. Make sure output is not empty.
calc_patch_id () {
- git patch-id |
- sed "s# .*##" > patch-id_"$1"
+ patch_name="$1"
+ shift
+ git patch-id "$@" |
+ sed "s/ .*//" >patch-id_"$patch_name" &&
+ test_line_count -gt 0 patch-id_"$patch_name"
+}
+
+get_top_diff () {
+ git log -p -1 "$@" -O bar-then-foo --
}
get_patch_id () {
- git log -p -1 "$1" | git patch-id |
- sed "s# .*##" > patch-id_"$1"
+ get_top_diff "$1" | calc_patch_id "$@"
}
test_expect_success 'patch-id detects equality' '
@@ -45,8 +62,8 @@ test_expect_success 'patch-id supports git-format-patch output' '
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`
+ set $(git format-patch -1 --stdout | git patch-id) &&
+ test "$2" = $(git rev-parse HEAD)
'
test_expect_success 'whitespace is irrelevant in footer' '
@@ -56,6 +73,69 @@ test_expect_success 'whitespace is irrelevant in footer' '
test_cmp patch-id_master patch-id_same
'
+cmp_patch_id () {
+ if
+ test "$1" = "relevant"
+ then
+ ! test_cmp patch-id_"$2" patch-id_"$3"
+ else
+ test_cmp patch-id_"$2" patch-id_"$3"
+ fi
+}
+
+test_patch_id_file_order () {
+ relevant="$1"
+ shift
+ name="order-${1}-$relevant"
+ shift
+ get_top_diff "master" | calc_patch_id "$name" "$@" &&
+ git checkout same &&
+ git format-patch -1 --stdout -O foo-then-bar |
+ calc_patch_id "ordered-$name" "$@" &&
+ cmp_patch_id $relevant "$name" "ordered-$name"
+
+}
+
+# combined test for options: add more tests here to make them
+# run with all options
+test_patch_id () {
+ test_patch_id_file_order "$@"
+}
+
+# small tests with detailed diagnostic for basic options.
+test_expect_success 'file order is irrelevant with --stable' '
+ test_patch_id_file_order irrelevant --stable --stable
+'
+
+test_expect_success 'file order is relevant with --unstable' '
+ test_patch_id_file_order relevant --unstable --unstable
+'
+
+#Now test various option combinations.
+test_expect_success 'default is unstable' '
+ test_patch_id relevant default
+'
+
+test_expect_success 'patchid.stable = true is stable' '
+ test_config patchid.stable true &&
+ test_patch_id irrelevant patchid.stable=true
+'
+
+test_expect_success 'patchid.stable = false is unstable' '
+ test_config patchid.stable false &&
+ test_patch_id relevant patchid.stable=false
+'
+
+test_expect_success '--unstable overrides patchid.stable = true' '
+ test_config patchid.stable true &&
+ test_patch_id relevant patchid.stable=true--unstable --unstable
+'
+
+test_expect_success '--stable overrides patchid.stable = false' '
+ test_config patchid.stable false &&
+ test_patch_id irrelevant patchid.stable=false--stable --stable
+'
+
test_expect_success 'patch-id supports git-format-patch MIME output' '
get_patch_id master &&
git checkout same &&
@@ -63,4 +143,40 @@ test_expect_success 'patch-id supports git-format-patch MIME output' '
test_cmp patch-id_master patch-id_same
'
+cat >nonl <<\EOF
+diff --git i/a w/a
+index e69de29..2e65efe 100644
+--- i/a
++++ w/a
+@@ -0,0 +1 @@
++a
+\ No newline at end of file
+diff --git i/b w/b
+index e69de29..6178079 100644
+--- i/b
++++ w/b
+@@ -0,0 +1 @@
++b
+EOF
+
+cat >withnl <<\EOF
+diff --git i/a w/a
+index e69de29..7898192 100644
+--- i/a
++++ w/a
+@@ -0,0 +1 @@
++a
+diff --git i/b w/b
+index e69de29..6178079 100644
+--- i/b
++++ w/b
+@@ -0,0 +1 @@
++b
+EOF
+
+test_expect_success 'patch-id handles no-nl-at-eof markers' '
+ cat nonl | calc_patch_id nonl &&
+ cat withnl | calc_patch_id withnl &&
+ test_cmp patch-id_nonl patch-id_withnl
+'
test_done
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index cb9f2bd..d9f6242 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -1,20 +1,41 @@
#!/bin/sh
#
# Copyright (c) 2010, Will Palmer
+# Copyright (c) 2011, Alexey Shumkin (+ non-UTF-8 commit encoding tests)
#
test_description='Test pretty formats'
. ./test-lib.sh
+# Tested non-UTF-8 encoding
+test_encoding="ISO8859-1"
+
+sample_utf8_part=$(printf "f\303\244ng")
+
+commit_msg () {
+ # String "initial. initial" partly in German
+ # (translated with Google Translate),
+ # encoded in UTF-8, used as a commit log message below.
+ msg="initial. an${sample_utf8_part}lich\n"
+ if test -n "$1"
+ then
+ printf "$msg" | iconv -f utf-8 -t "$1"
+ else
+ printf "$msg"
+ fi
+}
+
test_expect_success 'set up basic repos' '
>foo &&
>bar &&
git add foo &&
test_tick &&
- git commit -m initial &&
+ git config i18n.commitEncoding $test_encoding &&
+ commit_msg $test_encoding | git commit -F - &&
git add bar &&
test_tick &&
- git commit -m "add bar"
+ git commit -m "add bar" &&
+ git config --unset i18n.commitEncoding
'
test_expect_success 'alias builtin format' '
@@ -38,6 +59,20 @@ test_expect_success 'alias user-defined format' '
test_cmp expected actual
'
+test_expect_success 'alias user-defined tformat with %s (ISO8859-1 encoding)' '
+ git config i18n.logOutputEncoding $test_encoding &&
+ git log --oneline >expected-s &&
+ git log --pretty="tformat:%h %s" >actual-s &&
+ git config --unset i18n.logOutputEncoding &&
+ test_cmp expected-s actual-s
+'
+
+test_expect_success 'alias user-defined tformat with %s (utf-8 encoding)' '
+ git log --oneline >expected-s &&
+ git log --pretty="tformat:%h %s" >actual-s &&
+ test_cmp expected-s actual-s
+'
+
test_expect_success 'alias user-defined tformat' '
git log --pretty="tformat:%h" >expected &&
git config pretty.test-alias "tformat:%h" &&
@@ -45,7 +80,7 @@ test_expect_success 'alias user-defined tformat' '
test_cmp expected actual
'
-test_expect_success 'alias non-existant format' '
+test_expect_success 'alias non-existent format' '
git config pretty.test-alias format-that-will-never-exist &&
test_must_fail git log --pretty=test-alias
'
@@ -71,4 +106,431 @@ test_expect_success 'alias loop' '
test_must_fail git log --pretty=test-foo
'
+test_expect_success 'NUL separation' '
+ printf "add bar\0$(commit_msg)" >expected &&
+ git log -z --pretty="format:%s" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'NUL termination' '
+ printf "add bar\0$(commit_msg)\0" >expected &&
+ git log -z --pretty="tformat:%s" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'NUL separation with --stat' '
+ stat0_part=$(git diff --stat HEAD^ HEAD) &&
+ stat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) &&
+ printf "add bar\n$stat0_part\n\0$(commit_msg)\n$stat1_part\n" >expected &&
+ git log -z --stat --pretty="format:%s" >actual &&
+ test_i18ncmp expected actual
+'
+
+test_expect_failure 'NUL termination with --stat' '
+ stat0_part=$(git diff --stat HEAD^ HEAD) &&
+ stat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) &&
+ printf "add bar\n$stat0_part\n\0$(commit_msg)\n$stat1_part\n0" >expected &&
+ git log -z --stat --pretty="tformat:%s" >actual &&
+ test_i18ncmp expected actual
+'
+
+test_expect_success 'setup more commits' '
+ test_commit "message one" one one message-one &&
+ test_commit "message two" two two message-two &&
+ head1=$(git rev-parse --verify --short HEAD~0) &&
+ head2=$(git rev-parse --verify --short HEAD~1) &&
+ head3=$(git rev-parse --verify --short HEAD~2) &&
+ head4=$(git rev-parse --verify --short HEAD~3)
+'
+
+test_expect_success 'left alignment formatting' '
+ git log --pretty="tformat:%<(40)%s" >actual &&
+ qz_to_tab_space <<EOF >expected &&
+message two Z
+message one Z
+add bar Z
+$(commit_msg) Z
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting. i18n.logOutputEncoding' '
+ git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(40)%s" >actual &&
+ qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+message two Z
+message one Z
+add bar Z
+$(commit_msg) Z
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting at the nth column' '
+ git log --pretty="tformat:%h %<|(40)%s" >actual &&
+ qz_to_tab_space <<EOF >expected &&
+$head1 message two Z
+$head2 message one Z
+$head3 add bar Z
+$head4 $(commit_msg) Z
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting at the nth column' '
+ COLUMNS=50 git log --pretty="tformat:%h %<|(-10)%s" >actual &&
+ qz_to_tab_space <<EOF >expected &&
+$head1 message two Z
+$head2 message one Z
+$head3 add bar Z
+$head4 $(commit_msg) Z
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting at the nth column. i18n.logOutputEncoding' '
+ git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %<|(40)%s" >actual &&
+ qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+$head1 message two Z
+$head2 message one Z
+$head3 add bar Z
+$head4 $(commit_msg) Z
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting with no padding' '
+ git log --pretty="tformat:%<(1)%s" >actual &&
+ cat <<EOF >expected &&
+message two
+message one
+add bar
+$(commit_msg)
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting with no padding. i18n.logOutputEncoding' '
+ git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(1)%s" >actual &&
+ cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+message two
+message one
+add bar
+$(commit_msg)
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting with trunc' '
+ git log --pretty="tformat:%<(10,trunc)%s" >actual &&
+ qz_to_tab_space <<EOF >expected &&
+message ..
+message ..
+add bar Z
+initial...
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting with trunc. i18n.logOutputEncoding' '
+ git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,trunc)%s" >actual &&
+ qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+message ..
+message ..
+add bar Z
+initial...
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting with ltrunc' '
+ git log --pretty="tformat:%<(10,ltrunc)%s" >actual &&
+ qz_to_tab_space <<EOF >expected &&
+..sage two
+..sage one
+add bar Z
+..${sample_utf8_part}lich
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting with ltrunc. i18n.logOutputEncoding' '
+ git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,ltrunc)%s" >actual &&
+ qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+..sage two
+..sage one
+add bar Z
+..${sample_utf8_part}lich
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting with mtrunc' '
+ git log --pretty="tformat:%<(10,mtrunc)%s" >actual &&
+ qz_to_tab_space <<EOF >expected &&
+mess.. two
+mess.. one
+add bar Z
+init..lich
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting with mtrunc. i18n.logOutputEncoding' '
+ git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,mtrunc)%s" >actual &&
+ qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+mess.. two
+mess.. one
+add bar Z
+init..lich
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'right alignment formatting' '
+ git log --pretty="tformat:%>(40)%s" >actual &&
+ qz_to_tab_space <<EOF >expected &&
+Z message two
+Z message one
+Z add bar
+Z $(commit_msg)
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'right alignment formatting. i18n.logOutputEncoding' '
+ git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%>(40)%s" >actual &&
+ qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+Z message two
+Z message one
+Z add bar
+Z $(commit_msg)
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'right alignment formatting at the nth column' '
+ git log --pretty="tformat:%h %>|(40)%s" >actual &&
+ qz_to_tab_space <<EOF >expected &&
+$head1 message two
+$head2 message one
+$head3 add bar
+$head4 $(commit_msg)
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'right alignment formatting at the nth column' '
+ COLUMNS=50 git log --pretty="tformat:%h %>|(-10)%s" >actual &&
+ qz_to_tab_space <<EOF >expected &&
+$head1 message two
+$head2 message one
+$head3 add bar
+$head4 $(commit_msg)
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'right alignment formatting at the nth column. i18n.logOutputEncoding' '
+ git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %>|(40)%s" >actual &&
+ qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+$head1 message two
+$head2 message one
+$head3 add bar
+$head4 $(commit_msg)
+EOF
+ test_cmp expected actual
+'
+
+# Note: Space between 'message' and 'two' should be in the same column
+# as in previous test.
+test_expect_success 'right alignment formatting at the nth column with --graph. i18n.logOutputEncoding' '
+ git -c i18n.logOutputEncoding=$test_encoding log --graph --pretty="tformat:%h %>|(40)%s" >actual &&
+ iconv -f utf-8 -t $test_encoding >expected <<EOF&&
+* $head1 message two
+* $head2 message one
+* $head3 add bar
+* $head4 $(commit_msg)
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'right alignment formatting with no padding' '
+ git log --pretty="tformat:%>(1)%s" >actual &&
+ cat <<EOF >expected &&
+message two
+message one
+add bar
+$(commit_msg)
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'right alignment formatting with no padding and with --graph' '
+ git log --graph --pretty="tformat:%>(1)%s" >actual &&
+ cat <<EOF >expected &&
+* message two
+* message one
+* add bar
+* $(commit_msg)
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'right alignment formatting with no padding. i18n.logOutputEncoding' '
+ git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%>(1)%s" >actual &&
+ cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+message two
+message one
+add bar
+$(commit_msg)
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'center alignment formatting' '
+ git log --pretty="tformat:%><(40)%s" >actual &&
+ qz_to_tab_space <<EOF >expected &&
+Z message two Z
+Z message one Z
+Z add bar Z
+Z $(commit_msg) Z
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'center alignment formatting. i18n.logOutputEncoding' '
+ git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%><(40)%s" >actual &&
+ qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+Z message two Z
+Z message one Z
+Z add bar Z
+Z $(commit_msg) Z
+EOF
+ test_cmp expected actual
+'
+test_expect_success 'center alignment formatting at the nth column' '
+ git log --pretty="tformat:%h %><|(40)%s" >actual &&
+ qz_to_tab_space <<EOF >expected &&
+$head1 message two Z
+$head2 message one Z
+$head3 add bar Z
+$head4 $(commit_msg) Z
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'center alignment formatting at the nth column' '
+ COLUMNS=70 git log --pretty="tformat:%h %><|(-30)%s" >actual &&
+ qz_to_tab_space <<EOF >expected &&
+$head1 message two Z
+$head2 message one Z
+$head3 add bar Z
+$head4 $(commit_msg) Z
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'center alignment formatting at the nth column. i18n.logOutputEncoding' '
+ git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %><|(40)%s" >actual &&
+ qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+$head1 message two Z
+$head2 message one Z
+$head3 add bar Z
+$head4 $(commit_msg) Z
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'center alignment formatting with no padding' '
+ git log --pretty="tformat:%><(1)%s" >actual &&
+ cat <<EOF >expected &&
+message two
+message one
+add bar
+$(commit_msg)
+EOF
+ test_cmp expected actual
+'
+
+# save HEAD's SHA-1 digest (with no abbreviations) to use it below
+# as far as the next test amends HEAD
+old_head1=$(git rev-parse --verify HEAD~0)
+test_expect_success 'center alignment formatting with no padding. i18n.logOutputEncoding' '
+ git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%><(1)%s" >actual &&
+ cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+message two
+message one
+add bar
+$(commit_msg)
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'left/right alignment formatting with stealing' '
+ git commit --amend -m short --author "long long long <long@me.com>" &&
+ git log --pretty="tformat:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual &&
+ cat <<EOF >expected &&
+short long long long
+message .. A U Thor
+add bar A U Thor
+initial... A U Thor
+EOF
+ test_cmp expected actual
+'
+test_expect_success 'left/right alignment formatting with stealing. i18n.logOutputEncoding' '
+ git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual &&
+ cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+short long long long
+message .. A U Thor
+add bar A U Thor
+initial... A U Thor
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'strbuf_utf8_replace() not producing NUL' '
+ git log --color --pretty="tformat:%<(10,trunc)%s%>>(10,ltrunc)%C(auto)%d" |
+ test_decode_color |
+ nul_to_q >actual &&
+ ! grep Q actual
+'
+
+# ISO strict date format
+test_expect_success 'ISO and ISO-strict date formats display the same values' '
+ git log --format=%ai%n%ci |
+ sed -e "s/ /T/; s/ //; s/..\$/:&/" >expected &&
+ git log --format=%aI%n%cI >actual &&
+ test_cmp expected actual
+'
+
+# get new digests (with no abbreviations)
+head1=$(git rev-parse --verify HEAD~0) &&
+head2=$(git rev-parse --verify HEAD~1) &&
+
+test_expect_success 'log decoration properly follows tag chain' '
+ git tag -a tag1 -m tag1 &&
+ git tag -a tag2 -m tag2 tag1 &&
+ git tag -d tag1 &&
+ git commit --amend -m shorter &&
+ git log --no-walk --tags --pretty="%H %d" --decorate=full >actual &&
+ cat <<EOF >expected &&
+$head1 (tag: refs/tags/tag2)
+$head2 (tag: refs/tags/message-one)
+$old_head1 (tag: refs/tags/message-two)
+EOF
+ sort actual >actual1 &&
+ test_cmp expected actual1
+'
+
+test_expect_success 'clean log decoration' '
+ git log --no-walk --tags --pretty="%H %D" --decorate=full >actual &&
+ cat >expected <<EOF &&
+$head1 tag: refs/tags/tag2
+$head2 tag: refs/tags/message-one
+$old_head1 tag: refs/tags/message-two
+EOF
+ sort actual >actual1 &&
+ test_cmp expected actual1
+'
+
test_done
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
index bbde31b..f8008b6 100755
--- a/t/t4207-log-decoration-colors.sh
+++ b/t/t4207-log-decoration-colors.sh
@@ -44,15 +44,15 @@ test_expect_success setup '
'
cat >expected <<EOF
-${c_commit}COMMIT_ID (${c_HEAD}HEAD${c_reset}${c_commit},\
- ${c_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}\
+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}${c_commit} ->\
+ ${c_reset}${c_branch}master${c_reset}${c_commit},\
+ ${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit},\
+ ${c_reset}${c_tag}tag: B${c_reset}${c_commit})${c_reset} B
+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A1${c_reset}${c_commit},\
+ ${c_reset}${c_remoteBranch}other/master${c_reset}${c_commit})${c_reset} A1
+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\
On master: Changes to A.t
-${c_commit}COMMIT_ID (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
EOF
# We want log to show all, but the second parent to refs/stash is irrelevant
diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh
new file mode 100755
index 0000000..d8f23f4
--- /dev/null
+++ b/t/t4208-log-magic-pathspec.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+test_description='magic pathspec tests using git-log'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit initial &&
+ test_tick &&
+ git commit --allow-empty -m empty &&
+ mkdir sub
+'
+
+test_expect_success '"git log :/" should not be ambiguous' '
+ git log :/
+'
+
+test_expect_success '"git log :/a" should be ambiguous (applied both rev and worktree)' '
+ : >a &&
+ test_must_fail git log :/a 2>error &&
+ grep ambiguous error
+'
+
+test_expect_success '"git log :/a -- " should not be ambiguous' '
+ git log :/a --
+'
+
+test_expect_success '"git log -- :/a" should not be ambiguous' '
+ git log -- :/a
+'
+
+test_expect_success '"git log :" should be ambiguous' '
+ test_must_fail git log : 2>error &&
+ grep ambiguous error
+'
+
+test_expect_success 'git log -- :' '
+ git log -- :
+'
+
+test_expect_success 'git log HEAD -- :/' '
+ cat >expected <<-EOF &&
+ 24b24cf initial
+ EOF
+ (cd sub && git log --oneline HEAD -- :/ >../actual) &&
+ test_cmp expected actual
+'
+
+test_expect_success 'command line pathspec parsing for "git log"' '
+ git reset --hard &&
+ >a &&
+ git add a &&
+ git commit -m "add an empty a" --allow-empty &&
+ echo 1 >a &&
+ git commit -a -m "update a to 1" &&
+ git checkout HEAD^ &&
+ echo 2 >a &&
+ git commit -a -m "update a to 2" &&
+ test_must_fail git merge master &&
+ git add a &&
+ git log --merge -- a
+'
+
+test_done
diff --git a/t/t4209-log-pickaxe.sh b/t/t4209-log-pickaxe.sh
new file mode 100755
index 0000000..844df76
--- /dev/null
+++ b/t/t4209-log-pickaxe.sh
@@ -0,0 +1,109 @@
+#!/bin/sh
+
+test_description='log --grep/--author/--regexp-ignore-case/-S/-G'
+. ./test-lib.sh
+
+test_log () {
+ expect=$1
+ kind=$2
+ needle=$3
+ shift 3
+ rest=$@
+
+ case $kind in
+ --*)
+ opt=$kind=$needle
+ ;;
+ *)
+ opt=$kind$needle
+ ;;
+ esac
+ case $expect in
+ expect_nomatch)
+ match=nomatch
+ ;;
+ *)
+ match=match
+ ;;
+ esac
+
+ test_expect_success "log $kind${rest:+ $rest} ($match)" "
+ git log $rest $opt --format=%H >actual &&
+ test_cmp $expect actual
+ "
+}
+
+# test -i and --regexp-ignore-case and expect both to behave the same way
+test_log_icase () {
+ test_log $@ --regexp-ignore-case
+ test_log $@ -i
+}
+
+test_expect_success setup '
+ >expect_nomatch &&
+
+ >file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ git rev-parse --verify HEAD >expect_initial &&
+
+ echo Picked >file &&
+ git add file &&
+ test_tick &&
+ git commit --author="Another Person <another@example.com>" -m second &&
+ git rev-parse --verify HEAD >expect_second
+'
+
+test_log expect_initial --grep initial
+test_log expect_nomatch --grep InItial
+test_log_icase expect_initial --grep InItial
+test_log_icase expect_nomatch --grep initail
+
+test_log expect_second --author Person
+test_log expect_nomatch --author person
+test_log_icase expect_second --author person
+test_log_icase expect_nomatch --author spreon
+
+test_log expect_nomatch -G picked
+test_log expect_second -G Picked
+test_log_icase expect_nomatch -G pickle
+test_log_icase expect_second -G picked
+
+test_expect_success 'log -G --textconv (missing textconv tool)' '
+ echo "* diff=test" >.gitattributes &&
+ test_must_fail git -c diff.test.textconv=missing log -Gfoo &&
+ rm .gitattributes
+'
+
+test_expect_success 'log -G --no-textconv (missing textconv tool)' '
+ echo "* diff=test" >.gitattributes &&
+ git -c diff.test.textconv=missing log -Gfoo --no-textconv >actual &&
+ test_cmp expect_nomatch actual &&
+ rm .gitattributes
+'
+
+test_log expect_nomatch -S picked
+test_log expect_second -S Picked
+test_log_icase expect_second -S picked
+test_log_icase expect_nomatch -S pickle
+
+test_log expect_nomatch -S p.cked --pickaxe-regex
+test_log expect_second -S P.cked --pickaxe-regex
+test_log_icase expect_second -S p.cked --pickaxe-regex
+test_log_icase expect_nomatch -S p.ckle --pickaxe-regex
+
+test_expect_success 'log -S --textconv (missing textconv tool)' '
+ echo "* diff=test" >.gitattributes &&
+ test_must_fail git -c diff.test.textconv=missing log -Sfoo &&
+ rm .gitattributes
+'
+
+test_expect_success 'log -S --no-textconv (missing textconv tool)' '
+ echo "* diff=test" >.gitattributes &&
+ git -c diff.test.textconv=missing log -Sfoo --no-textconv >actual &&
+ test_cmp expect_nomatch actual &&
+ rm .gitattributes
+'
+
+test_done
diff --git a/t/t4210-log-i18n.sh b/t/t4210-log-i18n.sh
new file mode 100755
index 0000000..e585fe6
--- /dev/null
+++ b/t/t4210-log-i18n.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+test_description='test log with i18n features'
+. ./test-lib.sh
+
+# two forms of é
+utf8_e=$(printf '\303\251')
+latin1_e=$(printf '\351')
+
+test_expect_success 'create commits in different encodings' '
+ test_tick &&
+ cat >msg <<-EOF &&
+ utf8
+
+ t${utf8_e}st
+ EOF
+ git add msg &&
+ git -c i18n.commitencoding=utf8 commit -F msg &&
+ cat >msg <<-EOF &&
+ latin1
+
+ t${latin1_e}st
+ EOF
+ git add msg &&
+ git -c i18n.commitencoding=ISO-8859-1 commit -F msg
+'
+
+test_expect_success 'log --grep searches in log output encoding (utf8)' '
+ cat >expect <<-\EOF &&
+ latin1
+ utf8
+ EOF
+ git log --encoding=utf8 --format=%s --grep=$utf8_e >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success !MINGW 'log --grep searches in log output encoding (latin1)' '
+ cat >expect <<-\EOF &&
+ latin1
+ utf8
+ EOF
+ git log --encoding=ISO-8859-1 --format=%s --grep=$latin1_e >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success !MINGW 'log --grep does not find non-reencoded values (utf8)' '
+ >expect &&
+ git log --encoding=utf8 --format=%s --grep=$latin1_e >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --grep does not find non-reencoded values (latin1)' '
+ >expect &&
+ git log --encoding=ISO-8859-1 --format=%s --grep=$utf8_e >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
new file mode 100755
index 0000000..4451127
--- /dev/null
+++ b/t/t4211-line-log.sh
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+test_description='test log -L'
+. ./test-lib.sh
+
+test_expect_success 'setup (import history)' '
+ git fast-import < "$TEST_DIRECTORY"/t4211/history.export &&
+ git reset --hard
+'
+
+canned_test_1 () {
+ test_expect_$1 "$2" "
+ git log $2 >actual &&
+ test_cmp \"\$TEST_DIRECTORY\"/t4211/expect.$3 actual
+ "
+}
+
+canned_test () {
+ canned_test_1 success "$@"
+}
+canned_test_failure () {
+ canned_test_1 failure "$@"
+}
+
+test_bad_opts () {
+ test_expect_success "invalid args: $1" "
+ test_must_fail git log $1 2>errors &&
+ grep '$2' errors
+ "
+}
+
+canned_test "-L 4,12:a.c simple" simple-f
+canned_test "-L 4,+9:a.c simple" simple-f
+canned_test "-L '/long f/,/^}/:a.c' simple" simple-f
+canned_test "-L :f:a.c simple" simple-f-to-main
+
+canned_test "-L '/main/,/^}/:a.c' simple" simple-main
+canned_test "-L :main:a.c simple" simple-main-to-end
+
+canned_test "-L 1,+4:a.c simple" beginning-of-file
+
+canned_test "-L 20:a.c simple" end-of-file
+
+canned_test "-L '/long f/',/^}/:a.c -L /main/,/^}/:a.c simple" two-ranges
+canned_test "-L 24,+1:a.c simple" vanishes-early
+
+canned_test "-M -L '/long f/,/^}/:b.c' move-support" move-support-f
+canned_test "-M -L ':f:b.c' parallel-change" parallel-change-f-to-main
+
+canned_test "-L 4,12:a.c -L :main:a.c simple" multiple
+canned_test "-L 4,18:a.c -L ^:main:a.c simple" multiple-overlapping
+canned_test "-L :main:a.c -L 4,18:a.c simple" multiple-overlapping
+canned_test "-L 4:a.c -L 8,12:a.c simple" multiple-superset
+canned_test "-L 8,12:a.c -L 4:a.c simple" multiple-superset
+
+test_bad_opts "-L" "switch.*requires a value"
+test_bad_opts "-L b.c" "argument not .start,end:file"
+test_bad_opts "-L 1:" "argument not .start,end:file"
+test_bad_opts "-L 1:nonexistent" "There is no path"
+test_bad_opts "-L 1:simple" "There is no path"
+test_bad_opts "-L '/foo:b.c'" "argument not .start,end:file"
+test_bad_opts "-L 1000:b.c" "has only.*lines"
+test_bad_opts "-L 1,1000:b.c" "has only.*lines"
+test_bad_opts "-L :b.c" "argument not .start,end:file"
+test_bad_opts "-L :foo:b.c" "no match"
+
+test_expect_success '-L X (X == nlines)' '
+ n=$(wc -l <b.c) &&
+ git log -L $n:b.c
+'
+
+test_expect_success '-L X (X == nlines + 1)' '
+ n=$(expr $(wc -l <b.c) + 1) &&
+ test_must_fail git log -L $n:b.c
+'
+
+test_expect_success '-L X (X == nlines + 2)' '
+ n=$(expr $(wc -l <b.c) + 2) &&
+ test_must_fail git log -L $n:b.c
+'
+
+test_expect_success '-L ,Y (Y == nlines)' '
+ n=$(printf "%d" $(wc -l <b.c)) &&
+ git log -L ,$n:b.c
+'
+
+test_expect_success '-L ,Y (Y == nlines + 1)' '
+ n=$(expr $(wc -l <b.c) + 1) &&
+ test_must_fail git log -L ,$n:b.c
+'
+
+test_expect_success '-L ,Y (Y == nlines + 2)' '
+ n=$(expr $(wc -l <b.c) + 2) &&
+ test_must_fail git log -L ,$n:b.c
+'
+
+test_expect_success '-L with --first-parent and a merge' '
+ git checkout parallel-change &&
+ git log --first-parent -L 1,1:b.c
+'
+
+test_done
diff --git a/t/t4211/expect.beginning-of-file b/t/t4211/expect.beginning-of-file
new file mode 100644
index 0000000..91b4054
--- /dev/null
+++ b/t/t4211/expect.beginning-of-file
@@ -0,0 +1,43 @@
+commit 4a23ae5c98d59a58c6da036156959f2dc9f472ad
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:47:40 2013 +0100
+
+ change at very beginning
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -1,3 +1,4 @@
++#include <unistd.h>
+ #include <stdio.h>
+
+ long f(long x)
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:16 2013 +0100
+
+ touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -1,3 +1,3 @@
+ #include <stdio.h>
+
+-int f(int x)
++long f(long x)
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:48 2013 +0100
+
+ initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +1,3 @@
++#include <stdio.h>
++
++int f(int x)
diff --git a/t/t4211/expect.end-of-file b/t/t4211/expect.end-of-file
new file mode 100644
index 0000000..bd25bb2
--- /dev/null
+++ b/t/t4211/expect.end-of-file
@@ -0,0 +1,62 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:43 2013 +0100
+
+ change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -20,3 +20,5 @@
+ printf("%ld\n", f(15));
+ return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:10 2013 +0100
+
+ change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -20,3 +20,3 @@
+ printf("%ld\n", f(15));
+ return 0;
+-}
++}
+\ No newline at end of file
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:16 2013 +0100
+
+ touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -19,3 +19,3 @@
+- printf("%d\n", f(15));
++ printf("%ld\n", f(15));
+ return 0;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:48 2013 +0100
+
+ initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +18,3 @@
++ printf("%d\n", f(15));
++ return 0;
++}
diff --git a/t/t4211/expect.move-support-f b/t/t4211/expect.move-support-f
new file mode 100644
index 0000000..c905e01
--- /dev/null
+++ b/t/t4211/expect.move-support-f
@@ -0,0 +1,80 @@
+commit 6ce3c4ff690136099bb17e1a8766b75764726ea7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:49:50 2013 +0100
+
+ another simple change
+
+diff --git a/b.c b/b.c
+--- a/b.c
++++ b/b.c
+@@ -4,9 +4,9 @@
+ long f(long x)
+ {
+ int s = 0;
+ while (x) {
+- x >>= 1;
++ x /= 2;
+ s++;
+ }
+ return s;
+ }
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:16 2013 +0100
+
+ touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,9 +3,9 @@
+-int f(int x)
++long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:55 2013 +0100
+
+ change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,8 +3,9 @@
+ int f(int x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
++ return s;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:48 2013 +0100
+
+ initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,8 @@
++int f(int x)
++{
++ int s = 0;
++ while (x) {
++ x >>= 1;
++ s++;
++ }
++}
diff --git a/t/t4211/expect.multiple b/t/t4211/expect.multiple
new file mode 100644
index 0000000..76ad5b5
--- /dev/null
+++ b/t/t4211/expect.multiple
@@ -0,0 +1,104 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:43 2013 +0100
+
+ change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,7 @@
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:10 2013 +0100
+
+ change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+-}
++}
+\ No newline at end of file
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:16 2013 +0100
+
+ touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,9 +3,9 @@
+-int f(int x)
++long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+@@ -17,5 +17,5 @@
+ int main ()
+ {
+- printf("%d\n", f(15));
++ printf("%ld\n", f(15));
+ return 0;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:55 2013 +0100
+
+ change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,8 +3,9 @@
+ int f(int x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
++ return s;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:48 2013 +0100
+
+ initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,8 @@
++int f(int x)
++{
++ int s = 0;
++ while (x) {
++ x >>= 1;
++ s++;
++ }
++}
diff --git a/t/t4211/expect.multiple-overlapping b/t/t4211/expect.multiple-overlapping
new file mode 100644
index 0000000..d930b6e
--- /dev/null
+++ b/t/t4211/expect.multiple-overlapping
@@ -0,0 +1,187 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:43 2013 +0100
+
+ change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,19 +4,21 @@
+ long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+
+ /*
+ * This is only an example!
+ */
+
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:10 2013 +0100
+
+ change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,19 +4,19 @@
+ long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+
+ /*
+ * This is only an example!
+ */
+
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+-}
++}
+\ No newline at end of file
+
+commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:41 2013 +0100
+
+ touch comment
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,19 +3,19 @@
+ long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+
+ /*
+- * A comment.
++ * This is only an example!
+ */
+
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+ }
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:16 2013 +0100
+
+ touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,19 +3,19 @@
+-int f(int x)
++long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+
+ /*
+ * A comment.
+ */
+
+ int main ()
+ {
+- printf("%d\n", f(15));
++ printf("%ld\n", f(15));
+ return 0;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:55 2013 +0100
+
+ change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,18 +3,19 @@
+ int f(int x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
++ return s;
+ }
+
+ /*
+ * A comment.
+ */
+
+ int main ()
+ {
+ printf("%d\n", f(15));
+ return 0;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:48 2013 +0100
+
+ initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,18 @@
++int f(int x)
++{
++ int s = 0;
++ while (x) {
++ x >>= 1;
++ s++;
++ }
++}
++
++/*
++ * A comment.
++ */
++
++int main ()
++{
++ printf("%d\n", f(15));
++ return 0;
++}
diff --git a/t/t4211/expect.multiple-superset b/t/t4211/expect.multiple-superset
new file mode 100644
index 0000000..d930b6e
--- /dev/null
+++ b/t/t4211/expect.multiple-superset
@@ -0,0 +1,187 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:43 2013 +0100
+
+ change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,19 +4,21 @@
+ long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+
+ /*
+ * This is only an example!
+ */
+
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:10 2013 +0100
+
+ change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,19 +4,19 @@
+ long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+
+ /*
+ * This is only an example!
+ */
+
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+-}
++}
+\ No newline at end of file
+
+commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:41 2013 +0100
+
+ touch comment
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,19 +3,19 @@
+ long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+
+ /*
+- * A comment.
++ * This is only an example!
+ */
+
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+ }
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:16 2013 +0100
+
+ touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,19 +3,19 @@
+-int f(int x)
++long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+
+ /*
+ * A comment.
+ */
+
+ int main ()
+ {
+- printf("%d\n", f(15));
++ printf("%ld\n", f(15));
+ return 0;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:55 2013 +0100
+
+ change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,18 +3,19 @@
+ int f(int x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
++ return s;
+ }
+
+ /*
+ * A comment.
+ */
+
+ int main ()
+ {
+ printf("%d\n", f(15));
+ return 0;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:48 2013 +0100
+
+ initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,18 @@
++int f(int x)
++{
++ int s = 0;
++ while (x) {
++ x >>= 1;
++ s++;
++ }
++}
++
++/*
++ * A comment.
++ */
++
++int main ()
++{
++ printf("%d\n", f(15));
++ return 0;
++}
diff --git a/t/t4211/expect.parallel-change-f-to-main b/t/t4211/expect.parallel-change-f-to-main
new file mode 100644
index 0000000..052def8
--- /dev/null
+++ b/t/t4211/expect.parallel-change-f-to-main
@@ -0,0 +1,160 @@
+commit 0469c60bc4837d52d97b1f081dec5f98dea20fed
+Merge: ba227c6 6ce3c4f
+Author: Thomas Rast <trast@inf.ethz.ch>
+Date: Fri Apr 12 16:16:24 2013 +0200
+
+ Merge across the rename
+
+
+commit 6ce3c4ff690136099bb17e1a8766b75764726ea7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:49:50 2013 +0100
+
+ another simple change
+
+diff --git a/b.c b/b.c
+--- a/b.c
++++ b/b.c
+@@ -4,14 +4,14 @@
+ long f(long x)
+ {
+ int s = 0;
+ while (x) {
+- x >>= 1;
++ x /= 2;
+ s++;
+ }
+ return s;
+ }
+
+ /*
+ * This is only an example!
+ */
+
+
+commit ba227c6632349700fbb957dec2b50f5e2358be3f
+Author: Thomas Rast <trast@inf.ethz.ch>
+Date: Fri Apr 12 16:15:57 2013 +0200
+
+ change on another line of history while rename happens
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,14 +4,14 @@
+ long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+
+ /*
+- * This is only an example!
++ * This is only a short example!
+ */
+
+
+commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:41 2013 +0100
+
+ touch comment
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,14 +3,14 @@
+ long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+
+ /*
+- * A comment.
++ * This is only an example!
+ */
+
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:16 2013 +0100
+
+ touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,14 +3,14 @@
+-int f(int x)
++long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+
+ /*
+ * A comment.
+ */
+
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:55 2013 +0100
+
+ change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,13 +3,14 @@
+ int f(int x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
++ return s;
+ }
+
+ /*
+ * A comment.
+ */
+
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:48 2013 +0100
+
+ initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,13 @@
++int f(int x)
++{
++ int s = 0;
++ while (x) {
++ x >>= 1;
++ s++;
++ }
++}
++
++/*
++ * A comment.
++ */
++
diff --git a/t/t4211/expect.simple-f b/t/t4211/expect.simple-f
new file mode 100644
index 0000000..a1f5bc4
--- /dev/null
+++ b/t/t4211/expect.simple-f
@@ -0,0 +1,59 @@
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:16 2013 +0100
+
+ touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,9 +3,9 @@
+-int f(int x)
++long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:55 2013 +0100
+
+ change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,8 +3,9 @@
+ int f(int x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
++ return s;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:48 2013 +0100
+
+ initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,8 @@
++int f(int x)
++{
++ int s = 0;
++ while (x) {
++ x >>= 1;
++ s++;
++ }
++}
diff --git a/t/t4211/expect.simple-f-to-main b/t/t4211/expect.simple-f-to-main
new file mode 100644
index 0000000..a475768
--- /dev/null
+++ b/t/t4211/expect.simple-f-to-main
@@ -0,0 +1,100 @@
+commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:41 2013 +0100
+
+ touch comment
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,14 +3,14 @@
+ long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+
+ /*
+- * A comment.
++ * This is only an example!
+ */
+
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:16 2013 +0100
+
+ touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,14 +3,14 @@
+-int f(int x)
++long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+
+ /*
+ * A comment.
+ */
+
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:55 2013 +0100
+
+ change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,13 +3,14 @@
+ int f(int x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
++ return s;
+ }
+
+ /*
+ * A comment.
+ */
+
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:48 2013 +0100
+
+ initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,13 @@
++int f(int x)
++{
++ int s = 0;
++ while (x) {
++ x >>= 1;
++ s++;
++ }
++}
++
++/*
++ * A comment.
++ */
++
diff --git a/t/t4211/expect.simple-main b/t/t4211/expect.simple-main
new file mode 100644
index 0000000..39ce39b
--- /dev/null
+++ b/t/t4211/expect.simple-main
@@ -0,0 +1,68 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:43 2013 +0100
+
+ change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+-}
+\ No newline at end of file
++}
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:10 2013 +0100
+
+ change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+-}
++}
+\ No newline at end of file
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:16 2013 +0100
+
+ touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -17,5 +17,5 @@
+ int main ()
+ {
+- printf("%d\n", f(15));
++ printf("%ld\n", f(15));
+ return 0;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:48 2013 +0100
+
+ initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +16,5 @@
++int main ()
++{
++ printf("%d\n", f(15));
++ return 0;
++}
diff --git a/t/t4211/expect.simple-main-to-end b/t/t4211/expect.simple-main-to-end
new file mode 100644
index 0000000..8480bd9
--- /dev/null
+++ b/t/t4211/expect.simple-main-to-end
@@ -0,0 +1,70 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:43 2013 +0100
+
+ change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,7 @@
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:10 2013 +0100
+
+ change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+-}
++}
+\ No newline at end of file
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:16 2013 +0100
+
+ touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -17,5 +17,5 @@
+ int main ()
+ {
+- printf("%d\n", f(15));
++ printf("%ld\n", f(15));
+ return 0;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:48 2013 +0100
+
+ initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +16,5 @@
++int main ()
++{
++ printf("%d\n", f(15));
++ return 0;
++}
diff --git a/t/t4211/expect.two-ranges b/t/t4211/expect.two-ranges
new file mode 100644
index 0000000..6109aa0
--- /dev/null
+++ b/t/t4211/expect.two-ranges
@@ -0,0 +1,102 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:43 2013 +0100
+
+ change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+-}
+\ No newline at end of file
++}
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:10 2013 +0100
+
+ change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+-}
++}
+\ No newline at end of file
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:16 2013 +0100
+
+ touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,9 +3,9 @@
+-int f(int x)
++long f(long x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+ }
+@@ -17,5 +17,5 @@
+ int main ()
+ {
+- printf("%d\n", f(15));
++ printf("%ld\n", f(15));
+ return 0;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:55 2013 +0100
+
+ change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,8 +3,9 @@
+ int f(int x)
+ {
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
++ return s;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:48 2013 +0100
+
+ initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,8 @@
++int f(int x)
++{
++ int s = 0;
++ while (x) {
++ x >>= 1;
++ s++;
++ }
++}
diff --git a/t/t4211/expect.vanishes-early b/t/t4211/expect.vanishes-early
new file mode 100644
index 0000000..1f7cd06
--- /dev/null
+++ b/t/t4211/expect.vanishes-early
@@ -0,0 +1,39 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:43 2013 +0100
+
+ change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -22,1 +24,1 @@
+-}
+\ No newline at end of file
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:10 2013 +0100
+
+ change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -22,1 +22,1 @@
+-}
++}
+\ No newline at end of file
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:48 2013 +0100
+
+ initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +20,1 @@
++}
diff --git a/t/t4211/history.export b/t/t4211/history.export
new file mode 100644
index 0000000..f9f41e2
--- /dev/null
+++ b/t/t4211/history.export
@@ -0,0 +1,406 @@
+blob
+mark :1
+data 157
+#include <stdio.h>
+
+int f(int x)
+{
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+}
+
+/*
+ * A comment.
+ */
+
+int main ()
+{
+ printf("%d\n", f(15));
+ return 0;
+}
+
+reset refs/tags/simple
+commit refs/tags/simple
+mark :2
+author Thomas Rast <trast@student.ethz.ch> 1362044688 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044688 +0100
+data 8
+initial
+M 100644 :1 a.c
+
+blob
+mark :3
+data 168
+#include <stdio.h>
+
+int f(int x)
+{
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+}
+
+/*
+ * A comment.
+ */
+
+int main ()
+{
+ printf("%d\n", f(15));
+ return 0;
+}
+
+commit refs/tags/simple
+mark :4
+author Thomas Rast <trast@student.ethz.ch> 1362044695 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044695 +0100
+data 11
+change f()
+from :2
+M 100644 :3 a.c
+
+blob
+mark :5
+data 171
+#include <stdio.h>
+
+long f(long x)
+{
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+}
+
+/*
+ * A comment.
+ */
+
+int main ()
+{
+ printf("%ld\n", f(15));
+ return 0;
+}
+
+commit refs/tags/simple
+mark :6
+author Thomas Rast <trast@student.ethz.ch> 1362044716 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044716 +0100
+data 21
+touch both functions
+from :4
+M 100644 :5 a.c
+
+blob
+mark :7
+data 185
+#include <stdio.h>
+
+long f(long x)
+{
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+}
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+ printf("%ld\n", f(15));
+ return 0;
+}
+
+commit refs/tags/simple
+mark :8
+author Thomas Rast <trast@student.ethz.ch> 1362044741 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044741 +0100
+data 14
+touch comment
+from :6
+M 100644 :7 a.c
+
+blob
+mark :9
+data 205
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+}
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+ printf("%ld\n", f(15));
+ return 0;
+}
+
+commit refs/tags/simple
+mark :10
+author Thomas Rast <trast@student.ethz.ch> 1362044860 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044860 +0100
+data 25
+change at very beginning
+from :8
+M 100644 :9 a.c
+
+blob
+mark :11
+data 204
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+}
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+ printf("%ld\n", f(15));
+ return 0;
+}
+commit refs/tags/simple
+mark :12
+author Thomas Rast <trast@student.ethz.ch> 1362044890 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044890 +0100
+data 36
+change to an incomplete line at end
+from :10
+M 100644 :11 a.c
+
+blob
+mark :13
+data 238
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+}
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+ printf("%ld\n", f(15));
+ return 0;
+}
+
+/* incomplete lines are bad! */
+
+commit refs/tags/simple
+mark :14
+author Thomas Rast <trast@student.ethz.ch> 1362044923 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044923 +0100
+data 29
+change back to complete line
+from :12
+M 100644 :13 a.c
+
+commit refs/tags/move-support
+mark :15
+author Thomas Rast <trast@student.ethz.ch> 1362044968 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044968 +0100
+data 10
+move file
+from :14
+D a.c
+M 100644 :13 b.c
+
+blob
+mark :16
+data 237
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+ int s = 0;
+ while (x) {
+ x /= 2;
+ s++;
+ }
+ return s;
+}
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+ printf("%ld\n", f(15));
+ return 0;
+}
+
+/* incomplete lines are bad! */
+
+commit refs/tags/move-support
+mark :17
+author Thomas Rast <trast@student.ethz.ch> 1362044990 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044990 +0100
+data 22
+another simple change
+from :15
+M 100644 :16 b.c
+
+blob
+mark :18
+data 254
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x);
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+ printf("%ld\n", f(15));
+ return 0;
+}
+
+/* incomplete lines are bad! */
+
+long f(long x)
+{
+ int s = 0;
+ while (x) {
+ x /= 2;
+ s++;
+ }
+ return s;
+}
+
+commit refs/heads/master
+mark :19
+author Thomas Rast <trast@student.ethz.ch> 1362045024 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362045024 +0100
+data 21
+move within the file
+from :17
+M 100644 :18 b.c
+
+blob
+mark :20
+data 243
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+ int s = 0;
+ while (x) {
+ x >>= 1;
+ s++;
+ }
+ return s;
+}
+
+/*
+ * This is only a short example!
+ */
+
+int main ()
+{
+ printf("%ld\n", f(15));
+ return 0;
+}
+
+/* incomplete lines are bad! */
+
+commit refs/heads/parallel-change
+mark :21
+author Thomas Rast <trast@inf.ethz.ch> 1365776157 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1365776157 +0200
+data 55
+change on another line of history while rename happens
+from :14
+M 100644 :20 a.c
+
+blob
+mark :22
+data 242
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+ int s = 0;
+ while (x) {
+ x /= 2;
+ s++;
+ }
+ return s;
+}
+
+/*
+ * This is only a short example!
+ */
+
+int main ()
+{
+ printf("%ld\n", f(15));
+ return 0;
+}
+
+/* incomplete lines are bad! */
+
+commit refs/heads/parallel-change
+mark :23
+author Thomas Rast <trast@inf.ethz.ch> 1365776184 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1365776191 +0200
+data 24
+Merge across the rename
+from :21
+merge :17
+D a.c
+M 100644 :22 b.c
+
+reset refs/heads/parallel-change
+from :23
+
diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
new file mode 100755
index 0000000..22aa8b7
--- /dev/null
+++ b/t/t4212-log-corrupt.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+test_description='git log with invalid commit headers'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit foo &&
+
+ git cat-file commit HEAD |
+ sed "/^author /s/>/>-<>/" >broken_email.commit &&
+ git hash-object -w -t commit broken_email.commit >broken_email.hash &&
+ git update-ref refs/heads/broken_email $(cat broken_email.hash)
+'
+
+test_expect_success 'fsck notices broken commit' '
+ test_must_fail git fsck 2>actual &&
+ test_i18ngrep invalid.author actual
+'
+
+test_expect_success 'git log with broken author email' '
+ {
+ echo commit $(cat broken_email.hash)
+ echo "Author: A U Thor <author@example.com>"
+ echo "Date: Thu Apr 7 15:13:13 2005 -0700"
+ echo
+ echo " foo"
+ } >expect.out &&
+ : >expect.err &&
+
+ git log broken_email >actual.out 2>actual.err &&
+
+ test_cmp expect.out actual.out &&
+ test_cmp expect.err actual.err
+'
+
+test_expect_success 'git log --format with broken author email' '
+ echo "A U Thor+author@example.com+Thu Apr 7 15:13:13 2005 -0700" >expect.out &&
+ : >expect.err &&
+
+ git log --format="%an+%ae+%ad" broken_email >actual.out 2>actual.err &&
+
+ test_cmp expect.out actual.out &&
+ test_cmp expect.err actual.err
+'
+
+munge_author_date () {
+ git cat-file commit "$1" >commit.orig &&
+ sed "s/^\(author .*>\) [0-9]*/\1 $2/" <commit.orig >commit.munge &&
+ git hash-object -w -t commit commit.munge
+}
+
+test_expect_success 'unparsable dates produce sentinel value' '
+ commit=$(munge_author_date HEAD totally_bogus) &&
+ echo "Date: Thu Jan 1 00:00:00 1970 +0000" >expect &&
+ git log -1 $commit >actual.full &&
+ grep Date <actual.full >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'unparsable dates produce sentinel value (%ad)' '
+ commit=$(munge_author_date HEAD totally_bogus) &&
+ echo >expect &&
+ git log -1 --format=%ad $commit >actual &&
+ test_cmp expect actual
+'
+
+# date is 2^64 + 1
+test_expect_success 'date parser recognizes integer overflow' '
+ commit=$(munge_author_date HEAD 18446744073709551617) &&
+ echo "Thu Jan 1 00:00:00 1970 +0000" >expect &&
+ git log -1 --format=%ad $commit >actual &&
+ test_cmp expect actual
+'
+
+# date is 2^64 - 2
+test_expect_success 'date parser recognizes time_t overflow' '
+ commit=$(munge_author_date HEAD 18446744073709551614) &&
+ echo "Thu Jan 1 00:00:00 1970 +0000" >expect &&
+ git log -1 --format=%ad $commit >actual &&
+ test_cmp expect actual
+'
+
+# date is within 2^63-1, but enough to choke glibc's gmtime
+test_expect_success 'absurdly far-in-future date' '
+ commit=$(munge_author_date HEAD 999999999999999999) &&
+ git log -1 --format=%ad $commit
+'
+
+test_done
diff --git a/t/t4213-log-tabexpand.sh b/t/t4213-log-tabexpand.sh
new file mode 100755
index 0000000..e01a8f6
--- /dev/null
+++ b/t/t4213-log-tabexpand.sh
@@ -0,0 +1,105 @@
+#!/bin/sh
+
+test_description='log/show --expand-tabs'
+
+. ./test-lib.sh
+
+HT=" "
+title='tab indent at the beginning of the title line'
+body='tab indent on a line in the body'
+
+# usage: count_expand $indent $numSP $numHT @format_args
+count_expand ()
+{
+ expect=
+ count=$(( $1 + $2 )) ;# expected spaces
+ while test $count -gt 0
+ do
+ expect="$expect "
+ count=$(( $count - 1 ))
+ done
+ shift 2
+ count=$1 ;# expected tabs
+ while test $count -gt 0
+ do
+ expect="$expect$HT"
+ count=$(( $count - 1 ))
+ done
+ shift
+
+ # The remainder of the command line is "git show -s" options
+ case " $* " in
+ *' --pretty=short '*)
+ line=$title ;;
+ *)
+ line=$body ;;
+ esac
+
+ # Prefix the output with the command line arguments, and
+ # replace SP with a dot both in the expecte and actual output
+ # so that test_cmp would show the differene together with the
+ # breakage in a way easier to consume by the debugging user.
+ {
+ echo "git show -s $*"
+ echo "$expect$line"
+ } | sed -e 's/ /./g' >expect
+
+ {
+ echo "git show -s $*"
+ git show -s "$@" |
+ sed -n -e "/$line\$/p"
+ } | sed -e 's/ /./g' >actual
+
+ test_cmp expect actual
+}
+
+test_expand ()
+{
+ fmt=$1
+ case "$fmt" in
+ *=raw | *=short | *=email)
+ default="0 1" ;;
+ *)
+ default="8 0" ;;
+ esac
+ case "$fmt" in
+ *=email)
+ in=0 ;;
+ *)
+ in=4 ;;
+ esac
+ test_expect_success "expand/no-expand${fmt:+ for $fmt}" '
+ count_expand $in $default $fmt &&
+ count_expand $in 8 0 $fmt --expand-tabs &&
+ count_expand $in 8 0 --expand-tabs $fmt &&
+ count_expand $in 8 0 $fmt --expand-tabs=8 &&
+ count_expand $in 8 0 --expand-tabs=8 $fmt &&
+ count_expand $in 0 1 $fmt --no-expand-tabs &&
+ count_expand $in 0 1 --no-expand-tabs $fmt &&
+ count_expand $in 0 1 $fmt --expand-tabs=0 &&
+ count_expand $in 0 1 --expand-tabs=0 $fmt &&
+ count_expand $in 4 0 $fmt --expand-tabs=4 &&
+ count_expand $in 4 0 --expand-tabs=4 $fmt
+ '
+}
+
+test_expect_success 'setup' '
+ test_tick &&
+ sed -e "s/Q/$HT/g" <<-EOF >msg &&
+ Q$title
+
+ Q$body
+ EOF
+ git commit --allow-empty -F msg
+'
+
+test_expand ""
+test_expand --pretty
+test_expand --pretty=short
+test_expand --pretty=medium
+test_expand --pretty=full
+test_expand --pretty=fuller
+test_expand --pretty=raw
+test_expand --pretty=email
+
+test_done
diff --git a/t/t4252-am-options.sh b/t/t4252-am-options.sh
index f603c1b..e758e63 100755
--- a/t/t4252-am-options.sh
+++ b/t/t4252-am-options.sh
@@ -59,7 +59,7 @@ test_expect_success 'interrupted am --directory="frotz nitfol"' '
'
test_expect_success 'apply to a funny path' '
- with_sq="with'\''sq"
+ with_sq="with'\''sq" &&
rm -fr .git/rebase-apply &&
git reset --hard initial &&
git am --directory="$with_sq" "$tm"/am-test-5-2 &&
diff --git a/t/t4253-am-keep-cr-dos.sh b/t/t4253-am-keep-cr-dos.sh
index 735e55d..553fe3e 100755
--- a/t/t4253-am-keep-cr-dos.sh
+++ b/t/t4253-am-keep-cr-dos.sh
@@ -62,7 +62,7 @@ test_expect_success 'am with dos files config am.keepcr' '
git diff --exit-code master
'
-test_expect_success 'am with dos files config am.keepcr overriden by --no-keep-cr' '
+test_expect_success 'am with dos files config am.keepcr overridden by --no-keep-cr' '
git config am.keepcr 1 &&
git checkout -b dosfiles-conf-keepcr-override initial &&
git format-patch -k initial..master &&
@@ -83,7 +83,7 @@ test_expect_success 'am with dos files with --keep-cr continue' '
git diff --exit-code master
'
-test_expect_success 'am with unix files config am.keepcr overriden by --no-keep-cr' '
+test_expect_success 'am with unix files config am.keepcr overridden by --no-keep-cr' '
git config am.keepcr 1 &&
git checkout -b unixfiles-conf-keepcr-override initial &&
cp -f file1 file &&
diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh
new file mode 100755
index 0000000..85716dd
--- /dev/null
+++ b/t/t4254-am-corrupt.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+test_description='git am with corrupt input'
+. ./test-lib.sh
+
+test_expect_success setup '
+ # Note the missing "+++" line:
+ cat >bad-patch.diff <<-\EOF &&
+ From: A U Thor <au.thor@example.com>
+ diff --git a/f b/f
+ index 7898192..6178079 100644
+ --- a/f
+ @@ -1 +1 @@
+ -a
+ +b
+ EOF
+
+ echo a >f &&
+ git add f &&
+ test_tick &&
+ git commit -m initial
+'
+
+# This used to fail before, too, but with a different diagnostic.
+# fatal: unable to write file '(null)' mode 100644: Bad address
+# Also, it had the unwanted side-effect of deleting f.
+test_expect_success 'try to apply corrupted patch' '
+ test_must_fail git am bad-patch.diff 2>actual
+'
+
+test_expect_success 'compare diagnostic; ensure file is still here' '
+ echo "fatal: git diff header lacks filename information (line 4)" >expected &&
+ test_path_is_file f &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t4255-am-submodule.sh b/t/t4255-am-submodule.sh
new file mode 100755
index 0000000..0ba8194
--- /dev/null
+++ b/t/t4255-am-submodule.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+test_description='git am handling submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+am () {
+ git format-patch --stdout --ignore-submodules=dirty "..$1" | git am -
+}
+
+test_submodule_switch "am"
+
+am_3way () {
+ git format-patch --stdout --ignore-submodules=dirty "..$1" | git am --3way -
+}
+
+KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+test_submodule_switch "am_3way"
+
+test_expect_success 'setup diff.submodule' '
+ test_commit one &&
+ INITIAL=$(git rev-parse HEAD) &&
+
+ git init submodule &&
+ (
+ cd submodule &&
+ test_commit two &&
+ git rev-parse HEAD >../initial-submodule
+ ) &&
+ git submodule add ./submodule &&
+ git commit -m first &&
+
+ (
+ cd submodule &&
+ test_commit three &&
+ git rev-parse HEAD >../first-submodule
+ ) &&
+ git add submodule &&
+ git commit -m second &&
+ SECOND=$(git rev-parse HEAD) &&
+
+ (
+ cd submodule &&
+ git mv two.t four.t &&
+ git commit -m "second submodule" &&
+ git rev-parse HEAD >../second-submodule
+ ) &&
+ test_commit four &&
+ git add submodule &&
+ git commit --amend --no-edit &&
+ THIRD=$(git rev-parse HEAD) &&
+ git submodule update --init
+'
+
+run_test() {
+ START_COMMIT=$1 &&
+ EXPECT=$2 &&
+ # Abort any merges in progress: the previous
+ # test may have failed, and we should clean up.
+ test_might_fail git am --abort &&
+ git reset --hard $START_COMMIT &&
+ rm -f *.patch &&
+ git format-patch -1 &&
+ git reset --hard $START_COMMIT^ &&
+ git submodule update &&
+ git am *.patch &&
+ git submodule update &&
+ git -C submodule rev-parse HEAD >actual &&
+ test_cmp $EXPECT actual
+}
+
+test_expect_success 'diff.submodule unset' '
+ test_unconfig diff.submodule &&
+ run_test $SECOND first-submodule
+'
+
+test_expect_success 'diff.submodule unset with extra file' '
+ test_unconfig diff.submodule &&
+ run_test $THIRD second-submodule
+'
+
+test_expect_success 'diff.submodule=log' '
+ test_config diff.submodule log &&
+ run_test $SECOND first-submodule
+'
+
+test_expect_success 'diff.submodule=log with extra file' '
+ test_config diff.submodule log &&
+ run_test $THIRD second-submodule
+'
+
+test_done
diff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh
index 46c3fe7..9015e47 100755
--- a/t/t4300-merge-tree.sh
+++ b/t/t4300-merge-tree.sh
@@ -26,8 +26,6 @@ EXPECTED
test_expect_success 'file add !A, B' '
cat >expected <<\EXPECTED &&
-added in local
- our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
EXPECTED
git reset --hard initial &&
@@ -38,9 +36,6 @@ EXPECTED
test_expect_success 'file add A, B (same)' '
cat >expected <<\EXPECTED &&
-added in both
- our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
- their 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
EXPECTED
git reset --hard initial &&
@@ -181,9 +176,6 @@ AAA" &&
test_expect_success 'file remove A, !B' '
cat >expected <<\EXPECTED &&
-removed in local
- base 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
- their 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
EXPECTED
git reset --hard initial &&
@@ -213,6 +205,19 @@ EXPECTED
test_cmp expected actual
'
+test_expect_success 'file remove A, B (same)' '
+ cat >expected <<\EXPECTED &&
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "rm-a-b-base" "ONE" "AAA" &&
+ git rm ONE &&
+ git commit -m "rm-a-b" &&
+ git tag "rm-a-b" &&
+ git merge-tree rm-a-b-base rm-a-b rm-a-b >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'file change A, remove B' '
cat >expected <<\EXPECTED &&
removed in remote
@@ -254,4 +259,97 @@ EXPECTED
test_cmp expected actual
'
+test_expect_success 'tree add A, B (same)' '
+ cat >expect <<-\EOF &&
+ EOF
+ git reset --hard initial &&
+ mkdir sub &&
+ test_commit "add sub/file" "sub/file" "file" add-tree-A &&
+ git merge-tree initial add-tree-A add-tree-A >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'tree add A, B (different)' '
+ cat >expect <<-\EOF &&
+ added in both
+ our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d sub/file
+ their 100644 ba629238ca89489f2b350e196ca445e09d8bb834 sub/file
+ @@ -1 +1,5 @@
+ +<<<<<<< .our
+ AAA
+ +=======
+ +BBB
+ +>>>>>>> .their
+ EOF
+ git reset --hard initial &&
+ mkdir sub &&
+ test_commit "add sub/file" "sub/file" "AAA" add-tree-a-b-A &&
+ git reset --hard initial &&
+ mkdir sub &&
+ test_commit "add sub/file" "sub/file" "BBB" add-tree-a-b-B &&
+ git merge-tree initial add-tree-a-b-A add-tree-a-b-B >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'tree unchanged A, removed B' '
+ cat >expect <<-\EOF &&
+ removed in remote
+ base 100644 43d5a8ed6ef6c00ff775008633f95787d088285d sub/file
+ our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d sub/file
+ @@ -1 +0,0 @@
+ -AAA
+ EOF
+ git reset --hard initial &&
+ mkdir sub &&
+ test_commit "add sub/file" "sub/file" "AAA" tree-remove-b-initial &&
+ git rm sub/file &&
+ test_tick &&
+ git commit -m "remove sub/file" &&
+ git tag tree-remove-b-B &&
+ git merge-tree tree-remove-b-initial tree-remove-b-initial tree-remove-b-B >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'turn file to tree' '
+ git reset --hard initial &&
+ rm initial-file &&
+ mkdir initial-file &&
+ test_commit "turn-file-to-tree" "initial-file/ONE" "CCC" &&
+ git merge-tree initial initial turn-file-to-tree >actual &&
+ cat >expect <<-\EOF &&
+ added in remote
+ their 100644 43aa4fdec31eb92e1fdc2f0ce6ea9ddb7c32bcf7 initial-file/ONE
+ @@ -0,0 +1 @@
+ +CCC
+ removed in remote
+ base 100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file
+ our 100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file
+ @@ -1 +0,0 @@
+ -initial
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'turn tree to file' '
+ git reset --hard initial &&
+ mkdir dir &&
+ test_commit "add-tree" "dir/path" "AAA" &&
+ test_commit "add-another-tree" "dir/another" "BBB" &&
+ rm -fr dir &&
+ test_commit "make-file" "dir" "CCC" &&
+ git merge-tree add-tree add-another-tree make-file >actual &&
+ cat >expect <<-\EOF &&
+ removed in remote
+ base 100644 43d5a8ed6ef6c00ff775008633f95787d088285d dir/path
+ our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d dir/path
+ @@ -1 +0,0 @@
+ -AAA
+ added in remote
+ their 100644 43aa4fdec31eb92e1fdc2f0ce6ea9ddb7c32bcf7 dir
+ @@ -0,0 +1 @@
+ +CCC
+ EOF
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 27bfba5..4b68bba 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -3,7 +3,7 @@
# Copyright (C) 2005 Rene Scharfe
#
-test_description='git tar-tree and git get-tar-commit-id test
+test_description='git archive and git get-tar-commit-id test
This test covers the topics of file contents, commit date handling and
commit id embedding:
@@ -13,11 +13,11 @@ commit id embedding:
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
+ git archive 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
+ When giving git archive 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.
@@ -25,16 +25,83 @@ commit id embedding:
'
. ./test-lib.sh
-UNZIP=${UNZIP:-unzip}
SUBSTFORMAT=%H%n
+test_lazy_prereq TAR_NEEDS_PAX_FALLBACK '
+ (
+ mkdir pax &&
+ cd pax &&
+ "$TAR" xf "$TEST_DIRECTORY"/t5000/pax.tar &&
+ test -f PaxHeaders.1791/file
+ )
+'
+
+test_lazy_prereq GZIP 'gzip --version'
+
+get_pax_header() {
+ file=$1
+ header=$2=
+
+ while read len rest
+ do
+ if test "$len" = $(echo "$len $rest" | wc -c)
+ then
+ case "$rest" in
+ $header*)
+ echo "${rest#$header}"
+ ;;
+ esac
+ fi
+ done <"$file"
+}
+
+check_tar() {
+ tarfile=$1.tar
+ listfile=$1.lst
+ dir=$1
+ dir_with_prefix=$dir/$2
+
+ test_expect_success ' extract tar archive' '
+ (mkdir $dir && cd $dir && "$TAR" xf -) <$tarfile
+ '
+
+ test_expect_success TAR_NEEDS_PAX_FALLBACK ' interpret pax headers' '
+ (
+ cd $dir &&
+ for header in *.paxheader
+ do
+ data=${header%.paxheader}.data &&
+ if test -h $data || test -e $data
+ then
+ path=$(get_pax_header $header path) &&
+ if test -n "$path"
+ then
+ mv "$data" "$path"
+ fi
+ fi
+ done
+ )
+ '
+
+ test_expect_success ' validate filenames' '
+ (cd ${dir_with_prefix}a && find .) | sort >$listfile &&
+ test_cmp a.lst $listfile
+ '
+
+ test_expect_success ' validate file contents' '
+ diff -r a ${dir_with_prefix}a
+ '
+}
+
test_expect_success \
'populate workdir' \
- 'mkdir a b c &&
+ 'mkdir a &&
echo simple textfile >a/a &&
+ ten=0123456789 && hundred=$ten$ten$ten$ten$ten$ten$ten$ten$ten$ten &&
+ echo long filename >a/four$hundred &&
mkdir a/bin &&
- cp /bin/sh a/bin &&
+ test-genrandom "frotz" 500000 >a/bin/sh &&
printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 &&
printf "A not substituted O" >a/substfile2 &&
if test_have_prereq SYMLINKS; then
@@ -52,14 +119,16 @@ test_expect_success \
'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 'add files to repository' '
+ git add a &&
+ GIT_COMMITTER_DATE="2005-05-27 22:00" git commit -m initial
+'
+
+test_expect_success 'setup export-subst' '
+ echo "substfile?" export-subst >>.git/info/attributes &&
+ git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
+ >a/substfile1
+'
test_expect_success \
'create bare clone' \
@@ -74,13 +143,25 @@ test_expect_success \
'git archive' \
'git archive HEAD >b.tar'
-test_expect_success \
- 'git tar-tree' \
- 'git tar-tree HEAD >b2.tar'
+check_tar b
-test_expect_success \
- 'git archive vs. git tar-tree' \
- 'test_cmp b.tar b2.tar'
+test_expect_success 'git archive --prefix=prefix/' '
+ git archive --prefix=prefix/ HEAD >with_prefix.tar
+'
+
+check_tar with_prefix prefix/
+
+test_expect_success 'git-archive --prefix=olde-' '
+ git archive --prefix=olde- HEAD >with_olde-prefix.tar
+'
+
+check_tar with_olde-prefix olde-
+
+test_expect_success 'git archive on large files' '
+ test_config core.bigfilethreshold 1 &&
+ git archive HEAD >b3.tar &&
+ test_cmp_bin b.tar b3.tar
+'
test_expect_success \
'git archive in a bare repo' \
@@ -88,15 +169,15 @@ test_expect_success \
test_expect_success \
'git archive vs. the same in a bare repo' \
- 'test_cmp b.tar b3.tar'
+ 'test_cmp_bin b.tar b3.tar'
test_expect_success 'git archive with --output' \
'git archive --output=b4.tar HEAD &&
- test_cmp b.tar b4.tar'
+ test_cmp_bin b.tar b4.tar'
test_expect_success 'git archive --remote' \
'git archive --remote=. HEAD >b5.tar &&
- test_cmp b.tar b5.tar'
+ test_cmp_bin b.tar b5.tar'
test_expect_success \
'validate file modification time' \
@@ -111,145 +192,131 @@ test_expect_success \
'git get-tar-commit-id <b.tar >b.commitid &&
test_cmp .git/$(git symbolic-ref HEAD) b.commitid'
-test_expect_success \
- 'extract tar archive' \
- '(cd b && "$TAR" xf -) <b.tar'
-
-test_expect_success \
- 'validate filenames' \
- '(cd b/a && find .) | sort >b.lst &&
- test_cmp a.lst b.lst'
-
-test_expect_success \
- 'validate file contents' \
- 'diff -r a b/a'
-
-test_expect_success \
- 'git tar-tree with prefix' \
- 'git tar-tree HEAD prefix >c.tar'
-
-test_expect_success \
- 'extract tar archive with prefix' \
- '(cd c && "$TAR" xf -) <c.tar'
-
-test_expect_success \
- 'validate filenames with prefix' \
- '(cd c/prefix/a && find .) | sort >c.lst &&
- test_cmp a.lst c.lst'
-
-test_expect_success \
- 'validate file contents with prefix' \
- 'diff -r a c/prefix/a'
-
-test_expect_success \
- 'create archives with substfiles' \
- 'cp .git/info/attributes .git/info/attributes.before &&
- echo "substfile?" export-subst >>.git/info/attributes &&
- git archive HEAD >f.tar &&
- git archive --prefix=prefix/ HEAD >g.tar &&
- mv .git/info/attributes.before .git/info/attributes'
+test_expect_success 'git archive with --output, override inferred format' '
+ git archive --format=tar --output=d4.zip HEAD &&
+ test_cmp_bin b.tar d4.zip
+'
test_expect_success \
- 'extract substfiles' \
- '(mkdir f && cd f && "$TAR" xf -) <f.tar'
+ 'git archive --list outside of a git repo' \
+ 'GIT_DIR=some/non-existing/directory git archive --list'
-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 'clients cannot access unreachable commits' '
+ test_commit unreachable &&
+ sha1=$(git rev-parse HEAD) &&
+ git reset --hard HEAD^ &&
+ git archive $sha1 >remote.tar &&
+ test_must_fail git archive --remote=. $sha1 >remote.tar
'
-test_expect_success \
- 'extract substfiles from archive with prefix' \
- '(mkdir g && cd g && "$TAR" xf -) <g.tar'
+test_expect_success 'upload-archive can allow unreachable commits' '
+ test_commit unreachable1 &&
+ sha1=$(git rev-parse HEAD) &&
+ git reset --hard HEAD^ &&
+ git archive $sha1 >remote.tar &&
+ test_config uploadarchive.allowUnreachable true &&
+ git archive --remote=. $sha1 >remote.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 'setup tar filters' '
+ git config tar.tar.foo.command "tr ab ba" &&
+ git config tar.bar.command "tr ab ba" &&
+ git config tar.bar.remote true &&
+ git config tar.invalid baz
'
-test_expect_success \
- 'git archive --format=zip' \
- 'git archive --format=zip HEAD >d.zip'
+test_expect_success 'archive --list mentions user filter' '
+ git archive --list >output &&
+ grep "^tar\.foo\$" output &&
+ grep "^bar\$" output
+'
-test_expect_success \
- 'git archive --format=zip in a bare repo' \
- '(cd bare.git && git archive --format=zip HEAD) >d1.zip'
+test_expect_success 'archive --list shows only enabled remote filters' '
+ git archive --list --remote=. >output &&
+ ! grep "^tar\.foo\$" output &&
+ grep "^bar\$" output
+'
-test_expect_success \
- 'git archive --format=zip vs. the same in a bare repo' \
- 'test_cmp d.zip d1.zip'
+test_expect_success 'invoke tar filter by format' '
+ git archive --format=tar.foo HEAD >config.tar.foo &&
+ tr ab ba <config.tar.foo >config.tar &&
+ test_cmp_bin b.tar config.tar &&
+ git archive --format=bar HEAD >config.bar &&
+ tr ab ba <config.bar >config.tar &&
+ test_cmp_bin b.tar config.tar
+'
-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 'invoke tar filter by extension' '
+ git archive -o config-implicit.tar.foo HEAD &&
+ test_cmp_bin config.tar.foo config-implicit.tar.foo &&
+ git archive -o config-implicit.bar HEAD &&
+ test_cmp_bin config.tar.foo config-implicit.bar
+'
-test_expect_success 'git archive with --output, inferring format' '
- git archive --output=d3.zip HEAD &&
- test_cmp d.zip d3.zip
+test_expect_success 'default output format remains tar' '
+ git archive -o config-implicit.baz HEAD &&
+ test_cmp_bin b.tar config-implicit.baz
'
-test_expect_success 'git archive with --output, override inferred format' '
- git archive --format=tar --output=d4.zip HEAD &&
- test_cmp b.tar d4.zip
+test_expect_success 'extension matching requires dot' '
+ git archive -o config-implicittar.foo HEAD &&
+ test_cmp_bin b.tar config-implicittar.foo
'
-$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 'only enabled filters are available remotely' '
+ test_must_fail git archive --remote=. --format=tar.foo HEAD \
+ >remote.tar.foo &&
+ git archive --remote=. --format=bar >remote.bar HEAD &&
+ test_cmp_bin remote.bar config.bar
+'
-test_expect_success UNZIP \
- 'extract ZIP archive' \
- '(mkdir d && cd d && $UNZIP ../d.zip)'
+test_expect_success GZIP 'git archive --format=tgz' '
+ git archive --format=tgz HEAD >j.tgz
+'
-test_expect_success UNZIP \
- 'validate filenames' \
- '(cd d/a && find .) | sort >d.lst &&
- test_cmp a.lst d.lst'
+test_expect_success GZIP 'git archive --format=tar.gz' '
+ git archive --format=tar.gz HEAD >j1.tar.gz &&
+ test_cmp_bin j.tgz j1.tar.gz
+'
-test_expect_success UNZIP \
- 'validate file contents' \
- 'diff -r a d/a'
+test_expect_success GZIP 'infer tgz from .tgz filename' '
+ git archive --output=j2.tgz HEAD &&
+ test_cmp_bin j.tgz j2.tgz
+'
-test_expect_success \
- 'git archive --format=zip with prefix' \
- 'git archive --format=zip --prefix=prefix/ HEAD >e.zip'
+test_expect_success GZIP 'infer tgz from .tar.gz filename' '
+ git archive --output=j3.tar.gz HEAD &&
+ test_cmp_bin j.tgz j3.tar.gz
+'
-test_expect_success UNZIP \
- 'extract ZIP archive with prefix' \
- '(mkdir e && cd e && $UNZIP ../e.zip)'
+test_expect_success GZIP 'extract tgz file' '
+ gzip -d -c <j.tgz >j.tar &&
+ test_cmp_bin b.tar j.tar
+'
-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 GZIP 'remote tar.gz is allowed by default' '
+ git archive --remote=. --format=tar.gz HEAD >remote.tar.gz &&
+ test_cmp_bin j.tgz remote.tar.gz
+'
-test_expect_success UNZIP \
- 'validate file contents with prefix' \
- 'diff -r a e/prefix/a'
+test_expect_success GZIP 'remote tar.gz can be disabled' '
+ git config tar.tar.gz.remote false &&
+ test_must_fail git archive --remote=. --format=tar.gz HEAD \
+ >remote.tar.gz
+'
-test_expect_success \
- 'git archive --list outside of a git repo' \
- 'GIT_DIR=some/non-existing/directory git archive --list'
+test_expect_success 'archive and :(glob)' '
+ git archive -v HEAD -- ":(glob)**/sh" >/dev/null 2>actual &&
+ cat >expect <<EOF &&
+a/
+a/bin/
+a/bin/sh
+EOF
+ test_cmp expect actual
+'
-test_expect_success 'git-archive --prefix=olde-' '
- git archive --prefix=olde- >h.tar HEAD &&
- (
- mkdir h &&
- cd h &&
- "$TAR" xf - <../h.tar
- ) &&
- test -d h/olde-a &&
- test -d h/olde-a/bin &&
- test -f h/olde-a/bin/sh
+test_expect_success 'catch non-matching pathspec' '
+ test_must_fail git archive -v HEAD -- "*.abc" >/dev/null
'
test_done
diff --git a/t/t5000/pax.tar b/t/t5000/pax.tar
new file mode 100644
index 0000000..d911737
--- /dev/null
+++ b/t/t5000/pax.tar
Binary files differ
diff --git a/t/t5001-archive-attr.sh b/t/t5001-archive-attr.sh
index 426b319..b04d955 100755
--- a/t/t5001-archive-attr.sh
+++ b/t/t5001-archive-attr.sh
@@ -4,7 +4,7 @@ test_description='git archive attribute tests'
. ./test-lib.sh
-SUBSTFORMAT=%H%n
+SUBSTFORMAT='%H (%h)%n'
test_expect_exists() {
test_expect_success " $1 exists" "test -e $1"
@@ -57,9 +57,18 @@ test_expect_missing worktree/ignored
test_expect_exists worktree/ignored-by-tree
test_expect_missing worktree/ignored-by-worktree
+test_expect_success 'git archive --worktree-attributes option' '
+ git archive --worktree-attributes --worktree-attributes HEAD >worktree.tar &&
+ (mkdir worktree2 && cd worktree2 && "$TAR" xf -) <worktree.tar
+'
+
+test_expect_missing worktree2/ignored
+test_expect_exists worktree2/ignored-by-tree
+test_expect_missing worktree2/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_cmp_bin archive.tar bare-archive.tar
'
test_expect_success 'git archive with worktree attributes, bare' '
@@ -78,14 +87,4 @@ test_expect_success 'export-subst' '
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/t5002-archive-attr-pattern.sh b/t/t5002-archive-attr-pattern.sh
new file mode 100755
index 0000000..6667d15
--- /dev/null
+++ b/t/t5002-archive-attr-pattern.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+test_description='git archive attribute pattern tests'
+
+. ./test-lib.sh
+
+test_expect_exists() {
+ test_expect_success " $1 exists" "test -e $1"
+}
+
+test_expect_missing() {
+ test_expect_success " $1 does not exist" "test ! -e $1"
+}
+
+test_expect_success 'setup' '
+ echo ignored >ignored &&
+ echo ignored export-ignore >>.git/info/attributes &&
+ git add ignored &&
+
+ mkdir not-ignored-dir &&
+ echo ignored-in-tree >not-ignored-dir/ignored &&
+ echo not-ignored-in-tree >not-ignored-dir/ignored-only-if-dir &&
+ git add not-ignored-dir &&
+
+ mkdir ignored-only-if-dir &&
+ echo ignored by ignored dir >ignored-only-if-dir/ignored-by-ignored-dir &&
+ echo ignored-only-if-dir/ export-ignore >>.git/info/attributes &&
+ git add ignored-only-if-dir &&
+
+ mkdir -p ignored-without-slash &&
+ echo "ignored without slash" >ignored-without-slash/foo &&
+ git add ignored-without-slash/foo &&
+ echo "ignored-without-slash export-ignore" >>.git/info/attributes &&
+
+ mkdir -p wildcard-without-slash &&
+ echo "ignored without slash" >wildcard-without-slash/foo &&
+ git add wildcard-without-slash/foo &&
+ echo "wild*-without-slash export-ignore" >>.git/info/attributes &&
+
+ mkdir -p deep/and/slashless &&
+ echo "ignored without slash" >deep/and/slashless/foo &&
+ git add deep/and/slashless/foo &&
+ echo "deep/and/slashless export-ignore" >>.git/info/attributes &&
+
+ mkdir -p deep/with/wildcard &&
+ echo "ignored without slash" >deep/with/wildcard/foo &&
+ git add deep/with/wildcard/foo &&
+ echo "deep/*t*/wildcard export-ignore" >>.git/info/attributes &&
+
+ mkdir -p one-level-lower/two-levels-lower/ignored-only-if-dir &&
+ echo ignored by ignored dir >one-level-lower/two-levels-lower/ignored-only-if-dir/ignored-by-ignored-dir &&
+ git add one-level-lower &&
+
+ git commit -m. &&
+
+ git clone --bare . bare &&
+ cp .git/info/attributes bare/info/attributes
+'
+
+test_expect_success 'git archive' '
+ git archive HEAD >archive.tar &&
+ (mkdir archive && cd archive && "$TAR" xf -) <archive.tar
+'
+
+test_expect_missing archive/ignored
+test_expect_missing archive/not-ignored-dir/ignored
+test_expect_exists archive/not-ignored-dir/ignored-only-if-dir
+test_expect_exists archive/not-ignored-dir/
+test_expect_missing archive/ignored-only-if-dir/
+test_expect_missing archive/ignored-ony-if-dir/ignored-by-ignored-dir
+test_expect_missing archive/ignored-without-slash/ &&
+test_expect_missing archive/ignored-without-slash/foo &&
+test_expect_missing archive/wildcard-without-slash/
+test_expect_missing archive/wildcard-without-slash/foo &&
+test_expect_missing archive/deep/and/slashless/ &&
+test_expect_missing archive/deep/and/slashless/foo &&
+test_expect_missing archive/deep/with/wildcard/ &&
+test_expect_missing archive/deep/with/wildcard/foo &&
+test_expect_exists archive/one-level-lower/
+test_expect_missing archive/one-level-lower/two-levels-lower/ignored-only-if-dir/
+test_expect_missing archive/one-level-lower/two-levels-lower/ignored-ony-if-dir/ignored-by-ignored-dir
+
+
+test_done
diff --git a/t/t5003-archive-zip.sh b/t/t5003-archive-zip.sh
new file mode 100755
index 0000000..14744b2
--- /dev/null
+++ b/t/t5003-archive-zip.sh
@@ -0,0 +1,172 @@
+#!/bin/sh
+
+test_description='git archive --format=zip test'
+
+. ./test-lib.sh
+
+SUBSTFORMAT=%H%n
+
+test_lazy_prereq UNZIP_SYMLINKS '
+ (
+ mkdir unzip-symlinks &&
+ cd unzip-symlinks &&
+ "$GIT_UNZIP" "$TEST_DIRECTORY"/t5003/infozip-symlinks.zip &&
+ test -h symlink
+ )
+'
+
+check_zip() {
+ zipfile=$1.zip
+ listfile=$1.lst
+ dir=$1
+ dir_with_prefix=$dir/$2
+
+ test_expect_success UNZIP " extract ZIP archive" '
+ (mkdir $dir && cd $dir && "$GIT_UNZIP" ../$zipfile)
+ '
+
+ test_expect_success UNZIP " validate filenames" "
+ (cd ${dir_with_prefix}a && find .) | sort >$listfile &&
+ test_cmp a.lst $listfile
+ "
+
+ test_expect_success UNZIP " validate file contents" "
+ diff -r a ${dir_with_prefix}a
+ "
+
+ dir=eol_$1
+ dir_with_prefix=$dir/$2
+ extracted=${dir_with_prefix}a
+ original=a
+
+ test_expect_success UNZIP " extract ZIP archive with EOL conversion" '
+ (mkdir $dir && cd $dir && "$GIT_UNZIP" -a ../$zipfile)
+ '
+
+ test_expect_success UNZIP " validate that text files are converted" "
+ test_cmp_bin $extracted/text.cr $extracted/text.crlf &&
+ test_cmp_bin $extracted/text.cr $extracted/text.lf
+ "
+
+ test_expect_success UNZIP " validate that binary files are unchanged" "
+ test_cmp_bin $original/binary.cr $extracted/binary.cr &&
+ test_cmp_bin $original/binary.crlf $extracted/binary.crlf &&
+ test_cmp_bin $original/binary.lf $extracted/binary.lf
+ "
+
+ test_expect_success UNZIP " validate that diff files are converted" "
+ test_cmp_bin $extracted/diff.cr $extracted/diff.crlf &&
+ test_cmp_bin $extracted/diff.cr $extracted/diff.lf
+ "
+
+ test_expect_success UNZIP " validate that -diff files are unchanged" "
+ test_cmp_bin $original/nodiff.cr $extracted/nodiff.cr &&
+ test_cmp_bin $original/nodiff.crlf $extracted/nodiff.crlf &&
+ test_cmp_bin $original/nodiff.lf $extracted/nodiff.lf
+ "
+}
+
+test_expect_success \
+ 'populate workdir' \
+ 'mkdir a &&
+ echo simple textfile >a/a &&
+ mkdir a/bin &&
+ cp /bin/sh a/bin &&
+ printf "text\r" >a/text.cr &&
+ printf "text\r\n" >a/text.crlf &&
+ printf "text\n" >a/text.lf &&
+ printf "text\r" >a/nodiff.cr &&
+ printf "text\r\n" >a/nodiff.crlf &&
+ printf "text\n" >a/nodiff.lf &&
+ printf "\0\r" >a/binary.cr &&
+ printf "\0\r\n" >a/binary.crlf &&
+ printf "\0\n" >a/binary.lf &&
+ printf "\0\r" >a/diff.cr &&
+ printf "\0\r\n" >a/diff.crlf &&
+ printf "\0\n" >a/diff.lf &&
+ printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 &&
+ printf "A not substituted O" >a/substfile2 &&
+ (p=long_path_to_a_file && cd a &&
+ for depth in 1 2 3 4 5; do mkdir $p && cd $p; done &&
+ echo text >file_with_long_path)
+'
+
+test_expect_success SYMLINKS,UNZIP_SYMLINKS 'add symlink' '
+ ln -s a a/symlink_to_a
+'
+
+test_expect_success 'prepare file list' '
+ (cd a && find .) | sort >a.lst
+'
+
+test_expect_success \
+ 'add ignored file' \
+ 'echo ignore me >a/ignored &&
+ echo ignored export-ignore >.git/info/attributes'
+
+test_expect_success 'add files to repository' '
+ git add a &&
+ GIT_COMMITTER_DATE="2005-05-27 22:00" git commit -m initial
+'
+
+test_expect_success 'setup export-subst and diff attributes' '
+ echo "a/nodiff.* -diff" >>.git/info/attributes &&
+ echo "a/diff.* diff" >>.git/info/attributes &&
+ echo "substfile?" export-subst >>.git/info/attributes &&
+ git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
+ >a/substfile1
+'
+
+test_expect_success \
+ 'create bare clone' \
+ 'git clone --bare . bare.git &&
+ cp .git/info/attributes bare.git/info/attributes'
+
+test_expect_success \
+ 'remove ignored file' \
+ 'rm a/ignored'
+
+test_expect_success \
+ 'git archive --format=zip' \
+ 'git archive --format=zip HEAD >d.zip'
+
+check_zip d
+
+test_expect_success \
+ 'git archive --format=zip in a bare repo' \
+ '(cd bare.git && git archive --format=zip HEAD) >d1.zip'
+
+test_expect_success \
+ 'git archive --format=zip vs. the same in a bare repo' \
+ 'test_cmp_bin d.zip d1.zip'
+
+test_expect_success 'git archive --format=zip with --output' \
+ 'git archive --format=zip --output=d2.zip HEAD &&
+ test_cmp_bin d.zip d2.zip'
+
+test_expect_success 'git archive with --output, inferring format' '
+ git archive --output=d3.zip HEAD &&
+ test_cmp_bin d.zip d3.zip
+'
+
+test_expect_success \
+ 'git archive --format=zip with prefix' \
+ 'git archive --format=zip --prefix=prefix/ HEAD >e.zip'
+
+check_zip e prefix/
+
+test_expect_success 'git archive -0 --format=zip on large files' '
+ test_config core.bigfilethreshold 1 &&
+ git archive -0 --format=zip HEAD >large.zip
+'
+
+check_zip large
+
+test_expect_success 'git archive --format=zip on large files' '
+ test_config core.bigfilethreshold 1 &&
+ git archive --format=zip HEAD >large-compressed.zip
+'
+
+check_zip large-compressed
+
+test_done
diff --git a/t/t5003/infozip-symlinks.zip b/t/t5003/infozip-symlinks.zip
new file mode 100644
index 0000000..065728c
--- /dev/null
+++ b/t/t5003/infozip-symlinks.zip
Binary files differ
diff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh
new file mode 100755
index 0000000..cca2338
--- /dev/null
+++ b/t/t5004-archive-corner-cases.sh
@@ -0,0 +1,158 @@
+#!/bin/sh
+
+test_description='test corner cases of git-archive'
+. ./test-lib.sh
+
+test_expect_success 'create commit with empty tree' '
+ git commit --allow-empty -m foo
+'
+
+# Make a dir and clean it up afterwards
+make_dir() {
+ mkdir "$1" &&
+ test_when_finished "rm -rf '$1'"
+}
+
+# Check that the dir given in "$1" contains exactly the
+# set of paths given as arguments.
+check_dir() {
+ dir=$1; shift
+ {
+ echo "$dir" &&
+ for i in "$@"; do
+ echo "$dir/$i"
+ done
+ } | sort >expect &&
+ find "$dir" ! -name pax_global_header -print | sort >actual &&
+ test_cmp expect actual
+}
+
+
+# bsdtar/libarchive versions before 3.1.3 consider a tar file with a
+# global pax header that is not followed by a file record as corrupt.
+if "$TAR" tf "$TEST_DIRECTORY"/t5004/empty-with-pax-header.tar >/dev/null 2>&1
+then
+ test_set_prereq HEADER_ONLY_TAR_OK
+fi
+
+test_expect_success HEADER_ONLY_TAR_OK 'tar archive of commit with empty tree' '
+ git archive --format=tar HEAD >empty-with-pax-header.tar &&
+ make_dir extract &&
+ "$TAR" xf empty-with-pax-header.tar -C extract &&
+ check_dir extract
+'
+
+test_expect_success 'tar archive of empty tree is empty' '
+ git archive --format=tar HEAD: >empty.tar &&
+ perl -e "print \"\\0\" x 10240" >10knuls.tar &&
+ test_cmp_bin 10knuls.tar empty.tar
+'
+
+test_expect_success 'tar archive of empty tree with prefix' '
+ git archive --format=tar --prefix=foo/ HEAD >prefix.tar &&
+ make_dir extract &&
+ "$TAR" xf prefix.tar -C extract &&
+ check_dir extract foo
+'
+
+test_expect_success UNZIP 'zip archive of empty tree is empty' '
+ # Detect the exit code produced when our particular flavor of unzip
+ # sees an empty archive. Infozip will generate a warning and exit with
+ # code 1. But in the name of sanity, we do not expect other unzip
+ # implementations to do the same thing (it would be perfectly
+ # reasonable to exit 0, for example).
+ #
+ # This makes our test less rigorous on some platforms (unzip may not
+ # handle the empty repo at all, making our later check of its exit code
+ # a no-op). But we cannot do anything reasonable except skip the test
+ # on such platforms anyway, and this is the moral equivalent.
+ {
+ "$GIT_UNZIP" "$TEST_DIRECTORY"/t5004/empty.zip
+ expect_code=$?
+ } &&
+
+ git archive --format=zip HEAD >empty.zip &&
+ make_dir extract &&
+ (
+ cd extract &&
+ test_expect_code $expect_code "$GIT_UNZIP" ../empty.zip
+ ) &&
+ check_dir extract
+'
+
+test_expect_success UNZIP 'zip archive of empty tree with prefix' '
+ # We do not have to play exit-code tricks here, because our
+ # result should not be empty; it has a directory in it.
+ git archive --format=zip --prefix=foo/ HEAD >prefix.zip &&
+ make_dir extract &&
+ (
+ cd extract &&
+ "$GIT_UNZIP" ../prefix.zip
+ ) &&
+ check_dir extract foo
+'
+
+test_expect_success 'archive complains about pathspec on empty tree' '
+ test_must_fail git archive --format=tar HEAD -- foo >/dev/null
+'
+
+test_expect_success 'create a commit with an empty subtree' '
+ empty_tree=$(git hash-object -t tree /dev/null) &&
+ root_tree=$(printf "040000 tree $empty_tree\tsub\n" | git mktree)
+'
+
+test_expect_success 'archive empty subtree with no pathspec' '
+ git archive --format=tar $root_tree >subtree-all.tar &&
+ make_dir extract &&
+ "$TAR" xf subtree-all.tar -C extract &&
+ check_dir extract sub
+'
+
+test_expect_success 'archive empty subtree by direct pathspec' '
+ git archive --format=tar $root_tree -- sub >subtree-path.tar &&
+ make_dir extract &&
+ "$TAR" xf subtree-path.tar -C extract &&
+ check_dir extract sub
+'
+
+ZIPINFO=zipinfo
+
+test_lazy_prereq ZIPINFO '
+ n=$("$ZIPINFO" "$TEST_DIRECTORY"/t5004/empty.zip | sed -n "2s/.* //p")
+ test "x$n" = "x0"
+'
+
+test_expect_success ZIPINFO 'zip archive with many entries' '
+ # add a directory with 256 files
+ mkdir 00 &&
+ for a in 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ do
+ for b in 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ do
+ : >00/$a$b
+ done
+ done &&
+ git add 00 &&
+ git commit -m "256 files in 1 directory" &&
+
+ # duplicate it to get 65536 files in 256 directories
+ subtree=$(git write-tree --prefix=00/) &&
+ for c in 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ do
+ for d in 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ do
+ echo "040000 tree $subtree $c$d"
+ done
+ done >tree &&
+ tree=$(git mktree <tree) &&
+
+ # zip them
+ git archive -o many.zip $tree &&
+
+ # check the number of entries in the ZIP file directory
+ expr 65536 + 256 >expect &&
+ "$ZIPINFO" many.zip | head -2 | sed -n "2s/.* //p" >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t5004/empty-with-pax-header.tar b/t/t5004/empty-with-pax-header.tar
new file mode 100644
index 0000000..da9e39e
--- /dev/null
+++ b/t/t5004/empty-with-pax-header.tar
Binary files differ
diff --git a/t/t5004/empty.zip b/t/t5004/empty.zip
new file mode 100644
index 0000000..1a76bb6
--- /dev/null
+++ b/t/t5004/empty.zip
Binary files differ
diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
index ebc36c1..85b3df5 100755
--- a/t/t5100-mailinfo.sh
+++ b/t/t5100-mailinfo.sh
@@ -9,9 +9,9 @@ test_description='git mailinfo and git mailsplit test'
test_expect_success 'split sample box' \
'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last &&
- last=`cat last` &&
+ last=$(cat last) &&
echo total is $last &&
- test `cat last` = 16'
+ test $(cat last) = 17'
check_mailinfo () {
mail=$1 opt=$2
@@ -23,7 +23,7 @@ check_mailinfo () {
}
-for mail in `echo 00*`
+for mail in 00*
do
test_expect_success "mailinfo $mail" '
check_mailinfo $mail "" &&
@@ -34,6 +34,10 @@ do
if test -f "$TEST_DIRECTORY"/t5100/msg$mail--no-inbody-headers
then
check_mailinfo $mail --no-inbody-headers
+ fi &&
+ if test -f "$TEST_DIRECTORY"/t5100/msg$mail--message-id
+ then
+ check_mailinfo $mail --message-id
fi
'
done
@@ -43,11 +47,11 @@ 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` &&
+ last=$(cat rfc2047/last) &&
echo total is $last &&
- test `cat rfc2047/last` = 11'
+ test $(cat rfc2047/last) = 11'
-for mail in `echo rfc2047/00*`
+for mail in rfc2047/00*
do
test_expect_success "mailinfo $mail" '
git mailinfo -u $mail-msg $mail-patch <$mail >$mail-info &&
@@ -65,7 +69,7 @@ test_expect_success 'respect NULs' '
git mailsplit -d3 -o. "$TEST_DIRECTORY"/t5100/nul-plain &&
test_cmp "$TEST_DIRECTORY"/t5100/nul-plain 001 &&
(cat 001 | git mailinfo msg patch) &&
- test 4 = $(wc -l < patch)
+ test_line_count = 4 patch
'
@@ -89,4 +93,22 @@ test_expect_success 'mailinfo on from header without name works' '
'
+test_expect_success 'mailinfo finds headers after embedded From line' '
+ mkdir embed-from &&
+ git mailsplit -oembed-from "$TEST_DIRECTORY"/t5100/embed-from.in &&
+ test_cmp "$TEST_DIRECTORY"/t5100/embed-from.in embed-from/0001 &&
+ git mailinfo embed-from/msg embed-from/patch \
+ <embed-from/0001 >embed-from/out &&
+ test_cmp "$TEST_DIRECTORY"/t5100/embed-from.expect embed-from/out
+'
+
+test_expect_success 'mailinfo on message with quoted >From' '
+ mkdir quoted-from &&
+ git mailsplit -oquoted-from "$TEST_DIRECTORY"/t5100/quoted-from.in &&
+ test_cmp "$TEST_DIRECTORY"/t5100/quoted-from.in quoted-from/0001 &&
+ git mailinfo quoted-from/msg quoted-from/patch \
+ <quoted-from/0001 >quoted-from/out &&
+ test_cmp "$TEST_DIRECTORY"/t5100/quoted-from.expect quoted-from/msg
+'
+
test_done
diff --git a/t/t5100/embed-from.expect b/t/t5100/embed-from.expect
new file mode 100644
index 0000000..06a3a38
--- /dev/null
+++ b/t/t5100/embed-from.expect
@@ -0,0 +1,5 @@
+Author: Commit Author
+Email: commit@example.com
+Subject: patch subject
+Date: Sat, 13 Sep 2014 21:13:23 -0400
+
diff --git a/t/t5100/embed-from.in b/t/t5100/embed-from.in
new file mode 100644
index 0000000..5f3f84e
--- /dev/null
+++ b/t/t5100/embed-from.in
@@ -0,0 +1,13 @@
+From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001
+From: Email Author <email@example.com>
+Date: Sun, 25 May 2008 00:38:18 -0700
+Subject: [PATCH] email subject
+
+>From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001
+From: Commit Author <commit@example.com>
+Date: Sat, 13 Sep 2014 21:13:23 -0400
+Subject: patch subject
+
+patch body
+---
+patch
diff --git a/t/t5100/info0012--message-id b/t/t5100/info0012--message-id
new file mode 100644
index 0000000..ac1216f
--- /dev/null
+++ b/t/t5100/info0012--message-id
@@ -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/info0017 b/t/t5100/info0017
new file mode 100644
index 0000000..d2bc89f
--- /dev/null
+++ b/t/t5100/info0017
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: A E I O U
+Date: Mon, 17 Sep 2012 14:23:44 -0700
+
diff --git a/t/t5100/msg0012--message-id b/t/t5100/msg0012--message-id
new file mode 100644
index 0000000..376e26e
--- /dev/null
+++ b/t/t5100/msg0012--message-id
@@ -0,0 +1,8 @@
+textlive-* иÑправлены на texlive-*
+docutils заменён на python-docutils
+
+ДейÑтвительно, оказалоÑÑŒ, что rest2web вытÑгивает за Ñобой
+python-docutils. Ð’ то Ð²Ñ€ÐµÐ¼Ñ ÐºÐ°Ðº Ñам rest2web не нужен.
+
+Signed-off-by: Dmitriy Blinov <bda@mnsspb.ru>
+Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
diff --git a/t/t5100/msg0017 b/t/t5100/msg0017
new file mode 100644
index 0000000..2ee0900
--- /dev/null
+++ b/t/t5100/msg0017
@@ -0,0 +1,2 @@
+New content here
+
diff --git a/t/t5100/patch0001 b/t/t5100/patch0001
index 8ce1551..02c9774 100644
--- a/t/t5100/patch0001
+++ b/t/t5100/patch0001
@@ -1,5 +1,5 @@
---
- foo | 2 +-
+ foo | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/foo b/foo
diff --git a/t/t5100/patch0002 b/t/t5100/patch0002
index 8ce1551..02c9774 100644
--- a/t/t5100/patch0002
+++ b/t/t5100/patch0002
@@ -1,5 +1,5 @@
---
- foo | 2 +-
+ foo | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/foo b/foo
diff --git a/t/t5100/patch0003 b/t/t5100/patch0003
index 8ce1551..02c9774 100644
--- a/t/t5100/patch0003
+++ b/t/t5100/patch0003
@@ -1,5 +1,5 @@
---
- foo | 2 +-
+ foo | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/foo b/foo
diff --git a/t/t5100/patch0005 b/t/t5100/patch0005
index 7d24b24..ab7a383 100644
--- a/t/t5100/patch0005
+++ b/t/t5100/patch0005
@@ -1,7 +1,7 @@
---
- Documentation/git-cvsimport-script.txt | 9 ++++++++-
- git-cvsimport-script | 4 ++--
+ Documentation/git-cvsimport-script.txt | 9 ++++++++-
+ git-cvsimport-script | 4 ++--
2 files changed, 10 insertions(+), 3 deletions(-)
50452f9c0c2df1f04d83a26266ba704b13861632
diff --git a/t/t5100/patch0006 b/t/t5100/patch0006
index 8ce1551..02c9774 100644
--- a/t/t5100/patch0006
+++ b/t/t5100/patch0006
@@ -1,5 +1,5 @@
---
- foo | 2 +-
+ foo | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/foo b/foo
diff --git a/t/t5100/patch0010 b/t/t5100/patch0010
index f055481..436821c 100644
--- a/t/t5100/patch0010
+++ b/t/t5100/patch0010
@@ -1,5 +1,5 @@
---
- builtin-mailinfo.c | 2 +-
+ builtin-mailinfo.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
diff --git a/t/t5100/patch0011 b/t/t5100/patch0011
index 8841d3c..0988713 100644
--- a/t/t5100/patch0011
+++ b/t/t5100/patch0011
@@ -1,5 +1,5 @@
---
- builtin-mailinfo.c | 4 ++--
+ builtin-mailinfo.c | 4 ++--
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index 3e5fe51..aabfe5c 100644
diff --git a/t/t5100/patch0012--message-id b/t/t5100/patch0012--message-id
new file mode 100644
index 0000000..36a0b68
--- /dev/null
+++ b/t/t5100/patch0012--message-id
@@ -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/patch0014 b/t/t5100/patch0014
index 124efd2..3f3825f 100644
--- a/t/t5100/patch0014
+++ b/t/t5100/patch0014
@@ -1,5 +1,5 @@
---
- builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
1 files changed, 36 insertions(+), 1 deletions(-)
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
diff --git a/t/t5100/patch0014--scissors b/t/t5100/patch0014--scissors
index 124efd2..3f3825f 100644
--- a/t/t5100/patch0014--scissors
+++ b/t/t5100/patch0014--scissors
@@ -1,5 +1,5 @@
---
- builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
1 files changed, 36 insertions(+), 1 deletions(-)
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
diff --git a/t/t5100/patch0017 b/t/t5100/patch0017
new file mode 100644
index 0000000..35cf84c
--- /dev/null
+++ b/t/t5100/patch0017
@@ -0,0 +1,6 @@
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++New content
diff --git a/t/t5100/quoted-from.expect b/t/t5100/quoted-from.expect
new file mode 100644
index 0000000..8c9d48c
--- /dev/null
+++ b/t/t5100/quoted-from.expect
@@ -0,0 +1,3 @@
+>From the depths of history, we are stuck with the
+flaky mbox format.
+
diff --git a/t/t5100/quoted-from.in b/t/t5100/quoted-from.in
new file mode 100644
index 0000000..847e1c4
--- /dev/null
+++ b/t/t5100/quoted-from.in
@@ -0,0 +1,10 @@
+From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001
+From: Author Name <somebody@example.com>
+Date: Sun, 25 May 2008 00:38:18 -0700
+Subject: [PATCH] testing quoted >From
+
+>From the depths of history, we are stuck with the
+flaky mbox format.
+
+---
+patch
diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox
index de10312..8b2ae06 100644
--- a/t/t5100/sample.mbox
+++ b/t/t5100/sample.mbox
@@ -12,7 +12,7 @@ Subject: [PATCH] a commit.
Here is a patch from A U Thor.
---
- foo | 2 +-
+ foo | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/foo b/foo
@@ -52,7 +52,7 @@ two truly blank and another full of spaces in between.
Hope this helps.
---
- foo | 2 +-
+ foo | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/foo b/foo
@@ -83,7 +83,7 @@ Message-Id: <nitpicker.12121212@example.net>
Hopefully this would fix the problem stated there.
---
- foo | 2 +-
+ foo | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/foo b/foo
@@ -249,8 +249,8 @@ actual flags.
Signed-off-by: David K=E5gedal <davidk@lysator.liu.se>
---
- Documentation/git-cvsimport-script.txt | 9 ++++++++-
- git-cvsimport-script | 4 ++--
+ Documentation/git-cvsimport-script.txt | 9 ++++++++-
+ git-cvsimport-script | 4 ++--
2 files changed, 10 insertions(+), 3 deletions(-)
50452f9c0c2df1f04d83a26266ba704b13861632
@@ -379,7 +379,7 @@ Subject: [PATCH] a commit.
Here is a patch from A U Thor.
---
- foo | 2 +-
+ foo | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/foo b/foo
@@ -449,7 +449,7 @@ memcmp("Subject: ", header[i], 7) will never match.
Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
- builtin-mailinfo.c | 2 +-
+ builtin-mailinfo.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
@@ -482,7 +482,7 @@ Content-Transfer-Encoding: quoted-printable
Here comes a commit log message, and
its second line is here.
---
- builtin-mailinfo.c | 4 ++--
+ builtin-mailinfo.c | 4 ++--
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index 3e5fe51..aabfe5c 100644
@@ -587,7 +587,7 @@ everything before it in the message body.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
- builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
1 files changed, 36 insertions(+), 1 deletions(-)
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
@@ -683,3 +683,19 @@ index e69de29..d95f3ad 100644
@@ -0,0 +1 @@
+content
+From nobody Mon Sep 17 00:00:00 2001
+From: A U Thor <a.u.thor@example.com>
+Subject: A E I O U
+Date: Mon, 17 Sep 2012 14:23:44 -0700
+MIME-Version: 1.0
+Content-Type: text/plain; charset="iso-2022-jp"
+Content-type: text/plain; charset="UTF-8"
+
+New content here
+
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++New content
diff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh
index 9cc0a42..82c33b8 100755
--- a/t/t5150-request-pull.sh
+++ b/t/t5150-request-pull.sh
@@ -67,9 +67,11 @@ test_expect_success 'setup: two scripts for reading pull requests' '
cat <<-\EOT >read-request.sed &&
#!/bin/sed -nf
+ # Note that a request could ask for "tag $tagname"
/ in the git repository at:$/!d
n
/^$/ n
+ s/ tag \([^ ]*\)$/ tag--\1/
s/^[ ]*\(.*\) \([^ ]*\)/please pull\
\1\
\2/p
@@ -78,21 +80,22 @@ test_expect_success 'setup: two scripts for reading pull requests' '
cat <<-EOT >fuzz.sed
#!/bin/sed -nf
+ s/$downstream_url_for_sed/URL/g
s/$_x40/OBJECT_NAME/g
s/A U Thor/AUTHOR/g
s/[-0-9]\{10\} [:0-9]\{8\} [-+][0-9]\{4\}/DATE/g
s/ [^ ].*/ SUBJECT/g
s/ [^ ].* (DATE)/ SUBJECT (DATE)/g
- s/$downstream_url_for_sed/URL/g
- s/for-upstream/BRANCH/g
+ s|tags/full|BRANCH|g
s/mnemonic.txt/FILENAME/g
+ s/^version [0-9]/VERSION/
/^ FILENAME | *[0-9]* [-+]*\$/ b diffstat
/^AUTHOR ([0-9]*):\$/ b shortlog
p
b
: diffstat
n
- / [0-9]* files changed/ {
+ / [0-9]* files* changed/ {
a\\
DIFFSTAT
b
@@ -124,7 +127,7 @@ test_expect_success 'pull request when forgot to push' '
test_must_fail git request-pull initial "$downstream_url" \
2>../err
) &&
- grep "No branch of.*is at:\$" err &&
+ grep "No match for commit .*" err &&
grep "Are you sure you pushed" err
'
@@ -138,7 +141,7 @@ test_expect_success 'pull request after push' '
git checkout initial &&
git merge --ff-only master &&
git push origin master:for-upstream &&
- git request-pull initial origin >../request
+ git request-pull initial origin master:for-upstream >../request
) &&
sed -nf read-request.sed <request >digest &&
cat digest &&
@@ -157,7 +160,7 @@ test_expect_success 'pull request after push' '
'
-test_expect_success 'request names an appropriate branch' '
+test_expect_success 'request asks HEAD to be pulled' '
rm -fr downstream.git &&
git init --bare downstream.git &&
@@ -176,10 +179,7 @@ test_expect_success 'request names an appropriate branch' '
read repository &&
read branch
} <digest &&
- {
- test "$branch" = master ||
- test "$branch" = for-upstream
- }
+ test -z "$branch"
'
@@ -193,8 +193,17 @@ test_expect_success 'pull request format' '
SUBJECT (DATE)
are available in the git repository at:
+
URL BRANCH
+ for you to fetch changes up to OBJECT_NAME:
+
+ SUBJECT (DATE)
+
+ ----------------------------------------------------------------
+ VERSION
+
+ ----------------------------------------------------------------
SHORTLOG
DIFFSTAT
@@ -203,12 +212,24 @@ test_expect_success 'pull request format' '
cd local &&
git checkout initial &&
git merge --ff-only master &&
- git push origin master:for-upstream &&
- git request-pull initial "$downstream_url" >../request
+ git push origin tags/full &&
+ git request-pull initial "$downstream_url" tags/full >../request
) &&
<request sed -nf fuzz.sed >request.fuzzy &&
- test_cmp expect request.fuzzy
+ test_i18ncmp expect request.fuzzy &&
+
+ (
+ cd local &&
+ git request-pull initial "$downstream_url" tags/full:refs/tags/full
+ ) >request &&
+ sed -nf fuzz.sed <request >request.fuzzy &&
+ test_i18ncmp expect request.fuzzy &&
+ (
+ cd local &&
+ git request-pull initial "$downstream_url" full
+ ) >request &&
+ grep " tags/full\$" request
'
test_expect_success 'request-pull ignores OPTIONS_KEEPDASHDASH poison' '
@@ -220,7 +241,7 @@ test_expect_success 'request-pull ignores OPTIONS_KEEPDASHDASH poison' '
git checkout initial &&
git merge --ff-only master &&
git push origin master:for-upstream &&
- git request-pull -- initial "$downstream_url" >../request
+ git request-pull -- initial "$downstream_url" master:for-upstream >../request
)
'
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index bbb9c12..899e52d 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -8,11 +8,11 @@ test_description='git pack-object
'
. ./test-lib.sh
-TRASH=`pwd`
+TRASH=$(pwd)
test_expect_success \
'setup' \
- 'rm -f .git/index*
+ '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 &&
@@ -20,8 +20,8 @@ test_expect_success \
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` && {
+ 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/"
@@ -29,7 +29,7 @@ test_expect_success \
git diff-tree --root -p $commit &&
while read object
do
- t=`git cat-file -t $object` &&
+ t=$(git cat-file -t $object) &&
git cat-file $t $object || return 1
done <obj-list
} >expect'
@@ -38,6 +38,10 @@ test_expect_success \
'pack without delta' \
'packname_1=$(git pack-objects --window=0 test-1 <obj-list)'
+test_expect_success \
+ 'pack-objects with bogus arguments' \
+ 'test_must_fail git pack-objects --window=0 test-1 blah blah <obj-list'
+
rm -fr .git2
mkdir .git2
@@ -143,11 +147,11 @@ test_expect_success \
git diff-tree --root -p $commit &&
while read object
do
- t=`git cat-file -t $object` &&
+ t=$(git cat-file -t $object) &&
git cat-file $t $object || return 1
done <obj-list
} >current &&
- test_cmp expect current'
+ cmp expect current'
test_expect_success \
'use packed deltified (REF_DELTA) objects' \
@@ -158,11 +162,11 @@ test_expect_success \
git diff-tree --root -p $commit &&
while read object
do
- t=`git cat-file -t $object` &&
+ t=$(git cat-file -t $object) &&
git cat-file $t $object || return 1
done <obj-list
} >current &&
- test_cmp expect current'
+ cmp expect current'
test_expect_success \
'use packed deltified (OFS_DELTA) objects' \
@@ -173,11 +177,11 @@ test_expect_success \
git diff-tree --root -p $commit &&
while read object
do
- t=`git cat-file -t $object` &&
+ t=$(git cat-file -t $object) &&
git cat-file $t $object || return 1
done <obj-list
} >current &&
- test_cmp expect current'
+ cmp expect current'
unset GIT_OBJECT_DIRECTORY
@@ -191,9 +195,9 @@ test_expect_success 'survive missing objects/pack directory' '
rm -fr $GOP &&
git index-pack --stdin --keep=test <../test-3-${packname_3}.pack &&
test -f $GOP/pack-${packname_3}.pack &&
- test_cmp $GOP/pack-${packname_3}.pack ../test-3-${packname_3}.pack &&
+ cmp $GOP/pack-${packname_3}.pack ../test-3-${packname_3}.pack &&
test -f $GOP/pack-${packname_3}.idx &&
- test_cmp $GOP/pack-${packname_3}.idx ../test-3-${packname_3}.idx &&
+ cmp $GOP/pack-${packname_3}.idx ../test-3-${packname_3}.idx &&
test -f $GOP/pack-${packname_3}.keep
)
'
@@ -248,8 +252,8 @@ test_expect_success \
test_expect_success \
'verify-pack catches a corrupted sum of the index file itself' \
- 'l=`wc -c <test-3.idx` &&
- l=`expr $l - 20` &&
+ '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
@@ -280,6 +284,12 @@ test_expect_success \
git index-pack test-3.pack &&
cmp test-3.idx test-3-${packname_3}.idx &&
+ cat test-1-${packname_1}.pack >test-4.pack &&
+ rm -f test-4.keep &&
+ git index-pack --keep=why test-4.pack &&
+ cmp test-1-${packname_1}.idx test-4.idx &&
+ test -f test-4.keep &&
+
:'
test_expect_success 'unpacking with --strict' '
@@ -412,6 +422,11 @@ test_expect_success \
test_expect_success \
'make sure index-pack detects the SHA1 collision' \
'test_must_fail git index-pack -o bad.idx test-3.pack 2>msg &&
- grep "SHA1 COLLISION FOUND" msg'
+ test_i18ngrep "SHA1 COLLISION FOUND" msg'
+
+test_expect_success \
+ 'make sure index-pack detects the SHA1 collision (large blobs)' \
+ 'test_must_fail git -c core.bigfilethreshold=1 index-pack -o bad.idx test-3.pack 2>msg &&
+ test_i18ngrep "SHA1 COLLISION FOUND" msg'
test_done
diff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh
index 0a24e61..cae8c2e 100755
--- a/t/t5301-sliding-window.sh
+++ b/t/t5301-sliding-window.sh
@@ -8,7 +8,7 @@ test_description='mmap sliding window tests'
test_expect_success \
'setup' \
- 'rm -f .git/index*
+ 'rm -f .git/index* &&
for i in a b c
do
echo $i >$i &&
@@ -16,12 +16,12 @@ test_expect_success \
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` &&
+ 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 "$(git count-objects)" = "0 objects, 0 kilobytes" &&
+ pack1=$(ls .git/objects/pack/*.pack) &&
test -f "$pack1"'
test_expect_success \
@@ -43,12 +43,12 @@ 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` &&
+ 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 "$(git count-objects)" = "0 objects, 0 kilobytes" &&
+ pack2=$(ls .git/objects/pack/*.pack) &&
+ test -f "$pack2" &&
test "$pack1" \!= "$pack2"'
test_expect_success \
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index fb3a270..c2fc584 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -8,27 +8,27 @@ test_description='pack index with 64-bit offsets and object CRC'
test_expect_success \
'setup' \
- 'rm -rf .git
+ 'rm -rf .git &&
git init &&
git config pack.threads 1 &&
i=1 &&
while test $i -le 100
do
- iii=`printf '%03i' $i`
+ 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 &&
+ 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
+ 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` && {
+ 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 &&
@@ -65,6 +65,18 @@ test_expect_success \
'cmp "test-1-${pack1}.idx" "1.idx" &&
cmp "test-2-${pack2}.idx" "2.idx"'
+test_expect_success 'index-pack --verify on index version 1' '
+ git index-pack --verify "test-1-${pack1}.pack"
+'
+
+test_expect_success 'index-pack --verify on index version 2' '
+ git index-pack --verify "test-2-${pack2}.pack"
+'
+
+test_expect_success \
+ 'pack-objects --index-version=2, is not accepted' \
+ 'test_must_fail git pack-objects --index-version=2, test-3 <obj-list'
+
test_expect_success \
'index v2: force some 64-bit offsets with pack-objects' \
'pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list)'
@@ -93,6 +105,16 @@ test_expect_success OFF64_T \
'64-bit offsets: index-pack result should match pack-objects one' \
'cmp "test-3-${pack3}.idx" "3.idx"'
+test_expect_success OFF64_T 'index-pack --verify on 64-bit offset v2 (cheat)' '
+ # This cheats by knowing which lower offset should still be encoded
+ # in 64-bit representation.
+ git index-pack --verify --index-version=2,0x40000 "test-3-${pack3}.pack"
+'
+
+test_expect_success OFF64_T 'index-pack --verify on 64-bit offset v2' '
+ git index-pack --verify "test-3-${pack3}.pack"
+'
+
# returns the object number for given object in given pack index
index_obj_nr()
{
@@ -130,10 +152,10 @@ 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` &&
+ 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" \
@@ -152,11 +174,11 @@ test_expect_success \
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 -f "test-4-${pack4}.pack"'
test_expect_success \
'[index v1] 6) newly created pack is BAD !' \
- 'test_must_fail git verify-pack -v "test-4-${pack1}.pack"'
+ 'test_must_fail git verify-pack -v "test-4-${pack4}.pack"'
test_expect_success \
'[index v2] 1) stream pack to repository' \
@@ -171,10 +193,10 @@ 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` &&
+ 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" \
@@ -200,17 +222,16 @@ test_expect_success \
'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` &&
+ 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)) &&
+ 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_must_fail git verify-pack ".git/objects/pack/pack-${pack1}.pack"
+'
test_expect_success 'running index-pack in the object store' '
rm -f .git/objects/pack/* &&
@@ -222,4 +243,23 @@ test_expect_success 'running index-pack in the object store' '
test -f .git/objects/pack/pack-${pack1}.idx
'
+test_expect_success 'index-pack --strict warns upon missing tagger in tag' '
+ sha=$(git rev-parse HEAD) &&
+ cat >wrong-tag <<EOF &&
+object $sha
+type commit
+tag guten tag
+
+This is an invalid tag.
+EOF
+
+ tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
+ pack1=$(echo $tag $sha | git pack-objects tag-test) &&
+ echo remove tag object &&
+ thirtyeight=${tag#??} &&
+ rm -f .git/objects/${tag%$thirtyeight}/$thirtyeight &&
+ git index-pack --strict tag-test-${pack1}.pack 2>err &&
+ grep "^warning:.* expected .tagger. line" err
+'
+
test_done
diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh
index 5f6cd4f..5940ce2 100755
--- a/t/t5303-pack-corruption-resilience.sh
+++ b/t/t5303-pack-corruption-resilience.sh
@@ -32,26 +32,26 @@ create_test_files() {
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` &&
+ 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=$(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=$(git show-index < ${pack}.idx | grep $1 | cut -f1 -d" ") &&
ofs=$(($ofs + $2)) &&
chmod +w ${pack}.pack &&
- dd of=${pack}.pack count=1 bs=1 conv=notrunc seek=$ofs &&
+ dd of=${pack}.pack bs=1 conv=notrunc seek=$ofs &&
test_must_fail git verify-pack ${pack}.pack
}
@@ -276,6 +276,33 @@ test_expect_success \
git cat-file blob $blob_3 > /dev/null'
test_expect_success \
+ 'corruption of delta base reference pointing to wrong object' \
+ 'create_new_pack --delta-base-offset &&
+ git prune-packed &&
+ printf "\220\033" | do_corrupt_object $blob_3 2 &&
+ git cat-file blob $blob_1 >/dev/null &&
+ git cat-file blob $blob_2 >/dev/null &&
+ test_must_fail git cat-file blob $blob_3 >/dev/null'
+
+test_expect_success \
+ '... but having a loose copy allows for full recovery' \
+ 'mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_3 &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ '... and then a repack "clears" the corruption' \
+ 'do_repack --delta-base-offset --no-reuse-delta &&
+ git prune-packed &&
+ git verify-pack ${pack}.pack &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
'corrupting header to have too small output buffer fails unpack' \
'create_new_pack &&
git prune-packed &&
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index e2ed13d..133b584 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -13,8 +13,9 @@ 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
+ verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test_path_is_file $BLOB_FILE &&
+ test-chmtime =+0 $BLOB_FILE
}
test_expect_success setup '
@@ -34,9 +35,9 @@ test_expect_success 'prune stale packs' '
: > .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_path_is_file $orig_pack &&
+ test_path_is_file .git/objects/tmp_2.pack &&
+ test_path_is_missing .git/objects/tmp_1.pack
'
@@ -44,12 +45,12 @@ 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 &&
+ verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test_path_is_file $BLOB_FILE &&
test-chmtime =-86500 $BLOB_FILE &&
git prune --expire 1.day &&
- test $before = $(git count-objects | sed "s/ .*//") &&
- ! test -f $BLOB_FILE
+ verbose test $before = $(git count-objects | sed "s/ .*//") &&
+ test_path_is_missing $BLOB_FILE
'
@@ -58,12 +59,12 @@ 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 &&
+ verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test_path_is_file $BLOB_FILE &&
test-chmtime =-$((2*$week+1)) $BLOB_FILE &&
git gc &&
- test $before = $(git count-objects | sed "s/ .*//") &&
- ! test -f $BLOB_FILE
+ verbose test $before = $(git count-objects | sed "s/ .*//") &&
+ test_path_is_missing $BLOB_FILE
'
@@ -103,6 +104,28 @@ test_expect_success 'prune: prune unreachable heads' '
'
+test_expect_success 'prune: do not prune detached HEAD with no reflog' '
+
+ git checkout --detach --quiet &&
+ git commit --allow-empty -m "detached commit" &&
+ # verify that there is no reflogs
+ # (should be removed and disabled by previous test)
+ test_path_is_missing .git/logs &&
+ git prune -n >prune_actual &&
+ : >prune_expected &&
+ test_cmp prune_actual prune_expected
+
+'
+
+test_expect_success 'prune: prune former HEAD after checking out branch' '
+
+ head_sha1=$(git rev-parse HEAD) &&
+ git checkout --quiet master &&
+ git prune -v >prune_actual &&
+ grep "$head_sha1" prune_actual
+
+'
+
test_expect_success 'prune: do not prune heads listed as an argument' '
: > file2 &&
@@ -121,8 +144,8 @@ test_expect_success 'gc --no-prune' '
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
+ verbose test 1 = $(git count-objects | sed "s/ .*//") &&
+ test_path_is_file $BLOB_FILE
'
@@ -130,10 +153,10 @@ test_expect_success 'gc respects gc.pruneExpire' '
git config gc.pruneExpire 5002.days.ago &&
git gc &&
- test -f $BLOB_FILE &&
+ test_path_is_file $BLOB_FILE &&
git config gc.pruneExpire 5000.days.ago &&
git gc &&
- test ! -f $BLOB_FILE
+ test_path_is_missing $BLOB_FILE
'
@@ -142,9 +165,9 @@ test_expect_success 'gc --prune=<date>' '
add_blob &&
test-chmtime =-$((5001*$day)) $BLOB_FILE &&
git gc --prune=5002.days.ago &&
- test -f $BLOB_FILE &&
+ test_path_is_file $BLOB_FILE &&
git gc --prune=5000.days.ago &&
- test ! -f $BLOB_FILE
+ test_path_is_missing $BLOB_FILE
'
@@ -152,9 +175,9 @@ test_expect_success 'gc --prune=never' '
add_blob &&
git gc --prune=never &&
- test -f $BLOB_FILE &&
+ test_path_is_file $BLOB_FILE &&
git gc --prune=now &&
- test ! -f $BLOB_FILE
+ test_path_is_missing $BLOB_FILE
'
@@ -163,10 +186,10 @@ test_expect_success 'gc respects gc.pruneExpire=never' '
git config gc.pruneExpire never &&
add_blob &&
git gc &&
- test -f $BLOB_FILE &&
+ test_path_is_file $BLOB_FILE &&
git config gc.pruneExpire now &&
git gc &&
- test ! -f $BLOB_FILE
+ test_path_is_missing $BLOB_FILE
'
@@ -174,9 +197,9 @@ test_expect_success 'prune --expire=never' '
add_blob &&
git prune --expire=never &&
- test -f $BLOB_FILE &&
+ test_path_is_file $BLOB_FILE &&
git prune &&
- test ! -f $BLOB_FILE
+ test_path_is_missing $BLOB_FILE
'
@@ -186,12 +209,78 @@ test_expect_success 'gc: prune old objects after local clone' '
git clone --no-hardlinks . aclone &&
(
cd aclone &&
- test 1 = $(git count-objects | sed "s/ .*//") &&
- test -f $BLOB_FILE &&
+ verbose test 1 = $(git count-objects | sed "s/ .*//") &&
+ test_path_is_file $BLOB_FILE &&
git gc --prune &&
- test 0 = $(git count-objects | sed "s/ .*//") &&
- ! test -f $BLOB_FILE
+ verbose test 0 = $(git count-objects | sed "s/ .*//") &&
+ test_path_is_missing $BLOB_FILE
)
'
+test_expect_success 'garbage report in count-objects -v' '
+ test_when_finished "rm -f .git/objects/pack/fake*" &&
+ test_when_finished "rm -f .git/objects/pack/foo*" &&
+ : >.git/objects/pack/foo &&
+ : >.git/objects/pack/foo.bar &&
+ : >.git/objects/pack/foo.keep &&
+ : >.git/objects/pack/foo.pack &&
+ : >.git/objects/pack/fake.bar &&
+ : >.git/objects/pack/fake.keep &&
+ : >.git/objects/pack/fake.pack &&
+ : >.git/objects/pack/fake.idx &&
+ : >.git/objects/pack/fake2.keep &&
+ : >.git/objects/pack/fake3.idx &&
+ git count-objects -v 2>stderr &&
+ grep "index file .git/objects/pack/fake.idx is too small" stderr &&
+ grep "^warning:" stderr | sort >actual &&
+ cat >expected <<\EOF &&
+warning: garbage found: .git/objects/pack/fake.bar
+warning: garbage found: .git/objects/pack/foo
+warning: garbage found: .git/objects/pack/foo.bar
+warning: no corresponding .idx or .pack: .git/objects/pack/fake2.keep
+warning: no corresponding .idx: .git/objects/pack/foo.keep
+warning: no corresponding .idx: .git/objects/pack/foo.pack
+warning: no corresponding .pack: .git/objects/pack/fake3.idx
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'clean pack garbage with gc' '
+ test_when_finished "rm -f .git/objects/pack/fake*" &&
+ test_when_finished "rm -f .git/objects/pack/foo*" &&
+ : >.git/objects/pack/foo.keep &&
+ : >.git/objects/pack/foo.pack &&
+ : >.git/objects/pack/fake.idx &&
+ : >.git/objects/pack/fake2.keep &&
+ : >.git/objects/pack/fake2.idx &&
+ : >.git/objects/pack/fake3.keep &&
+ git gc &&
+ git count-objects -v 2>stderr &&
+ grep "^warning:" stderr | sort >actual &&
+ cat >expected <<\EOF &&
+warning: no corresponding .idx or .pack: .git/objects/pack/fake3.keep
+warning: no corresponding .idx: .git/objects/pack/foo.keep
+warning: no corresponding .idx: .git/objects/pack/foo.pack
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'prune .git/shallow' '
+ SHA1=$(echo hi|git commit-tree HEAD^{tree}) &&
+ echo $SHA1 >.git/shallow &&
+ git prune --dry-run >out &&
+ grep $SHA1 .git/shallow &&
+ grep $SHA1 out &&
+ git prune &&
+ test_path_is_missing .git/shallow
+'
+
+test_expect_success 'prune: handle alternate object database' '
+ test_create_repo A &&
+ git -C A commit --allow-empty -m "initial commit" &&
+ git clone --shared A B &&
+ git -C B commit --allow-empty -m "next commit" &&
+ git -C B prune
+'
+
test_done
diff --git a/t/t5305-include-tag.sh b/t/t5305-include-tag.sh
index b061864..f314ad5 100755
--- a/t/t5305-include-tag.sh
+++ b/t/t5305-include-tag.sh
@@ -3,20 +3,20 @@
test_description='git pack-object --include-tag'
. ./test-lib.sh
-TRASH=`pwd`
+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` &&
+ 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` &&
+ tag=$(git mktag <sig) &&
rm d sig &&
git update-ref refs/tags/mytag $tag && {
echo $tree &&
@@ -45,9 +45,7 @@ test_expect_success 'unpack objects' '
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 &&
+ test_must_fail env GIT_DIR=clone.git git cat-file -e $tag &&
git rev-list --objects $commit
) >list.actual &&
test_cmp list.expect list.actual
diff --git a/t/t5308-pack-detect-duplicates.sh b/t/t5308-pack-detect-duplicates.sh
new file mode 100755
index 0000000..9c5a876
--- /dev/null
+++ b/t/t5308-pack-detect-duplicates.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+test_description='handling of duplicate objects in incoming packfiles'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-pack.sh
+
+# The sha1s we have in our pack. It's important that these have the same
+# starting byte, so that they end up in the same fanout section of the index.
+# That lets us make sure we are exercising the binary search with both sets.
+LO_SHA1=e68fe8129b546b101aee9510c5328e7f21ca1d18
+HI_SHA1=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+
+# And here's a "missing sha1" which will produce failed lookups. It must also
+# be in the same fanout section, and should be between the two (so that during
+# our binary search, we are sure to end up looking at one or the other of the
+# duplicate runs).
+MISSING_SHA1='e69d000000000000000000000000000000000000'
+
+# git will never intentionally create packfiles with
+# duplicate objects, so we have to construct them by hand.
+#
+# $1 is the name of the packfile to create
+#
+# $2 is the number of times to duplicate each object
+create_pack () {
+ pack_header "$((2 * $2))" >"$1" &&
+ for i in $(test_seq 1 "$2"); do
+ pack_obj $LO_SHA1 &&
+ pack_obj $HI_SHA1
+ done >>"$1" &&
+ pack_trailer "$1"
+}
+
+# double-check that create_pack actually works
+test_expect_success 'pack with no duplicates' '
+ create_pack no-dups.pack 1 &&
+ git index-pack --stdin <no-dups.pack
+'
+
+test_expect_success 'index-pack will allow duplicate objects by default' '
+ clear_packs &&
+ create_pack dups.pack 100 &&
+ git index-pack --stdin <dups.pack
+'
+
+test_expect_success 'create batch-check test vectors' '
+ cat >input <<-EOF &&
+ $LO_SHA1
+ $HI_SHA1
+ $MISSING_SHA1
+ EOF
+ cat >expect <<-EOF
+ $LO_SHA1 blob 2
+ $HI_SHA1 blob 0
+ $MISSING_SHA1 missing
+ EOF
+'
+
+test_expect_success 'lookup in duplicated pack (binary search)' '
+ git cat-file --batch-check <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'lookup in duplicated pack (GIT_USE_LOOKUP)' '
+ (
+ GIT_USE_LOOKUP=1 &&
+ export GIT_USE_LOOKUP &&
+ git cat-file --batch-check <input >actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'index-pack can reject packs with duplicates' '
+ clear_packs &&
+ create_pack dups.pack 2 &&
+ test_must_fail git index-pack --strict --stdin <dups.pack &&
+ test_expect_code 1 git cat-file -e $LO_SHA1
+'
+
+test_done
diff --git a/t/t5309-pack-delta-cycles.sh b/t/t5309-pack-delta-cycles.sh
new file mode 100755
index 0000000..3e7861b
--- /dev/null
+++ b/t/t5309-pack-delta-cycles.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+test_description='test index-pack handling of delta cycles in packfiles'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-pack.sh
+
+# Two similar-ish objects that we have computed deltas between.
+A=01d7713666f4de822776c7622c10f1b07de280dc
+B=e68fe8129b546b101aee9510c5328e7f21ca1d18
+
+# double-check our hand-constucted packs
+test_expect_success 'index-pack works with a single delta (A->B)' '
+ clear_packs &&
+ {
+ pack_header 2 &&
+ pack_obj $A $B &&
+ pack_obj $B
+ } >ab.pack &&
+ pack_trailer ab.pack &&
+ git index-pack --stdin <ab.pack &&
+ git cat-file -t $A &&
+ git cat-file -t $B
+'
+
+test_expect_success 'index-pack works with a single delta (B->A)' '
+ clear_packs &&
+ {
+ pack_header 2 &&
+ pack_obj $A &&
+ pack_obj $B $A
+ } >ba.pack &&
+ pack_trailer ba.pack &&
+ git index-pack --stdin <ba.pack &&
+ git cat-file -t $A &&
+ git cat-file -t $B
+'
+
+test_expect_success 'index-pack detects missing base objects' '
+ clear_packs &&
+ {
+ pack_header 1 &&
+ pack_obj $A $B
+ } >missing.pack &&
+ pack_trailer missing.pack &&
+ test_must_fail git index-pack --fix-thin --stdin <missing.pack
+'
+
+test_expect_success 'index-pack detects REF_DELTA cycles' '
+ clear_packs &&
+ {
+ pack_header 2 &&
+ pack_obj $A $B &&
+ pack_obj $B $A
+ } >cycle.pack &&
+ pack_trailer cycle.pack &&
+ test_must_fail git index-pack --fix-thin --stdin <cycle.pack
+'
+
+test_expect_failure 'failover to an object in another pack' '
+ clear_packs &&
+ git index-pack --stdin <ab.pack &&
+ git index-pack --stdin --fix-thin <cycle.pack
+'
+
+test_expect_failure 'failover to a duplicate object in the same pack' '
+ clear_packs &&
+ {
+ pack_header 3 &&
+ pack_obj $A $B &&
+ pack_obj $B $A &&
+ pack_obj $A
+ } >recoverable.pack &&
+ pack_trailer recoverable.pack &&
+ git index-pack --fix-thin --stdin <recoverable.pack
+'
+
+test_done
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
new file mode 100755
index 0000000..3893afd
--- /dev/null
+++ b/t/t5310-pack-bitmaps.sh
@@ -0,0 +1,194 @@
+#!/bin/sh
+
+test_description='exercise basic bitmap functionality'
+. ./test-lib.sh
+
+objpath () {
+ echo ".git/objects/$(echo "$1" | sed -e 's|\(..\)|\1/|')"
+}
+
+test_expect_success 'setup repo with moderate-sized history' '
+ for i in $(test_seq 1 10); do
+ test_commit $i
+ done &&
+ git checkout -b other HEAD~5 &&
+ for i in $(test_seq 1 10); do
+ test_commit side-$i
+ done &&
+ git checkout master &&
+ blob=$(echo tagged-blob | git hash-object -w --stdin) &&
+ git tag tagged-blob $blob &&
+ git config repack.writebitmaps true &&
+ git config pack.writebitmaphashcache true
+'
+
+test_expect_success 'full repack creates bitmaps' '
+ git repack -ad &&
+ ls .git/objects/pack/ | grep bitmap >output &&
+ test_line_count = 1 output
+'
+
+test_expect_success 'rev-list --test-bitmap verifies bitmaps' '
+ git rev-list --test-bitmap HEAD
+'
+
+rev_list_tests() {
+ state=$1
+
+ test_expect_success "counting commits via bitmap ($state)" '
+ git rev-list --count HEAD >expect &&
+ git rev-list --use-bitmap-index --count HEAD >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "counting partial commits via bitmap ($state)" '
+ git rev-list --count HEAD~5..HEAD >expect &&
+ git rev-list --use-bitmap-index --count HEAD~5..HEAD >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "counting commits with limit ($state)" '
+ git rev-list --count -n 1 HEAD >expect &&
+ git rev-list --use-bitmap-index --count -n 1 HEAD >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "counting non-linear history ($state)" '
+ git rev-list --count other...master >expect &&
+ git rev-list --use-bitmap-index --count other...master >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "counting commits with limiting ($state)" '
+ git rev-list --count HEAD -- 1.t >expect &&
+ git rev-list --use-bitmap-index --count HEAD -- 1.t >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "enumerate --objects ($state)" '
+ git rev-list --objects --use-bitmap-index HEAD >tmp &&
+ cut -d" " -f1 <tmp >tmp2 &&
+ sort <tmp2 >actual &&
+ git rev-list --objects HEAD >tmp &&
+ cut -d" " -f1 <tmp >tmp2 &&
+ sort <tmp2 >expect &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "bitmap --objects handles non-commit objects ($state)" '
+ git rev-list --objects --use-bitmap-index HEAD tagged-blob >actual &&
+ grep $blob actual
+ '
+}
+
+rev_list_tests 'full bitmap'
+
+test_expect_success 'clone from bitmapped repository' '
+ git clone --no-local --bare . clone.git &&
+ git rev-parse HEAD >expect &&
+ git --git-dir=clone.git rev-parse HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup further non-bitmapped commits' '
+ for i in $(test_seq 1 10); do
+ test_commit further-$i
+ done
+'
+
+rev_list_tests 'partial bitmap'
+
+test_expect_success 'fetch (partial bitmap)' '
+ git --git-dir=clone.git fetch origin master:master &&
+ git rev-parse HEAD >expect &&
+ git --git-dir=clone.git rev-parse HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'incremental repack cannot create bitmaps' '
+ test_commit more-1 &&
+ find .git/objects/pack -name "*.bitmap" >expect &&
+ git repack -d &&
+ find .git/objects/pack -name "*.bitmap" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'incremental repack can disable bitmaps' '
+ test_commit more-2 &&
+ git repack -d --no-write-bitmap-index
+'
+
+test_expect_success 'full repack, reusing previous bitmaps' '
+ git repack -ad &&
+ ls .git/objects/pack/ | grep bitmap >output &&
+ test_line_count = 1 output
+'
+
+test_expect_success 'fetch (full bitmap)' '
+ git --git-dir=clone.git fetch origin master:master &&
+ git rev-parse HEAD >expect &&
+ git --git-dir=clone.git rev-parse HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create objects for missing-HAVE tests' '
+ blob=$(echo "missing have" | git hash-object -w --stdin) &&
+ tree=$(printf "100644 blob $blob\tfile\n" | git mktree) &&
+ parent=$(echo parent | git commit-tree $tree) &&
+ commit=$(echo commit | git commit-tree $tree -p $parent) &&
+ cat >revs <<-EOF
+ HEAD
+ ^HEAD^
+ ^$commit
+ EOF
+'
+
+test_expect_success 'pack with missing blob' '
+ rm $(objpath $blob) &&
+ git pack-objects --stdout --revs <revs >/dev/null
+'
+
+test_expect_success 'pack with missing tree' '
+ rm $(objpath $tree) &&
+ git pack-objects --stdout --revs <revs >/dev/null
+'
+
+test_expect_success 'pack with missing parent' '
+ rm $(objpath $parent) &&
+ git pack-objects --stdout --revs <revs >/dev/null
+'
+
+test_lazy_prereq JGIT '
+ type jgit
+'
+
+test_expect_success JGIT 'we can read jgit bitmaps' '
+ git clone . compat-jgit &&
+ (
+ cd compat-jgit &&
+ rm -f .git/objects/pack/*.bitmap &&
+ jgit gc &&
+ git rev-list --test-bitmap HEAD
+ )
+'
+
+test_expect_success JGIT 'jgit can read our bitmaps' '
+ git clone . compat-us &&
+ (
+ cd compat-us &&
+ git repack -adb &&
+ # jgit gc will barf if it does not like our bitmaps
+ jgit gc
+ )
+'
+
+test_expect_success 'splitting packs does not generate bogus bitmaps' '
+ test-genrandom foo $((1024 * 1024)) >rand &&
+ git add rand &&
+ git commit -m "commit with big file" &&
+ git -c pack.packSizeLimit=500k repack -adb &&
+ git init --bare no-bitmaps.git &&
+ git -C no-bitmaps.git fetch .. HEAD
+'
+
+test_done
diff --git a/t/t5311-pack-bitmaps-shallow.sh b/t/t5311-pack-bitmaps-shallow.sh
new file mode 100755
index 0000000..872a95d
--- /dev/null
+++ b/t/t5311-pack-bitmaps-shallow.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+test_description='check bitmap operation with shallow repositories'
+. ./test-lib.sh
+
+# We want to create a situation where the shallow, grafted
+# view of reachability does not match reality in a way that
+# might cause us to send insufficient objects.
+#
+# We do this with a history that repeats a state, like:
+#
+# A -- B -- C
+# file=1 file=2 file=1
+#
+# and then create a shallow clone to the second commit, B.
+# In a non-shallow clone, that would mean we already have
+# the tree for A. But in a shallow one, we've grafted away
+# A, and fetching A to B requires that the other side send
+# us the tree for file=1.
+test_expect_success 'setup shallow repo' '
+ echo 1 >file &&
+ git add file &&
+ git commit -m orig &&
+ echo 2 >file &&
+ git commit -a -m update &&
+ git clone --no-local --bare --depth=1 . shallow.git &&
+ echo 1 >file &&
+ git commit -a -m repeat
+'
+
+test_expect_success 'turn on bitmaps in the parent' '
+ git repack -adb
+'
+
+test_expect_success 'shallow fetch from bitmapped repo' '
+ (cd shallow.git && git fetch)
+'
+
+test_done
diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh
new file mode 100755
index 0000000..da9d599
--- /dev/null
+++ b/t/t5312-prune-corruption.sh
@@ -0,0 +1,114 @@
+#!/bin/sh
+
+test_description='
+Test pruning of repositories with minor corruptions. The goal
+here is that we should always be erring on the side of safety. So
+if we see, for example, a ref with a bogus name, it is OK either to
+bail out or to proceed using it as a reachable tip, but it is _not_
+OK to proceed as if it did not exist. Otherwise we might silently
+delete objects that cannot be recovered.
+'
+. ./test-lib.sh
+
+test_expect_success 'disable reflogs' '
+ git config core.logallrefupdates false &&
+ git reflog expire --expire=all --all
+'
+
+test_expect_success 'create history reachable only from a bogus-named ref' '
+ test_tick && git commit --allow-empty -m master &&
+ base=$(git rev-parse HEAD) &&
+ test_tick && git commit --allow-empty -m bogus &&
+ bogus=$(git rev-parse HEAD) &&
+ git cat-file commit $bogus >saved &&
+ echo $bogus >.git/refs/heads/bogus..name &&
+ git reset --hard HEAD^
+'
+
+test_expect_success 'pruning does not drop bogus object' '
+ test_when_finished "git hash-object -w -t commit saved" &&
+ test_might_fail git prune --expire=now &&
+ verbose git cat-file -e $bogus
+'
+
+test_expect_success 'put bogus object into pack' '
+ git tag reachable $bogus &&
+ git repack -ad &&
+ git tag -d reachable &&
+ verbose git cat-file -e $bogus
+'
+
+test_expect_success 'destructive repack keeps packed object' '
+ test_might_fail git repack -Ad --unpack-unreachable=now &&
+ verbose git cat-file -e $bogus &&
+ test_might_fail git repack -ad &&
+ verbose git cat-file -e $bogus
+'
+
+# subsequent tests will have different corruptions
+test_expect_success 'clean up bogus ref' '
+ rm .git/refs/heads/bogus..name
+'
+
+# We create two new objects here, "one" and "two". Our
+# master branch points to "two", which is deleted,
+# corrupting the repository. But we'd like to make sure
+# that the otherwise unreachable "one" is not pruned
+# (since it is the user's best bet for recovering
+# from the corruption).
+#
+# Note that we also point HEAD somewhere besides "two",
+# as we want to make sure we test the case where we
+# pick up the reference to "two" by iterating the refs,
+# not by resolving HEAD.
+test_expect_success 'create history with missing tip commit' '
+ test_tick && git commit --allow-empty -m one &&
+ recoverable=$(git rev-parse HEAD) &&
+ git cat-file commit $recoverable >saved &&
+ test_tick && git commit --allow-empty -m two &&
+ missing=$(git rev-parse HEAD) &&
+ git checkout --detach $base &&
+ rm .git/objects/$(echo $missing | sed "s,..,&/,") &&
+ test_must_fail git cat-file -e $missing
+'
+
+test_expect_success 'pruning with a corrupted tip does not drop history' '
+ test_when_finished "git hash-object -w -t commit saved" &&
+ test_might_fail git prune --expire=now &&
+ verbose git cat-file -e $recoverable
+'
+
+test_expect_success 'pack-refs does not silently delete broken loose ref' '
+ git pack-refs --all --prune &&
+ echo $missing >expect &&
+ git rev-parse refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
+# we do not want to count on running pack-refs to
+# actually pack it, as it is perfectly reasonable to
+# skip processing a broken ref
+test_expect_success 'create packed-refs file with broken ref' '
+ rm -f .git/refs/heads/master &&
+ cat >.git/packed-refs <<-EOF &&
+ $missing refs/heads/master
+ $recoverable refs/heads/other
+ EOF
+ echo $missing >expect &&
+ git rev-parse refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pack-refs does not silently delete broken packed ref' '
+ git pack-refs --all --prune &&
+ git rev-parse refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pack-refs does not drop broken refs during deletion' '
+ git update-ref -d refs/heads/other &&
+ git rev-parse refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh
new file mode 100755
index 0000000..a8a587a
--- /dev/null
+++ b/t/t5313-pack-bounds-checks.sh
@@ -0,0 +1,179 @@
+#!/bin/sh
+
+test_description='bounds-checking of access to mmapped on-disk file formats'
+. ./test-lib.sh
+
+clear_base () {
+ test_when_finished 'restore_base' &&
+ rm -f $base
+}
+
+restore_base () {
+ cp base-backup/* .git/objects/pack/
+}
+
+do_pack () {
+ pack_objects=$1; shift
+ sha1=$(
+ for i in $pack_objects
+ do
+ echo $i
+ done | git pack-objects "$@" .git/objects/pack/pack
+ ) &&
+ pack=.git/objects/pack/pack-$sha1.pack &&
+ idx=.git/objects/pack/pack-$sha1.idx &&
+ chmod +w $pack $idx &&
+ test_when_finished 'rm -f "$pack" "$idx"'
+}
+
+munge () {
+ printf "$3" | dd of="$1" bs=1 conv=notrunc seek=$2
+}
+
+# Offset in a v2 .idx to its initial and extended offset tables. For an index
+# with "nr" objects, this is:
+#
+# magic(4) + version(4) + fan-out(4*256) + sha1s(20*nr) + crc(4*nr),
+#
+# for the initial, and another ofs(4*nr) past that for the extended.
+#
+ofs_table () {
+ echo $((4 + 4 + 4*256 + 20*$1 + 4*$1))
+}
+extended_table () {
+ echo $(($(ofs_table "$1") + 4*$1))
+}
+
+test_expect_success 'set up base packfile and variables' '
+ # the hash of this content starts with ff, which
+ # makes some later computations much simpler
+ echo 74 >file &&
+ git add file &&
+ git commit -m base &&
+ git repack -ad &&
+ base=$(echo .git/objects/pack/*) &&
+ chmod +w $base &&
+ mkdir base-backup &&
+ cp $base base-backup/ &&
+ object=$(git rev-parse HEAD:file)
+'
+
+test_expect_success 'pack/index object count mismatch' '
+ do_pack $object &&
+ munge $pack 8 "\377\0\0\0" &&
+ clear_base &&
+
+ # We enumerate the objects from the completely-fine
+ # .idx, but notice later that the .pack is bogus
+ # and fail to show any data.
+ echo "$object missing" >expect &&
+ git cat-file --batch-all-objects --batch-check >actual &&
+ test_cmp expect actual &&
+
+ # ...and here fail to load the object (without segfaulting),
+ # but fallback to a good copy if available.
+ test_must_fail git cat-file blob $object &&
+ restore_base &&
+ git cat-file blob $object >actual &&
+ test_cmp file actual &&
+
+ # ...and make sure that index-pack --verify, which has its
+ # own reading routines, does not segfault.
+ test_must_fail git index-pack --verify $pack
+'
+
+test_expect_success 'matched bogus object count' '
+ do_pack $object &&
+ munge $pack 8 "\377\0\0\0" &&
+ munge $idx $((255 * 4)) "\377\0\0\0" &&
+ clear_base &&
+
+ # Unlike above, we should notice early that the .idx is totally
+ # bogus, and not even enumerate its contents.
+ >expect &&
+ git cat-file --batch-all-objects --batch-check >actual &&
+ test_cmp expect actual &&
+
+ # But as before, we can do the same object-access checks.
+ test_must_fail git cat-file blob $object &&
+ restore_base &&
+ git cat-file blob $object >actual &&
+ test_cmp file actual &&
+
+ test_must_fail git index-pack --verify $pack
+'
+
+# Note that we cannot check the fallback case for these
+# further .idx tests, as we notice the problem in functions
+# whose interface doesn't allow an error return (like use_pack()),
+# and thus we just die().
+#
+# There's also no point in doing enumeration tests, as
+# we are munging offsets here, which are about looking up
+# specific objects.
+
+test_expect_success 'bogus object offset (v1)' '
+ do_pack $object --index-version=1 &&
+ munge $idx $((4 * 256)) "\377\0\0\0" &&
+ clear_base &&
+ test_must_fail git cat-file blob $object &&
+ test_must_fail git index-pack --verify $pack
+'
+
+test_expect_success 'bogus object offset (v2, no msb)' '
+ do_pack $object --index-version=2 &&
+ munge $idx $(ofs_table 1) "\0\377\0\0" &&
+ clear_base &&
+ test_must_fail git cat-file blob $object &&
+ test_must_fail git index-pack --verify $pack
+'
+
+test_expect_success 'bogus offset into v2 extended table' '
+ do_pack $object --index-version=2 &&
+ munge $idx $(ofs_table 1) "\377\0\0\0" &&
+ clear_base &&
+ test_must_fail git cat-file blob $object &&
+ test_must_fail git index-pack --verify $pack
+'
+
+test_expect_success 'bogus offset inside v2 extended table' '
+ # We need two objects here, so we can plausibly require
+ # an extended table (if the first object were larger than 2^31).
+ do_pack "$object $(git rev-parse HEAD)" --index-version=2 &&
+
+ # We have to make extra room for the table, so we cannot
+ # just munge in place as usual.
+ {
+ dd if=$idx bs=1 count=$(($(ofs_table 2) + 4)) &&
+ printf "\200\0\0\0" &&
+ printf "\377\0\0\0\0\0\0\0" &&
+ dd if=$idx bs=1 skip=$(extended_table 2)
+ } >tmp &&
+ mv tmp "$idx" &&
+ clear_base &&
+ test_must_fail git cat-file blob $object &&
+ test_must_fail git index-pack --verify $pack
+'
+
+test_expect_success 'bogus OFS_DELTA in packfile' '
+ # Generate a pack with a delta in it.
+ base=$(test-genrandom foo 3000 | git hash-object --stdin -w) &&
+ delta=$(test-genrandom foo 2000 | git hash-object --stdin -w) &&
+ do_pack "$base $delta" --delta-base-offset &&
+ rm -f .git/objects/??/* &&
+
+ # Double check that we have the delta we expect.
+ echo $base >expect &&
+ echo $delta | git cat-file --batch-check="%(deltabase)" >actual &&
+ test_cmp expect actual &&
+
+ # Now corrupt it. We assume the varint size for the delta is small
+ # enough to fit in the first byte (which it should be, since it
+ # is a pure deletion from the base), and that original ofs_delta
+ # takes 2 bytes (which it should, as it should be ~3000).
+ ofs=$(git show-index <$idx | grep $delta | cut -d" " -f1) &&
+ munge $pack $(($ofs + 1)) "\177\377" &&
+ test_must_fail git cat-file blob $delta >/dev/null
+'
+
+test_done
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index c718253..305ca7a 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -76,8 +76,7 @@ test_expect_success 'refuse pushing rewound head without --force' '
test "$victim_head" = "$pushed_head"
'
-test_expect_success \
- 'push can be used to delete a ref' '
+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 &&
@@ -94,6 +93,29 @@ test_expect_success 'refuse deleting push with denyDeletes' '
test_must_fail git send-pack ./victim :extra master
'
+test_expect_success 'cannot override denyDeletes with git -c send-pack' '
+ (
+ cd victim &&
+ test_might_fail git branch -D extra &&
+ git config receive.denyDeletes true &&
+ git branch extra master
+ ) &&
+ test_must_fail git -c receive.denyDeletes=false \
+ send-pack ./victim :extra master
+'
+
+test_expect_success 'override denyDeletes with git -c receive-pack' '
+ (
+ cd victim &&
+ test_might_fail git branch -D extra &&
+ git config receive.denyDeletes true &&
+ git branch extra master
+ ) &&
+ git send-pack \
+ --receive-pack="git -c receive.denyDeletes=false receive-pack" \
+ ./victim :extra master
+'
+
test_expect_success 'denyNonFastforwards trumps --force' '
(
cd victim &&
@@ -106,7 +128,19 @@ test_expect_success 'denyNonFastforwards trumps --force' '
test "$victim_orig" = "$victim_head"
'
-test_expect_success 'push --all excludes remote tracking hierarchy' '
+test_expect_success 'send-pack --all sends all branches' '
+ # make sure we have at least 2 branches with different
+ # values, just to be thorough
+ git branch other-branch HEAD^ &&
+
+ git init --bare all.git &&
+ git send-pack --all all.git &&
+ git for-each-ref refs/heads >expect &&
+ git -C all.git for-each-ref refs/heads >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push --all excludes remote-tracking hierarchy' '
mkdir parent &&
(
cd parent &&
@@ -122,6 +156,41 @@ test_expect_success 'push --all excludes remote tracking hierarchy' '
)
'
+test_expect_success 'receive-pack runs auto-gc in remote repo' '
+ rm -rf parent child &&
+ git init parent &&
+ (
+ # Setup a repo with 2 packs
+ cd parent &&
+ echo "Some text" >file.txt &&
+ git add . &&
+ git commit -m "Initial commit" &&
+ git repack -adl &&
+ echo "Some more text" >>file.txt &&
+ git commit -a -m "Second commit" &&
+ git repack
+ ) &&
+ cp -R parent child &&
+ (
+ # Set the child to auto-pack if more than one pack exists
+ cd child &&
+ git config gc.autopacklimit 1 &&
+ git config gc.autodetach false &&
+ git branch test_auto_gc &&
+ # And create a file that follows the temporary object naming
+ # convention for the auto-gc to remove
+ : >.git/objects/tmp_test_object &&
+ test-chmtime =-1209601 .git/objects/tmp_test_object
+ ) &&
+ (
+ cd parent &&
+ echo "Even more text" >>file.txt &&
+ git commit -a -m "Third commit" &&
+ git send-pack ../child HEAD:refs/heads/test_auto_gc
+ ) &&
+ test ! -e child/.git/objects/tmp_test_object
+'
+
rewound_push_setup() {
rm -rf parent child &&
mkdir parent &&
@@ -138,19 +207,6 @@ rewound_push_setup() {
)
}
-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) &&
@@ -167,7 +223,7 @@ test_expect_success 'pushing explicit refspecs respects forcing' '
+refs/heads/master:refs/heads/master
) &&
parent_head=$(cd parent && git rev-parse --verify master) &&
- child_head=$(cd parent && git rev-parse --verify master) &&
+ child_head=$(cd child && git rev-parse --verify master) &&
test "$parent_head" = "$child_head"
'
@@ -187,7 +243,7 @@ test_expect_success 'pushing wildcard refspecs respects forcing' '
"+refs/heads/*:refs/heads/*"
) &&
parent_head=$(cd parent && git rev-parse --verify master) &&
- child_head=$(cd parent && git rev-parse --verify master) &&
+ child_head=$(cd child && git rev-parse --verify master) &&
test "$parent_head" = "$child_head"
'
diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
index 17bcb0b..7f278d8c 100755
--- a/t/t5401-update-hooks.sh
+++ b/t/t5401-update-hooks.sh
@@ -135,4 +135,17 @@ test_expect_success 'send-pack stderr contains hook messages' '
test_cmp expect actual
'
+test_expect_success 'pre-receive hook that forgets to read its input' '
+ write_script victim.git/hooks/pre-receive <<-\EOF &&
+ exit 0
+ EOF
+ rm -f victim.git/hooks/update victim.git/hooks/post-update &&
+
+ for v in $(test_seq 100 999)
+ do
+ git branch branch_$v master || return
+ done &&
+ git push ./victim.git "+refs/heads/*:refs/heads/*"
+'
+
test_done
diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh
index d05a913..fc898c9 100755
--- a/t/t5403-post-checkout-hook.sh
+++ b/t/t5403-post-checkout-hook.sh
@@ -31,44 +31,44 @@ EOF
done
test_expect_success 'post-checkout runs as expected ' '
- GIT_DIR=clone1/.git git checkout master &&
- test -e clone1/.git/post-checkout.args
+ 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
+ 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 && test $flag = 1
'
test_expect_success 'post-checkout runs as expected ' '
- GIT_DIR=clone1/.git git checkout master &&
- test -e clone1/.git/post-checkout.args
+ 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
+ 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 && test $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
+ 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 && test $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
+ 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 && test $flag = 0
'
if test "$(git config --bool core.filemode)" = true; then
diff --git a/t/t5404-tracking-branches.sh b/t/t5404-tracking-branches.sh
index c240035..2b8c0ba 100755
--- a/t/t5404-tracking-branches.sh
+++ b/t/t5404-tracking-branches.sh
@@ -36,7 +36,7 @@ test_expect_success 'prepare pushable branches' '
'
test_expect_success 'mixed-success push returns error' '
- test_must_fail git push
+ test_must_fail git push origin :
'
test_expect_success 'check tracking branches updated correctly after push' '
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
index 552da65..7a48236 100755
--- a/t/t5407-post-rewrite-hook.sh
+++ b/t/t5407-post-rewrite-hook.sh
@@ -10,7 +10,11 @@ test_expect_success 'setup' '
test_commit A foo A &&
test_commit B foo B &&
test_commit C foo C &&
- test_commit D foo D
+ test_commit D foo D &&
+ git checkout A^0 &&
+ test_commit E bar E &&
+ test_commit F foo F &&
+ git checkout master
'
mkdir .git/hooks
@@ -27,8 +31,8 @@ clear_hook_input () {
}
verify_hook_input () {
- test_cmp "$TRASH_DIRECTORY"/post-rewrite.args expected.args &&
- test_cmp "$TRASH_DIRECTORY"/post-rewrite.data expected.data
+ test_cmp expected.args "$TRASH_DIRECTORY"/post-rewrite.args &&
+ test_cmp expected.data "$TRASH_DIRECTORY"/post-rewrite.data
}
test_expect_success 'git commit --amend' '
@@ -57,10 +61,10 @@ test_expect_success 'git rebase' '
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
+ cat >expected.data <<-EOF &&
+ $(git rev-parse C) $(git rev-parse HEAD^)
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
@@ -73,9 +77,21 @@ test_expect_success 'git rebase --skip' '
git add foo &&
git rebase --continue &&
echo rebase >expected.args &&
- cat >expected.data <<EOF &&
-$(git rev-parse D) $(git rev-parse HEAD)
-EOF
+ cat >expected.data <<-EOF &&
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
+ verify_hook_input
+'
+
+test_expect_success 'git rebase --skip the last one' '
+ git reset --hard F &&
+ clear_hook_input &&
+ test_must_fail git rebase --onto D A &&
+ git rebase --skip &&
+ echo rebase >expected.args &&
+ cat >expected.data <<-EOF &&
+ $(git rev-parse E) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
@@ -87,10 +103,10 @@ test_expect_success 'git rebase -m' '
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
+ cat >expected.data <<-EOF &&
+ $(git rev-parse C) $(git rev-parse HEAD^)
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
@@ -103,9 +119,9 @@ test_expect_success 'git rebase -m --skip' '
git add foo &&
git rebase --continue &&
echo rebase >expected.args &&
- cat >expected.data <<EOF &&
-$(git rev-parse D) $(git rev-parse HEAD)
-EOF
+ cat >expected.data <<-EOF &&
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
@@ -132,10 +148,10 @@ test_expect_success 'git rebase -i (unchanged)' '
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
+ cat >expected.data <<-EOF &&
+ $(git rev-parse C) $(git rev-parse HEAD^)
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
@@ -147,9 +163,9 @@ test_expect_success 'git rebase -i (skip)' '
git add foo &&
git rebase --continue &&
echo rebase >expected.args &&
- cat >expected.data <<EOF &&
-$(git rev-parse D) $(git rev-parse HEAD)
-EOF
+ cat >expected.data <<-EOF &&
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
@@ -161,10 +177,10 @@ test_expect_success 'git rebase -i (squash)' '
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
+ cat >expected.data <<-EOF &&
+ $(git rev-parse C) $(git rev-parse HEAD)
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
@@ -173,10 +189,10 @@ test_expect_success 'git rebase -i (fixup without conflict)' '
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
+ cat >expected.data <<-EOF &&
+ $(git rev-parse C) $(git rev-parse HEAD)
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
@@ -189,10 +205,27 @@ test_expect_success 'git rebase -i (double edit)' '
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
+ 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 (exec)' '
+ git reset --hard D &&
+ clear_hook_input &&
+ FAKE_LINES="edit 1 exec_false 2" git rebase -i B &&
+ echo something >bar &&
+ git add bar &&
+ # Fails because of exec false
+ test_must_fail git rebase --continue &&
+ 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
'
diff --git a/t/t5408-send-pack-stdin.sh b/t/t5408-send-pack-stdin.sh
new file mode 100755
index 0000000..e8737df
--- /dev/null
+++ b/t/t5408-send-pack-stdin.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='send-pack --stdin tests'
+. ./test-lib.sh
+
+create_ref () {
+ tree=$(git write-tree) &&
+ test_tick &&
+ commit=$(echo "$1" | git commit-tree $tree) &&
+ git update-ref "$1" $commit
+}
+
+clear_remote () {
+ rm -rf remote.git &&
+ git init --bare remote.git
+}
+
+verify_push () {
+ git rev-parse "$1" >expect &&
+ git --git-dir=remote.git rev-parse "${2:-$1}" >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'setup refs' '
+ cat >refs <<-\EOF &&
+ refs/heads/A
+ refs/heads/C
+ refs/tags/D
+ refs/heads/B
+ refs/tags/E
+ EOF
+ for i in $(cat refs); do
+ create_ref $i || return 1
+ done
+'
+
+# sanity check our setup
+test_expect_success 'refs on cmdline' '
+ clear_remote &&
+ git send-pack remote.git $(cat refs) &&
+ for i in $(cat refs); do
+ verify_push $i || return 1
+ done
+'
+
+test_expect_success 'refs over stdin' '
+ clear_remote &&
+ git send-pack remote.git --stdin <refs &&
+ for i in $(cat refs); do
+ verify_push $i || return 1
+ done
+'
+
+test_expect_success 'stdin lines are full refspecs' '
+ clear_remote &&
+ echo "A:other" >input &&
+ git send-pack remote.git --stdin <input &&
+ verify_push refs/heads/A refs/heads/other
+'
+
+test_expect_success 'stdin mixed with cmdline' '
+ clear_remote &&
+ echo A >input &&
+ git send-pack remote.git --stdin B <input &&
+ verify_push A &&
+ verify_push B
+'
+
+test_expect_success 'cmdline refs written in order' '
+ clear_remote &&
+ test_must_fail git send-pack remote.git A:foo B:foo &&
+ verify_push A foo
+'
+
+test_expect_success '--stdin refs come after cmdline' '
+ clear_remote &&
+ echo A:foo >input &&
+ test_must_fail git send-pack remote.git --stdin B:foo <input &&
+ verify_push B foo
+'
+
+test_expect_success 'refspecs and --mirror do not mix (cmdline)' '
+ clear_remote &&
+ test_must_fail git send-pack remote.git --mirror $(cat refs)
+'
+
+test_expect_success 'refspecs and --mirror do not mix (stdin)' '
+ clear_remote &&
+ test_must_fail git send-pack remote.git --mirror --stdin <refs
+'
+
+test_done
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 18376d6..82d913a 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -14,7 +14,7 @@ test_description='Testing multi_ack pack fetching'
add () {
name=$1 &&
text="$@" &&
- branch=`echo $name | sed -e 's/^\(.\).*$/\1/'` &&
+ branch=$(echo $name | sed -e 's/^\(.\).*$/\1/') &&
parents="" &&
shift &&
@@ -50,18 +50,18 @@ pull_to_client () {
case "$heads" in *B*)
echo $BTIP > .git/refs/heads/B;;
esac &&
- git symbolic-ref HEAD refs/heads/`echo $heads \
- | sed -e "s/^\(.\).*$/\1/"` &&
+ 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` &&
+ p=$(ls -1 pack-*.pack) &&
git unpack-objects <$p &&
git fsck --full &&
- idx=`echo pack-*.idx` &&
- pack_count=`git show-index <$idx | wc -l` &&
+ idx=$(echo pack-*.idx) &&
+ pack_count=$(git show-index <$idx | wc -l) &&
test $pack_count = $count &&
rm -f pack-*
)
@@ -91,13 +91,13 @@ test_expect_success 'setup' '
prev=$cur &&
cur=$(($cur+1))
done &&
- add B1 $A1
+ 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))
+pull_to_client 1st "refs/heads/B refs/heads/A" $((11*3))
test_expect_success 'post 1st pull setup' '
add A11 $A10 &&
@@ -110,12 +110,44 @@ test_expect_success 'post 1st pull setup' '
done
'
-pull_to_client 2nd "B" $((64*3))
+pull_to_client 2nd "refs/heads/B" $((64*3))
-pull_to_client 3rd "A" $((1*3))
+pull_to_client 3rd "refs/heads/A" $((1*3))
+
+test_expect_success 'single branch clone' '
+ git clone --single-branch "file://$(pwd)/." singlebranch
+'
+
+test_expect_success 'single branch object count' '
+ GIT_DIR=singlebranch/.git git count-objects -v |
+ grep "^in-pack:" > count.singlebranch &&
+ echo "in-pack: 198" >expected &&
+ test_cmp expected count.singlebranch
+'
+
+test_expect_success 'single given branch clone' '
+ git clone --single-branch --branch A "file://$(pwd)/." branch-a &&
+ test_must_fail git --git-dir=branch-a/.git rev-parse origin/B
+'
+
+test_expect_success 'clone shallow depth 1' '
+ git clone --no-single-branch --depth 1 "file://$(pwd)/." shallow0 &&
+ test "$(git --git-dir=shallow0/.git rev-list --count HEAD)" = 1
+'
+
+test_expect_success 'clone shallow depth 1 with fsck' '
+ git config --global fetch.fsckobjects true &&
+ git clone --no-single-branch --depth 1 "file://$(pwd)/." shallow0fsck &&
+ test "$(git --git-dir=shallow0fsck/.git rev-list --count HEAD)" = 1 &&
+ git config --global --unset fetch.fsckobjects
+'
test_expect_success 'clone shallow' '
- git clone --depth 2 "file://$(pwd)/." shallow
+ git clone --no-single-branch --depth 2 "file://$(pwd)/." shallow
+'
+
+test_expect_success 'clone shallow depth count' '
+ test "$(git --git-dir=shallow/.git rev-list --count HEAD)" = 2
'
test_expect_success 'clone shallow object count' '
@@ -123,7 +155,7 @@ test_expect_success 'clone shallow object count' '
cd shallow &&
git count-objects -v
) > count.shallow &&
- grep "^in-pack: 18" count.shallow
+ grep "^in-pack: 12" count.shallow
'
test_expect_success 'clone shallow object count (part 2)' '
@@ -227,7 +259,8 @@ test_expect_success 'clone shallow object count' '
test_expect_success 'pull in shallow repo with missing merge base' '
(
cd shallow &&
- test_must_fail git pull --depth 4 .. A
+ git fetch --depth 4 .. A
+ test_must_fail git merge --allow-unrelated-histories FETCH_HEAD
)
'
@@ -240,12 +273,383 @@ test_expect_success 'additional simple shallow deepenings' '
)
'
+test_expect_success 'clone shallow depth count' '
+ test "$(git --git-dir=shallow/.git rev-list --count HEAD)" = 11
+'
+
test_expect_success 'clone shallow object count' '
(
cd shallow &&
+ git prune &&
git count-objects -v
) > count.shallow &&
- grep "^count: 52" count.shallow
+ grep "^count: 54" count.shallow
+'
+
+test_expect_success 'fetch --no-shallow on full repo' '
+ test_must_fail git fetch --noshallow
+'
+
+test_expect_success 'fetch --depth --no-shallow' '
+ (
+ cd shallow &&
+ test_must_fail git fetch --depth=1 --noshallow
+ )
+'
+
+test_expect_success 'turn shallow to complete repository' '
+ (
+ cd shallow &&
+ git fetch --unshallow &&
+ ! test -f .git/shallow &&
+ git fsck --full
+ )
+'
+
+test_expect_success 'clone shallow without --no-single-branch' '
+ git clone --depth 1 "file://$(pwd)/." shallow2
+'
+
+test_expect_success 'clone shallow object count' '
+ (
+ cd shallow2 &&
+ git count-objects -v
+ ) > count.shallow2 &&
+ grep "^in-pack: 3" count.shallow2
+'
+
+test_expect_success 'clone shallow with --branch' '
+ git clone --depth 1 --branch A "file://$(pwd)/." shallow3
+'
+
+test_expect_success 'clone shallow object count' '
+ echo "in-pack: 3" > count3.expected &&
+ GIT_DIR=shallow3/.git git count-objects -v |
+ grep "^in-pack" > count3.actual &&
+ test_cmp count3.expected count3.actual
+'
+
+test_expect_success 'clone shallow with detached HEAD' '
+ git checkout HEAD^ &&
+ git clone --depth 1 "file://$(pwd)/." shallow5 &&
+ git checkout - &&
+ GIT_DIR=shallow5/.git git rev-parse HEAD >actual &&
+ git rev-parse HEAD^ >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'shallow clone pulling tags' '
+ git tag -a -m A TAGA1 A &&
+ git tag -a -m B TAGB1 B &&
+ git tag TAGA2 A &&
+ git tag TAGB2 B &&
+ git clone --depth 1 "file://$(pwd)/." shallow6 &&
+
+ cat >taglist.expected <<\EOF &&
+TAGB1
+TAGB2
+EOF
+ GIT_DIR=shallow6/.git git tag -l >taglist.actual &&
+ test_cmp taglist.expected taglist.actual &&
+
+ echo "in-pack: 4" > count6.expected &&
+ GIT_DIR=shallow6/.git git count-objects -v |
+ grep "^in-pack" > count6.actual &&
+ test_cmp count6.expected count6.actual
+'
+
+test_expect_success 'shallow cloning single tag' '
+ git clone --depth 1 --branch=TAGB1 "file://$(pwd)/." shallow7 &&
+ cat >taglist.expected <<\EOF &&
+TAGB1
+TAGB2
+EOF
+ GIT_DIR=shallow7/.git git tag -l >taglist.actual &&
+ test_cmp taglist.expected taglist.actual &&
+
+ echo "in-pack: 4" > count7.expected &&
+ GIT_DIR=shallow7/.git git count-objects -v |
+ grep "^in-pack" > count7.actual &&
+ test_cmp count7.expected count7.actual
+'
+
+test_expect_success 'clone shallow with packed refs' '
+ git pack-refs --all &&
+ git clone --depth 1 --branch A "file://$(pwd)/." shallow8 &&
+ echo "in-pack: 4" > count8.expected &&
+ GIT_DIR=shallow8/.git git count-objects -v |
+ grep "^in-pack" > count8.actual &&
+ test_cmp count8.expected count8.actual
+'
+
+test_expect_success 'fetch in shallow repo unreachable shallow objects' '
+ (
+ git clone --bare --branch B --single-branch "file://$(pwd)/." no-reflog &&
+ git clone --depth 1 "file://$(pwd)/no-reflog" shallow9 &&
+ cd no-reflog &&
+ git tag -d TAGB1 TAGB2 &&
+ git update-ref refs/heads/B B~~ &&
+ git gc --prune=now &&
+ cd ../shallow9 &&
+ git fetch origin &&
+ git fsck --no-dangling
+ )
+'
+test_expect_success 'fetch creating new shallow root' '
+ (
+ git clone "file://$(pwd)/." shallow10 &&
+ git commit --allow-empty -m empty &&
+ cd shallow10 &&
+ git fetch --depth=1 --progress 2>actual &&
+ # This should fetch only the empty commit, no tree or
+ # blob objects
+ grep "remote: Total 1" actual
+ )
+'
+
+test_expect_success 'setup tests for the --stdin parameter' '
+ for head in C D E F
+ do
+ add $head
+ done &&
+ for head in A B C D E F
+ do
+ git tag $head $head
+ done &&
+ cat >input <<-\EOF &&
+ refs/heads/C
+ refs/heads/A
+ refs/heads/D
+ refs/tags/C
+ refs/heads/B
+ refs/tags/A
+ refs/heads/E
+ refs/tags/B
+ refs/tags/E
+ refs/tags/D
+ EOF
+ sort <input >expect &&
+ (
+ echo refs/heads/E &&
+ echo refs/tags/E &&
+ cat input
+ ) >input.dup
+'
+
+test_expect_success 'fetch refs from cmdline' '
+ (
+ cd client &&
+ git fetch-pack --no-progress .. $(cat ../input)
+ ) >output &&
+ cut -d " " -f 2 <output | sort >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fetch refs from stdin' '
+ (
+ cd client &&
+ git fetch-pack --stdin --no-progress .. <../input
+ ) >output &&
+ cut -d " " -f 2 <output | sort >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fetch mixed refs from cmdline and stdin' '
+ (
+ cd client &&
+ tail -n +5 ../input |
+ git fetch-pack --stdin --no-progress .. $(head -n 4 ../input)
+ ) >output &&
+ cut -d " " -f 2 <output | sort >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'test duplicate refs from stdin' '
+ (
+ cd client &&
+ git fetch-pack --stdin --no-progress .. <../input.dup
+ ) >output &&
+ cut -d " " -f 2 <output | sort >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'set up tests of missing reference' '
+ cat >expect-error <<-\EOF
+ error: no such remote ref refs/heads/xyzzy
+ EOF
+'
+
+test_expect_success 'test lonely missing ref' '
+ (
+ cd client &&
+ test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy
+ ) >/dev/null 2>error-m &&
+ test_cmp expect-error error-m
+'
+
+test_expect_success 'test missing ref after existing' '
+ (
+ cd client &&
+ test_must_fail git fetch-pack --no-progress .. refs/heads/A refs/heads/xyzzy
+ ) >/dev/null 2>error-em &&
+ test_cmp expect-error error-em
+'
+
+test_expect_success 'test missing ref before existing' '
+ (
+ cd client &&
+ test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy refs/heads/A
+ ) >/dev/null 2>error-me &&
+ test_cmp expect-error error-me
+'
+
+test_expect_success 'test --all, --depth, and explicit head' '
+ (
+ cd client &&
+ git fetch-pack --no-progress --all --depth=1 .. refs/heads/A
+ ) >out-adh 2>error-adh
+'
+
+test_expect_success 'test --all, --depth, and explicit tag' '
+ git tag OLDTAG refs/heads/B~5 &&
+ (
+ cd client &&
+ git fetch-pack --no-progress --all --depth=1 .. refs/tags/OLDTAG
+ ) >out-adt 2>error-adt
+'
+
+test_expect_success 'shallow fetch with tags does not break the repository' '
+ mkdir repo1 &&
+ (
+ cd repo1 &&
+ git init &&
+ test_commit 1 &&
+ test_commit 2 &&
+ test_commit 3 &&
+ mkdir repo2 &&
+ cd repo2 &&
+ git init &&
+ git fetch --depth=2 ../.git master:branch &&
+ git fsck
+ )
+'
+
+test_expect_success 'fetch-pack can fetch a raw sha1' '
+ git init hidden &&
+ (
+ cd hidden &&
+ test_commit 1 &&
+ test_commit 2 &&
+ git update-ref refs/hidden/one HEAD^ &&
+ git config transfer.hiderefs refs/hidden &&
+ git config uploadpack.allowtipsha1inwant true
+ ) &&
+ git fetch-pack hidden $(git -C hidden rev-parse refs/hidden/one)
+'
+
+check_prot_path () {
+ cat >expected <<-EOF &&
+ Diag: url=$1
+ Diag: protocol=$2
+ Diag: path=$3
+ EOF
+ git fetch-pack --diag-url "$1" | grep -v hostandport= >actual &&
+ test_cmp expected actual
+}
+
+check_prot_host_port_path () {
+ case "$2" in
+ *ssh*)
+ pp=ssh
+ uah=userandhost
+ ehost=$(echo $3 | tr -d "[]")
+ diagport="Diag: port=$4"
+ ;;
+ *)
+ pp=$p
+ uah=hostandport
+ ehost=$(echo $3$4 | sed -e "s/22$/:22/" -e "s/NONE//")
+ diagport=""
+ ;;
+ esac
+ cat >exp <<-EOF &&
+ Diag: url=$1
+ Diag: protocol=$pp
+ Diag: $uah=$ehost
+ $diagport
+ Diag: path=$5
+ EOF
+ grep -v "^$" exp >expected
+ git fetch-pack --diag-url "$1" >actual &&
+ test_cmp expected actual
+}
+
+for r in repo re:po re/po
+do
+ # git or ssh with scheme
+ for p in "ssh+git" "git+ssh" git ssh
+ do
+ for h in host user@host user@[::1] user@::1
+ do
+ for c in "" :
+ do
+ test_expect_success "fetch-pack --diag-url $p://$h$c/$r" '
+ check_prot_host_port_path $p://$h/$r $p "$h" NONE "/$r"
+ '
+ # "/~" -> "~" conversion
+ test_expect_success "fetch-pack --diag-url $p://$h$c/~$r" '
+ check_prot_host_port_path $p://$h/~$r $p "$h" NONE "~$r"
+ '
+ done
+ done
+ for h in host User@host User@[::1]
+ do
+ test_expect_success "fetch-pack --diag-url $p://$h:22/$r" '
+ check_prot_host_port_path $p://$h:22/$r $p "$h" 22 "/$r"
+ '
+ done
+ done
+ # file with scheme
+ for p in file
+ do
+ test_expect_success "fetch-pack --diag-url $p://$h/$r" '
+ check_prot_path $p://$h/$r $p "/$r"
+ '
+ # No "/~" -> "~" conversion for file
+ test_expect_success "fetch-pack --diag-url $p://$h/~$r" '
+ check_prot_path $p://$h/~$r $p "/~$r"
+ '
+ done
+ # file without scheme
+ for h in nohost nohost:12 [::1] [::1]:23 [ [:aa
+ do
+ test_expect_success "fetch-pack --diag-url ./$h:$r" '
+ check_prot_path ./$h:$r $p "./$h:$r"
+ '
+ # No "/~" -> "~" conversion for file
+ test_expect_success "fetch-pack --diag-url ./$p:$h/~$r" '
+ check_prot_path ./$p:$h/~$r $p "./$p:$h/~$r"
+ '
+ done
+ #ssh without scheme
+ p=ssh
+ for h in host [::1]
+ do
+ test_expect_success "fetch-pack --diag-url $h:$r" '
+ check_prot_host_port_path $h:$r $p "$h" NONE "$r"
+ '
+ # Do "/~" -> "~" conversion
+ test_expect_success "fetch-pack --diag-url $h:/~$r" '
+ check_prot_host_port_path $h:/~$r $p "$h" NONE "~$r"
+ '
+ done
+done
+
+test_expect_success MINGW 'fetch-pack --diag-url file://c:/repo' '
+ check_prot_path file://c:/repo file c:/repo
+'
+test_expect_success MINGW 'fetch-pack --diag-url c:repo' '
+ check_prot_path c:repo file c:repo
'
test_done
diff --git a/t/t5501-fetch-push-alternates.sh b/t/t5501-fetch-push-alternates.sh
new file mode 100755
index 0000000..1bc57ac
--- /dev/null
+++ b/t/t5501-fetch-push-alternates.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+test_description='fetch/push involving alternates'
+. ./test-lib.sh
+
+count_objects () {
+ loose=0 inpack=0
+ eval "$(
+ git count-objects -v |
+ sed -n -e 's/^count: \(.*\)/loose=\1/p' \
+ -e 's/^in-pack: \(.*\)/inpack=\1/p'
+ )" &&
+ echo $(( $loose + $inpack ))
+}
+
+
+test_expect_success setup '
+ (
+ git init original &&
+ cd original &&
+ i=0 &&
+ while test $i -le 100
+ do
+ echo "$i" >count &&
+ git add count &&
+ git commit -m "$i" || exit
+ i=$(($i + 1))
+ done
+ ) &&
+ (
+ git clone --reference=original "file://$(pwd)/original" one &&
+ cd one &&
+ echo Z >count &&
+ git add count &&
+ git commit -m Z &&
+ count_objects >../one.count
+ ) &&
+ A=$(pwd)/original/.git/objects &&
+ git init receiver &&
+ echo "$A" >receiver/.git/objects/info/alternates &&
+ git init fetcher &&
+ echo "$A" >fetcher/.git/objects/info/alternates
+'
+
+test_expect_success 'pushing into a repository with the same alternate' '
+ (
+ cd one &&
+ git push ../receiver master:refs/heads/it
+ ) &&
+ (
+ cd receiver &&
+ count_objects >../receiver.count
+ ) &&
+ test_cmp one.count receiver.count
+'
+
+test_expect_success 'fetching from a repository with the same alternate' '
+ (
+ cd fetcher &&
+ git fetch ../one master:refs/heads/it &&
+ count_objects >../fetcher.count
+ ) &&
+ test_cmp one.count fetcher.count
+'
+
+test_done
diff --git a/t/t5502-quickfetch.sh b/t/t5502-quickfetch.sh
index 1037a72..7a46cbd 100755
--- a/t/t5502-quickfetch.sh
+++ b/t/t5502-quickfetch.sh
@@ -57,7 +57,7 @@ test_expect_success 'copy commit and tree but not blob by hand' '
cd cloned &&
git count-objects | sed -e "s/ *objects,.*//"
) ) &&
- test $cnt -eq 6
+ test $cnt -eq 6 &&
blob=$(git rev-parse HEAD:file | sed -e "s|..|&/|") &&
test -f "cloned/.git/objects/$blob" &&
diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
index bab1a53..4ca48f0 100755
--- a/t/t5503-tagfollow.sh
+++ b/t/t5503-tagfollow.sh
@@ -4,12 +4,6 @@ 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
@@ -41,21 +35,31 @@ test_expect_success setup '
'
U=UPLOAD_LOG
+UPATH="$(pwd)/$U"
+test_expect_success 'setup expect' '
cat - <<EOF >expect
-#S
want $A
-#E
EOF
+'
+
+get_needs () {
+ test -s "$1" &&
+ perl -alne '
+ next unless $F[1] eq "upload-pack<";
+ last if $F[2] eq "0000";
+ print $F[2], " ", $F[3];
+ ' "$1"
+}
+
test_expect_success 'fetch A (new commit : 1 connection)' '
- rm -f $U
+ rm -f $U &&
(
cd cloned &&
- GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+ GIT_TRACE_PACKET=$UPATH git fetch &&
test $A = $(git rev-parse --verify origin/master)
) &&
- test -s $U &&
- cut -d" " -f1,2 $U >actual &&
+ get_needs $U >actual &&
test_cmp expect actual
'
@@ -71,23 +75,23 @@ test_expect_success "create tag T on A, create C on branch cat" '
git checkout master
'
+test_expect_success 'setup expect' '
cat - <<EOF >expect
-#S
want $C
want $T
-#E
EOF
+'
+
test_expect_success 'fetch C, T (new branch, tag : 1 connection)' '
- rm -f $U
+ rm -f $U &&
(
cd cloned &&
- GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+ GIT_TRACE_PACKET=$UPATH git fetch &&
test $C = $(git rev-parse --verify origin/cat) &&
test $T = $(git rev-parse --verify tag1) &&
test $A = $(git rev-parse --verify tag1^0)
) &&
- test -s $U &&
- cut -d" " -f1,2 $U >actual &&
+ get_needs $U >actual &&
test_cmp expect actual
'
@@ -107,49 +111,49 @@ test_expect_success "create commits O, B, tag S on B" '
S=$(git rev-parse --verify tag2)
'
+test_expect_success 'setup expect' '
cat - <<EOF >expect
-#S
want $B
want $S
-#E
EOF
+'
+
test_expect_success 'fetch B, S (commit and tag : 1 connection)' '
- rm -f $U
+ rm -f $U &&
(
cd cloned &&
- GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+ GIT_TRACE_PACKET=$UPATH git fetch &&
test $B = $(git rev-parse --verify origin/master) &&
test $B = $(git rev-parse --verify tag2^0) &&
test $S = $(git rev-parse --verify tag2)
) &&
- test -s $U &&
- cut -d" " -f1,2 $U >actual &&
+ get_needs $U >actual &&
test_cmp expect actual
'
+test_expect_success 'setup expect' '
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
+ test_might_fail 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 &&
+ GIT_TRACE_PACKET=$UPATH git fetch &&
test $B = $(git rev-parse --verify origin/master) &&
test $S = $(git rev-parse --verify tag2) &&
test $B = $(git rev-parse --verify tag2^0) &&
test $T = $(git rev-parse --verify tag1) &&
test $A = $(git rev-parse --verify tag1^0)
) &&
- test -s $U &&
- cut -d" " -f1,2 $U >actual &&
+ get_needs $U >actual &&
test_cmp expect actual
'
diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh
new file mode 100755
index 0000000..44f3d5f
--- /dev/null
+++ b/t/t5504-fetch-receive-strict.sh
@@ -0,0 +1,169 @@
+#!/bin/sh
+
+test_description='fetch/receive strict mode'
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo hello >greetings &&
+ git add greetings &&
+ git commit -m greetings &&
+
+ S=$(git rev-parse :greetings | sed -e "s|^..|&/|") &&
+ X=$(echo bye | git hash-object -w --stdin | sed -e "s|^..|&/|") &&
+ mv -f .git/objects/$X .git/objects/$S &&
+
+ test_must_fail git fsck
+'
+
+test_expect_success 'fetch without strict' '
+ rm -rf dst &&
+ git init dst &&
+ (
+ cd dst &&
+ git config fetch.fsckobjects false &&
+ git config transfer.fsckobjects false &&
+ test_must_fail git fetch ../.git master
+ )
+'
+
+test_expect_success 'fetch with !fetch.fsckobjects' '
+ rm -rf dst &&
+ git init dst &&
+ (
+ cd dst &&
+ git config fetch.fsckobjects false &&
+ git config transfer.fsckobjects true &&
+ test_must_fail git fetch ../.git master
+ )
+'
+
+test_expect_success 'fetch with fetch.fsckobjects' '
+ rm -rf dst &&
+ git init dst &&
+ (
+ cd dst &&
+ git config fetch.fsckobjects true &&
+ git config transfer.fsckobjects false &&
+ test_must_fail git fetch ../.git master
+ )
+'
+
+test_expect_success 'fetch with transfer.fsckobjects' '
+ rm -rf dst &&
+ git init dst &&
+ (
+ cd dst &&
+ git config transfer.fsckobjects true &&
+ test_must_fail git fetch ../.git master
+ )
+'
+
+cat >exp <<EOF
+To dst
+! refs/heads/master:refs/heads/test [remote rejected] (missing necessary objects)
+EOF
+
+test_expect_success 'push without strict' '
+ rm -rf dst &&
+ git init dst &&
+ (
+ cd dst &&
+ git config fetch.fsckobjects false &&
+ git config transfer.fsckobjects false
+ ) &&
+ test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+ test_cmp exp act
+'
+
+test_expect_success 'push with !receive.fsckobjects' '
+ rm -rf dst &&
+ git init dst &&
+ (
+ cd dst &&
+ git config receive.fsckobjects false &&
+ git config transfer.fsckobjects true
+ ) &&
+ test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+ test_cmp exp act
+'
+
+cat >exp <<EOF
+To dst
+! refs/heads/master:refs/heads/test [remote rejected] (unpacker error)
+EOF
+
+test_expect_success 'push with receive.fsckobjects' '
+ rm -rf dst &&
+ git init dst &&
+ (
+ cd dst &&
+ git config receive.fsckobjects true &&
+ git config transfer.fsckobjects false
+ ) &&
+ test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+ test_cmp exp act
+'
+
+test_expect_success 'push with transfer.fsckobjects' '
+ rm -rf dst &&
+ git init dst &&
+ (
+ cd dst &&
+ git config transfer.fsckobjects true
+ ) &&
+ test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+ test_cmp exp act
+'
+
+cat >bogus-commit <<\EOF
+tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904
+author Bugs Bunny 1234567890 +0000
+committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000
+
+This commit object intentionally broken
+EOF
+
+test_expect_success 'push with receive.fsck.skipList' '
+ commit="$(git hash-object -t commit -w --stdin <bogus-commit)" &&
+ git push . $commit:refs/heads/bogus &&
+ rm -rf dst &&
+ git init dst &&
+ git --git-dir=dst/.git config receive.fsckObjects true &&
+ test_must_fail git push --porcelain dst bogus &&
+ git --git-dir=dst/.git config receive.fsck.skipList SKIP &&
+ echo $commit >dst/.git/SKIP &&
+ git push --porcelain dst bogus
+'
+
+test_expect_success 'push with receive.fsck.missingEmail=warn' '
+ commit="$(git hash-object -t commit -w --stdin <bogus-commit)" &&
+ git push . $commit:refs/heads/bogus &&
+ rm -rf dst &&
+ git init dst &&
+ git --git-dir=dst/.git config receive.fsckobjects true &&
+ test_must_fail git push --porcelain dst bogus &&
+ git --git-dir=dst/.git config \
+ receive.fsck.missingEmail warn &&
+ git push --porcelain dst bogus >act 2>&1 &&
+ grep "missingEmail" act &&
+ git --git-dir=dst/.git branch -D bogus &&
+ git --git-dir=dst/.git config --add \
+ receive.fsck.missingEmail ignore &&
+ git --git-dir=dst/.git config --add \
+ receive.fsck.badDate warn &&
+ git push --porcelain dst bogus >act 2>&1 &&
+ test_must_fail grep "missingEmail" act
+'
+
+test_expect_success \
+ 'receive.fsck.unterminatedHeader=warn triggers error' '
+ rm -rf dst &&
+ git init dst &&
+ git --git-dir=dst/.git config receive.fsckobjects true &&
+ git --git-dir=dst/.git config \
+ receive.fsck.unterminatedheader warn &&
+ test_must_fail git push --porcelain dst HEAD >act 2>&1 &&
+ grep "Cannot demote unterminatedheader" act
+'
+
+test_done
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 4c498b1..dd2e6ce 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -42,99 +42,142 @@ check_tracking_branch () {
}
test_expect_success setup '
-
setup_repository one &&
setup_repository two &&
(
- cd two && git branch another
+ cd two &&
+ git branch another
) &&
git clone one test
+'
+test_expect_success 'add remote whose URL agrees with url.<...>.insteadOf' '
+ test_config url.git@host.com:team/repo.git.insteadOf myremote &&
+ git remote add myremote git@host.com:team/repo.git
'
-test_expect_success 'remote information for the origin' '
-(
- cd test &&
- tokens_match origin "$(git remote)" &&
- check_remote_track origin master side &&
- check_tracking_branch origin HEAD master side
-)
+test_expect_success C_LOCALE_OUTPUT 'remote information for the origin' '
+ (
+ cd test &&
+ tokens_match origin "$(git remote)" &&
+ check_remote_track origin master side &&
+ check_tracking_branch origin HEAD master side
+ )
'
test_expect_success 'add another remote' '
-(
- cd test &&
- git remote add -f second ../two &&
- tokens_match "origin second" "$(git remote)" &&
- check_remote_track origin master side &&
- check_remote_track second master side another &&
- check_tracking_branch second master side another &&
- git for-each-ref "--format=%(refname)" refs/remotes |
- sed -e "/^refs\/remotes\/origin\//d" \
- -e "/^refs\/remotes\/second\//d" >actual &&
- >expect &&
- test_cmp expect actual
-)
+ (
+ cd test &&
+ git remote add -f second ../two &&
+ tokens_match "origin second" "$(git remote)" &&
+ check_tracking_branch second master side another &&
+ git for-each-ref "--format=%(refname)" refs/remotes |
+ sed -e "/^refs\/remotes\/origin\//d" \
+ -e "/^refs\/remotes\/second\//d" >actual &&
+ >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success C_LOCALE_OUTPUT 'check remote-tracking' '
+ (
+ cd test &&
+ check_remote_track origin master side &&
+ check_remote_track second master side another
+ )
'
test_expect_success 'remote forces tracking branches' '
-(
- cd test &&
- case `git config remote.second.fetch` in
- +*) true ;;
- *) false ;;
- esac
-)
+ (
+ cd test &&
+ case $(git config remote.second.fetch) in
+ +*) true ;;
+ *) false ;;
+ esac
+ )
'
test_expect_success 'remove remote' '
-(
- cd test &&
- git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/master &&
- git remote rm second
-)
+ (
+ cd test &&
+ git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/master &&
+ git remote rm second
+ )
'
-test_expect_success 'remove remote' '
-(
- cd test &&
- tokens_match origin "$(git remote)" &&
- check_remote_track origin master side &&
- git for-each-ref "--format=%(refname)" refs/remotes |
- sed -e "/^refs\/remotes\/origin\//d" >actual &&
- >expect &&
- test_cmp expect actual
-)
+test_expect_success C_LOCALE_OUTPUT 'remove remote' '
+ (
+ cd test &&
+ tokens_match origin "$(git remote)" &&
+ check_remote_track origin master side &&
+ git for-each-ref "--format=%(refname)" refs/remotes |
+ sed -e "/^refs\/remotes\/origin\//d" >actual &&
+ >expect &&
+ test_cmp expect actual
+ )
'
-test_expect_success 'remove remote protects 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
+test_expect_success 'remove remote protects local branches' '
+ (
+ cd test &&
+ cat >expect1 <<-\EOF &&
+ Note: A branch outside the refs/remotes/ hierarchy was not removed;
+ to delete it, use:
+ git branch -d master
+ EOF
+ cat >expect2 <<-\EOF &&
+ Note: Some branches outside the refs/remotes/ hierarchy were not removed;
+ to delete them, use:
+ git branch -d foobranch
+ git branch -d master
+ EOF
+ git tag footag &&
+ git config --add remote.oops.fetch "+refs/*:refs/*" &&
+ git remote remove oops 2>actual1 &&
+ git branch foobranch &&
+ git config --add remote.oops.fetch "+refs/*:refs/*" &&
+ git remote rm oops 2>actual2 &&
+ git branch -d foobranch &&
+ git tag -d footag &&
+ test_i18ncmp expect1 actual1 &&
+ test_i18ncmp expect2 actual2
+ )
+'
+
+test_expect_success 'remove errors out early when deleting non-existent branch' '
+ (
+ cd test &&
+ echo "fatal: No such remote: foo" >expect &&
+ test_must_fail git remote rm foo 2>actual &&
+ test_i18ncmp expect actual
+ )
+'
+
+test_expect_success 'rename errors out early when deleting non-existent branch' '
+ (
+ cd test &&
+ echo "fatal: No such remote: foo" >expect &&
+ test_must_fail git remote rename foo bar 2>actual &&
+ test_i18ncmp expect actual
+ )
+'
+
+test_expect_success 'add existing foreign_vcs remote' '
+ test_config remote.foo.vcs bar &&
+ echo "fatal: remote foo already exists." >expect &&
+ test_must_fail git remote add foo bar 2>actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'add existing foreign_vcs remote' '
+ test_config remote.foo.vcs bar &&
+ test_config remote.bar.vcs bar &&
+ echo "fatal: remote bar already exists." >expect &&
+ test_must_fail git remote rename foo bar 2>actual &&
+ test_i18ncmp expect actual
+'
+
+cat >test/expect <<EOF
* remote origin
Fetch URL: $(pwd)/one
Push URL: $(pwd)/one
@@ -155,45 +198,47 @@ cat > test/expect << EOF
* remote two
Fetch URL: ../two
Push URL: ../three
- HEAD branch (remote HEAD is ambiguous, may be one of the following):
- another
- master
+ HEAD branch: master
Local refs configured for 'git push':
ahead forces to master (fast-forwardable)
master pushes to another (up to date)
EOF
test_expect_success 'show' '
- (cd test &&
- git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream &&
- git fetch &&
- git checkout -b ahead origin/master &&
- echo 1 >> file &&
- test_tick &&
- git commit -m update file &&
- git checkout master &&
- git branch --track octopus origin/master &&
- git branch --track rebase origin/master &&
- git branch -d -r origin/master &&
- git config --add remote.two.url ../two &&
- git config --add remote.two.pushurl ../three &&
- git config branch.rebase.rebase true &&
- git config branch.octopus.merge "topic-a topic-b topic-c" &&
- (cd ../one &&
- echo 1 > file &&
- test_tick &&
- git commit -m update file) &&
- git config --add remote.origin.push : &&
- git config --add remote.origin.push refs/heads/master:refs/heads/upstream &&
- git config --add remote.origin.push +refs/tags/lastbackup &&
- git config --add remote.two.push +refs/heads/ahead:refs/heads/master &&
- git config --add remote.two.push refs/heads/master:refs/heads/another &&
- git remote show origin two > output &&
- git branch -d rebase octopus &&
- test_cmp expect output)
-'
-
-cat > test/expect << EOF
+ (
+ cd test &&
+ git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream &&
+ git fetch &&
+ git checkout -b ahead origin/master &&
+ echo 1 >>file &&
+ test_tick &&
+ git commit -m update file &&
+ git checkout master &&
+ git branch --track octopus origin/master &&
+ git branch --track rebase origin/master &&
+ git branch -d -r origin/master &&
+ git config --add remote.two.url ../two &&
+ git config --add remote.two.pushurl ../three &&
+ git config branch.rebase.rebase true &&
+ git config branch.octopus.merge "topic-a topic-b topic-c" &&
+ (
+ cd ../one &&
+ echo 1 >file &&
+ test_tick &&
+ git commit -m update file
+ ) &&
+ git config --add remote.origin.push : &&
+ git config --add remote.origin.push refs/heads/master:refs/heads/upstream &&
+ git config --add remote.origin.push +refs/tags/lastbackup &&
+ git config --add remote.two.push +refs/heads/ahead:refs/heads/master &&
+ git config --add remote.two.push refs/heads/master:refs/heads/another &&
+ git remote show origin two >output &&
+ git branch -d rebase octopus &&
+ test_i18ncmp expect output
+ )
+'
+
+cat >test/expect <<EOF
* remote origin
Fetch URL: $(pwd)/one
Push URL: $(pwd)/one
@@ -211,113 +256,256 @@ cat > test/expect << EOF
EOF
test_expect_success 'show -n' '
- (mv one one.unreachable &&
- cd test &&
- git remote show -n origin > output &&
- mv ../one.unreachable ../one &&
- test_cmp expect output)
+ mv one one.unreachable &&
+ (
+ cd test &&
+ git remote show -n origin >output &&
+ mv ../one.unreachable ../one &&
+ test_i18ncmp expect output
+ )
'
test_expect_success 'prune' '
- (cd one &&
- git branch -m side side2) &&
- (cd test &&
- git fetch origin &&
- git remote prune origin &&
- git rev-parse refs/remotes/origin/side2 &&
- test_must_fail git rev-parse refs/remotes/origin/side)
+ (
+ cd one &&
+ git branch -m side side2
+ ) &&
+ (
+ cd test &&
+ git fetch origin &&
+ git remote prune origin &&
+ git rev-parse refs/remotes/origin/side2 &&
+ test_must_fail git rev-parse refs/remotes/origin/side
+ )
'
test_expect_success 'set-head --delete' '
- (cd test &&
- git symbolic-ref refs/remotes/origin/HEAD &&
- git remote set-head --delete origin &&
- test_must_fail git symbolic-ref refs/remotes/origin/HEAD)
+ (
+ cd test &&
+ git symbolic-ref refs/remotes/origin/HEAD &&
+ git remote set-head --delete origin &&
+ test_must_fail git symbolic-ref refs/remotes/origin/HEAD
+ )
'
test_expect_success 'set-head --auto' '
- (cd test &&
- git remote set-head --auto origin &&
- echo refs/remotes/origin/master >expect &&
- git symbolic-ref refs/remotes/origin/HEAD >output &&
- test_cmp expect output
+ (
+ cd test &&
+ git remote set-head --auto origin &&
+ echo refs/remotes/origin/master >expect &&
+ git symbolic-ref refs/remotes/origin/HEAD >output &&
+ test_cmp expect output
)
'
-cat >test/expect <<EOF
-error: Multiple remote HEAD branches. Please choose one explicitly with:
- git remote set-head two another
- git remote set-head two master
-EOF
-
-test_expect_success 'set-head --auto fails w/multiple HEADs' '
- (cd test &&
- test_must_fail git remote set-head --auto two >output 2>&1 &&
- test_cmp expect output)
+test_expect_success 'set-head --auto has no problem w/multiple HEADs' '
+ (
+ cd test &&
+ git fetch two "refs/heads/*:refs/remotes/two/*" &&
+ git remote set-head --auto two >output 2>&1 &&
+ echo "two/HEAD set to master" >expect &&
+ test_i18ncmp expect output
+ )
'
-cat >test/expect <<EOF
+cat >test/expect <<\EOF
refs/remotes/origin/side2
EOF
test_expect_success 'set-head explicit' '
- (cd test &&
- git remote set-head origin side2 &&
- git symbolic-ref refs/remotes/origin/HEAD >output &&
- git remote set-head origin master &&
- test_cmp expect output)
+ (
+ cd test &&
+ git remote set-head origin side2 &&
+ git symbolic-ref refs/remotes/origin/HEAD >output &&
+ git remote set-head origin master &&
+ test_cmp expect output
+ )
'
-cat > test/expect << EOF
+cat >test/expect <<EOF
Pruning origin
URL: $(pwd)/one
* [would prune] origin/side2
EOF
test_expect_success 'prune --dry-run' '
- (cd one &&
- git branch -m side2 side) &&
- (cd test &&
- git remote prune --dry-run origin > output &&
- git rev-parse refs/remotes/origin/side2 &&
- test_must_fail git rev-parse refs/remotes/origin/side &&
- (cd ../one &&
- git branch -m side side2) &&
- test_cmp expect output)
+ (
+ cd one &&
+ git branch -m side2 side) &&
+ (
+ cd test &&
+ git remote prune --dry-run origin >output &&
+ git rev-parse refs/remotes/origin/side2 &&
+ test_must_fail git rev-parse refs/remotes/origin/side &&
+ (
+ cd ../one &&
+ git branch -m side side2) &&
+ test_i18ncmp expect output
+ )
'
test_expect_success 'add --mirror && prune' '
- (mkdir mirror &&
- cd mirror &&
- git init --bare &&
- git remote add --mirror -f origin ../one) &&
- (cd one &&
- git branch -m side2 side) &&
- (cd mirror &&
- git rev-parse --verify refs/heads/side2 &&
- test_must_fail git rev-parse --verify refs/heads/side &&
- git fetch origin &&
- git remote prune origin &&
- test_must_fail git rev-parse --verify refs/heads/side2 &&
- git rev-parse --verify refs/heads/side)
+ mkdir mirror &&
+ (
+ cd mirror &&
+ git init --bare &&
+ git remote add --mirror -f origin ../one
+ ) &&
+ (
+ cd one &&
+ git branch -m side2 side
+ ) &&
+ (
+ cd mirror &&
+ git rev-parse --verify refs/heads/side2 &&
+ test_must_fail git rev-parse --verify refs/heads/side &&
+ git fetch origin &&
+ git remote prune origin &&
+ test_must_fail git rev-parse --verify refs/heads/side2 &&
+ git rev-parse --verify refs/heads/side
+ )
+'
+
+test_expect_success 'add --mirror=fetch' '
+ mkdir mirror-fetch &&
+ git init mirror-fetch/parent &&
+ (
+ cd mirror-fetch/parent &&
+ test_commit one
+ ) &&
+ git init --bare mirror-fetch/child &&
+ (
+ cd mirror-fetch/child &&
+ git remote add --mirror=fetch -f parent ../parent
+ )
+'
+
+test_expect_success 'fetch mirrors act as mirrors during fetch' '
+ (
+ cd mirror-fetch/parent &&
+ git branch new &&
+ git branch -m master renamed
+ ) &&
+ (
+ cd mirror-fetch/child &&
+ git fetch parent &&
+ git rev-parse --verify refs/heads/new &&
+ git rev-parse --verify refs/heads/renamed
+ )
+'
+
+test_expect_success 'fetch mirrors can prune' '
+ (
+ cd mirror-fetch/child &&
+ git remote prune parent &&
+ test_must_fail git rev-parse --verify refs/heads/master
+ )
+'
+
+test_expect_success 'fetch mirrors do not act as mirrors during push' '
+ (
+ cd mirror-fetch/parent &&
+ git checkout HEAD^0
+ ) &&
+ (
+ cd mirror-fetch/child &&
+ git branch -m renamed renamed2 &&
+ git push parent :
+ ) &&
+ (
+ cd mirror-fetch/parent &&
+ git rev-parse --verify renamed &&
+ test_must_fail git rev-parse --verify refs/heads/renamed2
+ )
+'
+
+test_expect_success 'add fetch mirror with specific branches' '
+ git init --bare mirror-fetch/track &&
+ (
+ cd mirror-fetch/track &&
+ git remote add --mirror=fetch -t heads/new parent ../parent
+ )
+'
+
+test_expect_success 'fetch mirror respects specific branches' '
+ (
+ cd mirror-fetch/track &&
+ git fetch parent &&
+ git rev-parse --verify refs/heads/new &&
+ test_must_fail git rev-parse --verify refs/heads/renamed
+ )
+'
+
+test_expect_success 'add --mirror=push' '
+ mkdir mirror-push &&
+ git init --bare mirror-push/public &&
+ git init mirror-push/private &&
+ (
+ cd mirror-push/private &&
+ test_commit one &&
+ git remote add --mirror=push public ../public
+ )
+'
+
+test_expect_success 'push mirrors act as mirrors during push' '
+ (
+ cd mirror-push/private &&
+ git branch new &&
+ git branch -m master renamed &&
+ git push public
+ ) &&
+ (
+ cd mirror-push/private &&
+ git rev-parse --verify refs/heads/new &&
+ git rev-parse --verify refs/heads/renamed &&
+ test_must_fail git rev-parse --verify refs/heads/master
+ )
+'
+
+test_expect_success 'push mirrors do not act as mirrors during fetch' '
+ (
+ cd mirror-push/public &&
+ git branch -m renamed renamed2 &&
+ git symbolic-ref HEAD refs/heads/renamed2
+ ) &&
+ (
+ cd mirror-push/private &&
+ git fetch public &&
+ git rev-parse --verify refs/heads/renamed &&
+ test_must_fail git rev-parse --verify refs/heads/renamed2
+ )
+'
+
+test_expect_success 'push mirrors do not allow you to specify refs' '
+ git init mirror-push/track &&
+ (
+ cd mirror-push/track &&
+ test_must_fail git remote add --mirror=push -t new public ../public
+ )
'
test_expect_success 'add alt && prune' '
- (mkdir alttst &&
- cd alttst &&
- git init &&
- git remote add -f origin ../one &&
- git config remote.alt.url ../one &&
- git config remote.alt.fetch "+refs/heads/*:refs/remotes/origin/*") &&
- (cd one &&
- git branch -m side side2) &&
- (cd alttst &&
- git rev-parse --verify refs/remotes/origin/side &&
- test_must_fail git rev-parse --verify refs/remotes/origin/side2 &&
- git fetch alt &&
- git remote prune alt &&
- test_must_fail git rev-parse --verify refs/remotes/origin/side &&
- git rev-parse --verify refs/remotes/origin/side2)
+ mkdir alttst &&
+ (
+ cd alttst &&
+ git init &&
+ git remote add -f origin ../one &&
+ git config remote.alt.url ../one &&
+ git config remote.alt.fetch "+refs/heads/*:refs/remotes/origin/*"
+ ) &&
+ (
+ cd one &&
+ git branch -m side side2
+ ) &&
+ (
+ cd alttst &&
+ git rev-parse --verify refs/remotes/origin/side &&
+ test_must_fail git rev-parse --verify refs/remotes/origin/side2 &&
+ git fetch alt &&
+ git remote prune alt &&
+ test_must_fail git rev-parse --verify refs/remotes/origin/side &&
+ git rev-parse --verify refs/remotes/origin/side2
+ )
'
cat >test/expect <<\EOF
@@ -325,20 +513,24 @@ some-tag
EOF
test_expect_success 'add with reachable tags (default)' '
- (cd one &&
- >foobar &&
- git add foobar &&
- git commit -m "Foobar" &&
- git tag -a -m "Foobar tag" foobar-tag &&
- git reset --hard HEAD~1 &&
- git tag -a -m "Some tag" some-tag) &&
- (mkdir add-tags &&
- cd add-tags &&
- git init &&
- git remote add -f origin ../one &&
- git tag -l some-tag >../test/output &&
- git tag -l foobar-tag >>../test/output &&
- test_must_fail git config remote.origin.tagopt) &&
+ (
+ cd one &&
+ >foobar &&
+ git add foobar &&
+ git commit -m "Foobar" &&
+ git tag -a -m "Foobar tag" foobar-tag &&
+ git reset --hard HEAD~1 &&
+ git tag -a -m "Some tag" some-tag
+ ) &&
+ mkdir add-tags &&
+ (
+ cd add-tags &&
+ git init &&
+ git remote add -f origin ../one &&
+ git tag -l some-tag >../test/output &&
+ git tag -l foobar-tag >>../test/output &&
+ test_must_fail git config remote.origin.tagopt
+ ) &&
test_cmp test/expect test/output
'
@@ -349,14 +541,16 @@ foobar-tag
EOF
test_expect_success 'add --tags' '
- (rm -rf add-tags &&
- mkdir add-tags &&
- cd add-tags &&
- git init &&
- git remote add -f --tags origin ../one &&
- git tag -l some-tag >../test/output &&
- git tag -l foobar-tag >>../test/output &&
- git config remote.origin.tagopt >>../test/output) &&
+ rm -rf add-tags &&
+ (
+ mkdir add-tags &&
+ cd add-tags &&
+ git init &&
+ git remote add -f --tags origin ../one &&
+ git tag -l some-tag >../test/output &&
+ git tag -l foobar-tag >>../test/output &&
+ git config remote.origin.tagopt >>../test/output
+ ) &&
test_cmp test/expect test/output
'
@@ -365,25 +559,31 @@ cat >test/expect <<\EOF
EOF
test_expect_success 'add --no-tags' '
- (rm -rf add-tags &&
- mkdir add-no-tags &&
- cd add-no-tags &&
- git init &&
- git remote add -f --no-tags origin ../one &&
- git tag -l some-tag >../test/output &&
- git tag -l foobar-tag >../test/output &&
- git config remote.origin.tagopt >>../test/output) &&
- (cd one &&
- git tag -d some-tag foobar-tag) &&
+ rm -rf add-tags &&
+ (
+ mkdir add-no-tags &&
+ cd add-no-tags &&
+ git init &&
+ git remote add -f --no-tags origin ../one &&
+ git tag -l some-tag >../test/output &&
+ git tag -l foobar-tag >../test/output &&
+ git config remote.origin.tagopt >>../test/output
+ ) &&
+ (
+ cd one &&
+ git tag -d some-tag foobar-tag
+ ) &&
test_cmp test/expect test/output
'
test_expect_success 'reject --no-no-tags' '
- (cd add-no-tags &&
- test_must_fail git remote add -f --no-no-tags neworigin ../one)
+ (
+ cd add-no-tags &&
+ test_must_fail git remote add -f --no-no-tags neworigin ../one
+ )
'
-cat > one/expect << EOF
+cat >one/expect <<\EOF
apis/master
apis/side
drosophila/another
@@ -392,17 +592,17 @@ cat > one/expect << EOF
EOF
test_expect_success 'update' '
-
- (cd one &&
- git remote add drosophila ../two &&
- git remote add apis ../mirror &&
- git remote update &&
- git branch -r > output &&
- test_cmp expect output)
-
+ (
+ cd one &&
+ git remote add drosophila ../two &&
+ git remote add apis ../mirror &&
+ git remote update &&
+ git branch -r >output &&
+ test_cmp expect output
+ )
'
-cat > one/expect << EOF
+cat >one/expect <<\EOF
drosophila/another
drosophila/master
drosophila/side
@@ -413,34 +613,40 @@ cat > one/expect << EOF
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)
-
+ (
+ cd one &&
+ for b in $(git branch -r)
+ do
+ git branch -r -d $b || exit 1
+ done &&
+ git remote add manduca ../mirror &&
+ git remote add megaloprepus ../mirror &&
+ git config remotes.phobaeticus "drosophila megaloprepus" &&
+ git config remotes.titanus manduca &&
+ git remote update phobaeticus titanus &&
+ git branch -r >output &&
+ test_cmp expect output
+ )
'
test_expect_success 'update --prune' '
-
- (cd one &&
- git branch -m side2 side3) &&
- (cd test &&
- git remote update --prune &&
- (cd ../one && git branch -m side3 side2)
- git rev-parse refs/remotes/origin/side3 &&
- test_must_fail git rev-parse refs/remotes/origin/side2)
+ (
+ cd one &&
+ git branch -m side2 side3
+ ) &&
+ (
+ cd test &&
+ git remote update --prune &&
+ (
+ cd ../one &&
+ git branch -m side3 side2
+ ) &&
+ git rev-parse refs/remotes/origin/side3 &&
+ test_must_fail git rev-parse refs/remotes/origin/side2
+ )
'
-cat > one/expect << EOF
+cat >one/expect <<-\EOF
apis/master
apis/side
manduca/master
@@ -450,145 +656,204 @@ cat > one/expect << EOF
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)
-
+ (
+ cd one &&
+ for b in $(git branch -r)
+ do
+ git branch -r -d $b || exit 1
+ done &&
+ git config remote.drosophila.skipDefaultUpdate true &&
+ git remote update default &&
+ git branch -r >output &&
+ test_cmp expect output
+ )
'
-cat > one/expect << EOF
+cat >one/expect <<\EOF
drosophila/another
drosophila/master
drosophila/side
EOF
test_expect_success 'update default (overridden, with funny whitespace)' '
-
- (cd one &&
- for b in $(git branch -r)
- do
- 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)
-
+ (
+ cd one &&
+ for b in $(git branch -r)
+ do
+ git branch -r -d $b || exit 1
+ 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)
-
+ (
+ cd one &&
+ for b in $(git branch -r)
+ do
+ git branch -r -d $b || exit 1
+ done &&
+ git config remotes.default "drosophila" &&
+ git remote update &&
+ git branch -r >output &&
+ test_cmp expect output
+ )
'
test_expect_success '"remote show" does not show symbolic refs' '
-
git clone one three &&
- (cd three &&
- git remote show origin > output &&
- ! grep "^ *HEAD$" < output &&
- ! grep -i stale < output)
-
+ (
+ cd three &&
+ git remote show origin >output &&
+ ! grep "^ *HEAD$" < output &&
+ ! grep -i stale < output
+ )
'
test_expect_success 'reject adding remote with an invalid name' '
-
test_must_fail git remote add some:url desired-name
-
'
# The first three test if the tracking branches are properly renamed,
# the last two ones check if the config is updated.
test_expect_success 'rename a remote' '
-
git clone one four &&
- (cd four &&
- git remote rename origin upstream &&
- rmdir .git/refs/remotes/origin &&
- test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" &&
- test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" &&
- test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" &&
- test "$(git config branch.master.remote)" = "upstream")
+ (
+ cd four &&
+ git remote rename origin upstream &&
+ rmdir .git/refs/remotes/origin &&
+ test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" &&
+ test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" &&
+ test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" &&
+ test "$(git config branch.master.remote)" = "upstream"
+ )
+'
+
+test_expect_success 'rename does not update a non-default fetch refspec' '
+ git clone one four.one &&
+ (
+ cd four.one &&
+ git config remote.origin.fetch +refs/heads/*:refs/heads/origin/* &&
+ git remote rename origin upstream &&
+ test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/heads/origin/*" &&
+ git rev-parse -q origin/master
+ )
+'
+test_expect_success 'rename a remote with name part of fetch spec' '
+ git clone one four.two &&
+ (
+ cd four.two &&
+ git remote rename origin remote &&
+ git remote rename remote upstream &&
+ test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*"
+ )
+'
+
+test_expect_success 'rename a remote with name prefix of other remote' '
+ git clone one four.three &&
+ (
+ cd four.three &&
+ git remote add o git://example.com/repo.git &&
+ git remote rename o upstream &&
+ test "$(git rev-parse origin/master)" = "$(git rev-parse master)"
+ )
'
-cat > remotes_origin << EOF
+cat >remotes_origin <<EOF
URL: $(pwd)/one
Push: refs/heads/master:refs/heads/upstream
+Push: refs/heads/next:refs/heads/upstream2
Pull: refs/heads/master:refs/heads/origin
+Pull: refs/heads/next:refs/heads/origin2
EOF
test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '
git clone one five &&
origin_url=$(pwd)/one &&
- (cd five &&
- git remote rm origin &&
- mkdir -p .git/remotes &&
- cat ../remotes_origin > .git/remotes/origin &&
- git remote rename origin origin &&
- ! test -f .git/remotes/origin &&
- test "$(git config remote.origin.url)" = "$origin_url" &&
- test "$(git config remote.origin.push)" = "refs/heads/master:refs/heads/upstream" &&
- test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
+ (
+ cd five &&
+ git remote remove origin &&
+ mkdir -p .git/remotes &&
+ cat ../remotes_origin >.git/remotes/origin &&
+ git remote rename origin origin &&
+ test_path_is_missing .git/remotes/origin &&
+ test "$(git config remote.origin.url)" = "$origin_url" &&
+ cat >push_expected <<-\EOF &&
+ refs/heads/master:refs/heads/upstream
+ refs/heads/next:refs/heads/upstream2
+ EOF
+ cat >fetch_expected <<-\EOF &&
+ refs/heads/master:refs/heads/origin
+ refs/heads/next:refs/heads/origin2
+ EOF
+ git config --get-all remote.origin.push >push_actual &&
+ git config --get-all remote.origin.fetch >fetch_actual &&
+ test_cmp push_expected push_actual &&
+ test_cmp fetch_expected fetch_actual
+ )
'
test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
git clone one six &&
origin_url=$(pwd)/one &&
- (cd six &&
- git remote rm origin &&
- echo "$origin_url" > .git/branches/origin &&
- git remote rename origin origin &&
- ! test -f .git/branches/origin &&
- test "$(git config remote.origin.url)" = "$origin_url" &&
- test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
+ (
+ cd six &&
+ git remote rm origin &&
+ echo "$origin_url" >.git/branches/origin &&
+ git remote rename origin origin &&
+ test_path_is_missing .git/branches/origin &&
+ test "$(git config remote.origin.url)" = "$origin_url" &&
+ test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin" &&
+ test "$(git config remote.origin.push)" = "HEAD:refs/heads/master"
+ )
'
-test_expect_success 'remote prune to cause a dangling symref' '
+test_expect_success 'migrate a remote from named file in $GIT_DIR/branches (2)' '
git clone one seven &&
(
+ cd seven &&
+ git remote rm origin &&
+ echo "quux#foom" > .git/branches/origin &&
+ git remote rename origin origin &&
+ test_path_is_missing .git/branches/origin &&
+ test "$(git config remote.origin.url)" = "quux" &&
+ test "$(git config remote.origin.fetch)" = "refs/heads/foom:refs/heads/origin"
+ test "$(git config remote.origin.push)" = "HEAD:refs/heads/foom"
+ )
+'
+
+test_expect_success 'remote prune to cause a dangling symref' '
+ git clone one eight &&
+ (
cd one &&
git checkout side2 &&
git branch -D master
) &&
(
- cd seven &&
+ cd eight &&
git remote prune origin
) >err 2>&1 &&
- grep "has become dangling" err &&
+ test_i18ngrep "has become dangling" err &&
: And the dangling symref will not cause other annoying errors &&
(
- cd seven &&
+ cd eight &&
git branch -a
) 2>err &&
! grep "points nowhere" err &&
(
- cd seven &&
+ cd eight &&
test_must_fail git branch nomore origin
) 2>err &&
grep "dangling symref" err
'
test_expect_success 'show empty remote' '
-
test_create_repo empty &&
git clone empty empty-clone &&
(
@@ -692,6 +957,28 @@ test_expect_success 'new remote' '
cmp expect actual
'
+get_url_test () {
+ cat >expect &&
+ git remote get-url "$@" >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'get-url on new remote' '
+ echo foo | get_url_test someremote &&
+ echo foo | get_url_test --all someremote &&
+ echo foo | get_url_test --push someremote &&
+ echo foo | get_url_test --push --all someremote
+'
+
+test_expect_success 'remote set-url with locked config' '
+ test_when_finished "rm -f .git/config.lock" &&
+ git config --get-all remote.someremote.url >expect &&
+ >.git/config.lock &&
+ test_must_fail git remote set-url someremote baz &&
+ 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 &&
@@ -734,6 +1021,13 @@ test_expect_success 'remote set-url --push zot' '
cmp expect actual
'
+test_expect_success 'get-url with different urls' '
+ echo baz | get_url_test someremote &&
+ echo baz | get_url_test --all someremote &&
+ echo zot | get_url_test --push someremote &&
+ echo zot | get_url_test --push --all someremote
+'
+
test_expect_success 'remote set-url --push qux zot' '
git remote set-url --push someremote qux zot &&
echo qux >expect &&
@@ -768,6 +1062,14 @@ test_expect_success 'remote set-url --push --add aaa' '
cmp expect actual
'
+test_expect_success 'get-url on multi push remote' '
+ echo foo | get_url_test --push someremote &&
+ get_url_test --push --all someremote <<-\EOF
+ foo
+ aaa
+ EOF
+'
+
test_expect_success 'remote set-url --push bar aaa' '
git remote set-url --push someremote bar aaa &&
echo foo >expect &&
@@ -812,6 +1114,14 @@ test_expect_success 'remote set-url --add bbb' '
cmp expect actual
'
+test_expect_success 'get-url on multi fetch remote' '
+ echo baz | get_url_test someremote &&
+ get_url_test --all someremote <<-\EOF
+ baz
+ bbb
+ EOF
+'
+
test_expect_success 'remote set-url --delete .*' '
test_must_fail git remote set-url --delete someremote .\* &&
echo "YYY" >expect &&
@@ -864,4 +1174,32 @@ test_expect_success 'remote set-url --delete baz' '
cmp expect actual
'
+test_expect_success 'extra args: setup' '
+ # add a dummy origin so that this does not trigger failure
+ git remote add origin .
+'
+
+test_extra_arg () {
+ test_expect_success "extra args: $*" "
+ test_must_fail git remote $* bogus_extra_arg 2>actual &&
+ grep '^usage:' actual
+ "
+}
+
+test_extra_arg add nick url
+test_extra_arg rename origin newname
+test_extra_arg remove origin
+test_extra_arg set-head origin master
+# set-branches takes any number of args
+test_extra_arg get-url origin newurl
+test_extra_arg set-url origin newurl oldurl
+# show takes any number of args
+# prune takes any number of args
+# update takes any number of args
+
+test_expect_success 'add remote matching the "insteadOf" URL' '
+ git config url.xyz@example.com.insteadOf backup &&
+ git remote add backup xyz@example.com
+'
+
test_done
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
index b7b7dda..83d5558 100755
--- a/t/t5506-remote-groups.sh
+++ b/t/t5506-remote-groups.sh
@@ -20,7 +20,7 @@ update_repos() {
}
repo_fetched() {
- if test "`git log -1 --pretty=format:%s $1 --`" = "`cat mark`"; then
+ if test "$(git log -1 --pretty=format:%s $1 --)" = "$(cat mark)"; then
echo >&2 "repo was fetched: $1"
return 0
fi
@@ -43,10 +43,10 @@ test_expect_success 'no group updates all' '
repo_fetched two
'
-test_expect_success 'nonexistant group produces error' '
- mark nonexistant &&
+test_expect_success 'nonexistent group produces error' '
+ mark nonexistent &&
update_repos &&
- test_must_fail git remote update nonexistant &&
+ test_must_fail git remote update nonexistent &&
! repo_fetched one &&
! repo_fetched two
'
diff --git a/t/t5507-remote-environment.sh b/t/t5507-remote-environment.sh
new file mode 100755
index 0000000..e614929
--- /dev/null
+++ b/t/t5507-remote-environment.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+test_description='check environment showed to remote side of transports'
+. ./test-lib.sh
+
+test_expect_success 'set up "remote" push situation' '
+ test_commit one &&
+ git config push.default current &&
+ git init remote
+'
+
+test_expect_success 'set up fake ssh' '
+ GIT_SSH_COMMAND="f() {
+ cd \"\$TRASH_DIRECTORY\" &&
+ eval \"\$2\"
+ }; f" &&
+ export GIT_SSH_COMMAND &&
+ export TRASH_DIRECTORY
+'
+
+# due to receive.denyCurrentBranch=true
+test_expect_success 'confirm default push fails' '
+ test_must_fail git push remote
+'
+
+test_expect_success 'config does not travel over same-machine push' '
+ test_must_fail git -c receive.denyCurrentBranch=false push remote
+'
+
+test_expect_success 'config does not travel over ssh push' '
+ test_must_fail git -c receive.denyCurrentBranch=false push host:remote
+'
+
+test_done
diff --git a/t/t5509-fetch-push-namespaces.sh b/t/t5509-fetch-push-namespaces.sh
new file mode 100755
index 0000000..bc44ac3
--- /dev/null
+++ b/t/t5509-fetch-push-namespaces.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+test_description='fetch/push involving ref namespaces'
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_tick &&
+ git init original &&
+ (
+ cd original &&
+ echo 0 >count &&
+ git add count &&
+ test_commit 0 &&
+ echo 1 >count &&
+ git add count &&
+ test_commit 1 &&
+ git remote add pushee-namespaced "ext::git --namespace=namespace %s ../pushee" &&
+ git remote add pushee-unnamespaced ../pushee
+ ) &&
+ commit0=$(cd original && git rev-parse HEAD^) &&
+ commit1=$(cd original && git rev-parse HEAD) &&
+ git init pushee &&
+ git init puller
+'
+
+test_expect_success 'pushing into a repository using a ref namespace' '
+ (
+ cd original &&
+ git push pushee-namespaced master &&
+ git ls-remote pushee-namespaced >actual &&
+ printf "$commit1\trefs/heads/master\n" >expected &&
+ test_cmp expected actual &&
+ git push pushee-namespaced --tags &&
+ git ls-remote pushee-namespaced >actual &&
+ printf "$commit0\trefs/tags/0\n" >>expected &&
+ printf "$commit1\trefs/tags/1\n" >>expected &&
+ test_cmp expected actual &&
+ # Verify that the GIT_NAMESPACE environment variable works as well
+ GIT_NAMESPACE=namespace git ls-remote "ext::git %s ../pushee" >actual &&
+ test_cmp expected actual &&
+ # Verify that --namespace overrides GIT_NAMESPACE
+ GIT_NAMESPACE=garbage git ls-remote pushee-namespaced >actual &&
+ test_cmp expected actual &&
+ # Try a namespace with no content
+ git ls-remote "ext::git --namespace=garbage %s ../pushee" >actual &&
+ test_cmp /dev/null actual &&
+ git ls-remote pushee-unnamespaced >actual &&
+ sed -e "s|refs/|refs/namespaces/namespace/refs/|" expected >expected.unnamespaced &&
+ test_cmp expected.unnamespaced actual
+ )
+'
+
+test_expect_success 'pulling from a repository using a ref namespace' '
+ (
+ cd puller &&
+ git remote add -f pushee-namespaced "ext::git --namespace=namespace %s ../pushee" &&
+ git for-each-ref refs/ >actual &&
+ printf "$commit1 commit\trefs/remotes/pushee-namespaced/master\n" >expected &&
+ printf "$commit0 commit\trefs/tags/0\n" >>expected &&
+ printf "$commit1 commit\trefs/tags/1\n" >>expected &&
+ test_cmp expected actual
+ )
+'
+
+# This test with clone --mirror checks for possible regressions in clone
+# or the machinery underneath it. It ensures that no future change
+# causes clone to ignore refs in refs/namespaces/*. In particular, it
+# protects against a regression caused by any future change to the refs
+# machinery that might cause it to ignore refs outside of refs/heads/*
+# or refs/tags/*. More generally, this test also checks the high-level
+# functionality of using clone --mirror to back up a set of repos hosted
+# in the namespaces of a single repo.
+test_expect_success 'mirroring a repository using a ref namespace' '
+ git clone --mirror pushee mirror &&
+ (
+ cd mirror &&
+ git for-each-ref refs/ >actual &&
+ printf "$commit1 commit\trefs/namespaces/namespace/refs/heads/master\n" >expected &&
+ printf "$commit0 commit\trefs/namespaces/namespace/refs/tags/0\n" >>expected &&
+ printf "$commit1 commit\trefs/namespaces/namespace/refs/tags/1\n" >>expected &&
+ test_cmp expected actual
+ )
+'
+
+test_expect_success 'hide namespaced refs with transfer.hideRefs' '
+ GIT_NAMESPACE=namespace \
+ git -C pushee -c transfer.hideRefs=refs/tags \
+ ls-remote "ext::git %s ." >actual &&
+ printf "$commit1\trefs/heads/master\n" >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'check that transfer.hideRefs does not match unstripped refs' '
+ GIT_NAMESPACE=namespace \
+ git -C pushee -c transfer.hideRefs=refs/namespaces/namespace/refs/tags \
+ ls-remote "ext::git %s ." >actual &&
+ printf "$commit1\trefs/heads/master\n" >expected &&
+ printf "$commit0\trefs/tags/0\n" >>expected &&
+ printf "$commit1\trefs/tags/1\n" >>expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'hide full refs with transfer.hideRefs' '
+ GIT_NAMESPACE=namespace \
+ git -C pushee -c transfer.hideRefs="^refs/namespaces/namespace/refs/tags" \
+ ls-remote "ext::git %s ." >actual &&
+ printf "$commit1\trefs/heads/master\n" >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'try to update a hidden ref' '
+ test_config -C pushee transfer.hideRefs refs/heads/master &&
+ test_must_fail git -C original push pushee-namespaced master
+'
+
+test_expect_success 'try to update a ref that is not hidden' '
+ test_config -C pushee transfer.hideRefs refs/namespaces/namespace/refs/heads/master &&
+ git -C original push pushee-namespaced master
+'
+
+test_expect_success 'try to update a hidden full ref' '
+ test_config -C pushee transfer.hideRefs "^refs/namespaces/namespace/refs/heads/master" &&
+ test_must_fail git -C original push pushee-namespaced master
+'
+
+test_done
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 4eb10f6..454d896 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -7,13 +7,21 @@ test_description='Per branch config variables affects "git fetch".
. ./test-lib.sh
-D=`pwd`
+D=$(pwd)
test_bundle_object_count () {
git verify-pack -v "$1" >verify.out &&
test "$2" = $(grep '^[0-9a-f]\{40\} ' verify.out | wc -l)
}
+convert_bundle_to_pack () {
+ while read x && test -n "$x"
+ do
+ :;
+ done
+ cat
+}
+
test_expect_success setup '
echo >file original &&
git add file &&
@@ -21,27 +29,30 @@ test_expect_success setup '
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 .. &&
+ (
+ cd one &&
+ echo >file updated by one &&
+ git commit -a -m "updated by one"
+ ) &&
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 .. &&
+ (
+ 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
+ ) &&
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 .. &&
+ (
+ 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
+ ) &&
git clone . bundle &&
git clone . seven
'
@@ -53,8 +64,8 @@ test_expect_success "fetch test" '
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` &&
+ mine=$(git rev-parse refs/heads/one) &&
+ his=$(cd ../one && git rev-parse refs/heads/master) &&
test "z$mine" = "z$his"
'
@@ -64,15 +75,105 @@ test_expect_success "fetch test for-merge" '
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` &&
+ 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 "
+ echo "$master_in_two not-for-merge"
} >expected &&
cut -f -2 .git/FETCH_HEAD >actual &&
test_cmp expected actual'
+test_expect_success 'fetch --prune on its own works as expected' '
+ cd "$D" &&
+ git clone . prune &&
+ cd prune &&
+ git update-ref refs/remotes/origin/extrabranch master &&
+
+ git fetch --prune origin &&
+ test_must_fail git rev-parse origin/extrabranch
+'
+
+test_expect_success 'fetch --prune with a branch name keeps branches' '
+ cd "$D" &&
+ git clone . prune-branch &&
+ cd prune-branch &&
+ git update-ref refs/remotes/origin/extrabranch master &&
+
+ git fetch --prune origin master &&
+ git rev-parse origin/extrabranch
+'
+
+test_expect_success 'fetch --prune with a namespace keeps other namespaces' '
+ cd "$D" &&
+ git clone . prune-namespace &&
+ cd prune-namespace &&
+
+ git fetch --prune origin refs/heads/a/*:refs/remotes/origin/a/* &&
+ git rev-parse origin/master
+'
+
+test_expect_success 'fetch --prune handles overlapping refspecs' '
+ cd "$D" &&
+ git update-ref refs/pull/42/head master &&
+ git clone . prune-overlapping &&
+ cd prune-overlapping &&
+ git config --add remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* &&
+
+ git fetch --prune origin &&
+ git rev-parse origin/master &&
+ git rev-parse origin/pr/42 &&
+
+ git config --unset-all remote.origin.fetch &&
+ git config remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* &&
+ git config --add remote.origin.fetch refs/heads/*:refs/remotes/origin/* &&
+
+ git fetch --prune origin &&
+ git rev-parse origin/master &&
+ git rev-parse origin/pr/42
+'
+
+test_expect_success 'fetch --prune --tags prunes branches but not tags' '
+ cd "$D" &&
+ git clone . prune-tags &&
+ cd prune-tags &&
+ git tag sometag master &&
+ # Create what looks like a remote-tracking branch from an earlier
+ # fetch that has since been deleted from the remote:
+ git update-ref refs/remotes/origin/fake-remote master &&
+
+ git fetch --prune --tags origin &&
+ git rev-parse origin/master &&
+ test_must_fail git rev-parse origin/fake-remote &&
+ git rev-parse sometag
+'
+
+test_expect_success 'fetch --prune --tags with branch does not prune other things' '
+ cd "$D" &&
+ git clone . prune-tags-branch &&
+ cd prune-tags-branch &&
+ git tag sometag master &&
+ git update-ref refs/remotes/origin/extrabranch master &&
+
+ git fetch --prune --tags origin master &&
+ git rev-parse origin/extrabranch &&
+ git rev-parse sometag
+'
+
+test_expect_success 'fetch --prune --tags with refspec prunes based on refspec' '
+ cd "$D" &&
+ git clone . prune-tags-refspec &&
+ cd prune-tags-refspec &&
+ git tag sometag master &&
+ git update-ref refs/remotes/origin/foo/otherbranch master &&
+ git update-ref refs/remotes/origin/extrabranch master &&
+
+ git fetch --prune --tags origin refs/heads/foo/*:refs/remotes/origin/foo/* &&
+ test_must_fail git rev-parse refs/remotes/origin/foo/otherbranch &&
+ git rev-parse origin/extrabranch &&
+ git rev-parse sometag
+'
+
test_expect_success 'fetch tags when there is no tags' '
cd "$D" &&
@@ -101,6 +202,36 @@ test_expect_success 'fetch following tags' '
'
+test_expect_success 'fetch uses remote ref names to describe new refs' '
+ cd "$D" &&
+ git init descriptive &&
+ (
+ cd descriptive &&
+ git config remote.o.url .. &&
+ git config remote.o.fetch "refs/heads/*:refs/crazyheads/*" &&
+ git config --add remote.o.fetch "refs/others/*:refs/heads/*" &&
+ git fetch o
+ ) &&
+ git tag -a -m "Descriptive tag" descriptive-tag &&
+ git branch descriptive-branch &&
+ git checkout descriptive-branch &&
+ echo "Nuts" >crazy &&
+ git add crazy &&
+ git commit -a -m "descriptive commit" &&
+ git update-ref refs/others/crazy HEAD &&
+ (
+ cd descriptive &&
+ git fetch o 2>actual &&
+ grep " -> refs/crazyheads/descriptive-branch$" actual |
+ test_i18ngrep "new branch" &&
+ grep " -> descriptive-tag$" actual |
+ test_i18ngrep "new tag" &&
+ grep " -> crazy$" actual |
+ test_i18ngrep "new ref"
+ ) &&
+ git checkout master
+'
+
test_expect_success 'fetch must not resolve short tag name' '
cd "$D" &&
@@ -113,17 +244,16 @@ test_expect_success 'fetch must not resolve short tag name' '
'
-test_expect_success 'fetch must not resolve short remote name' '
+test_expect_success 'fetch can now resolve short remote name' '
cd "$D" &&
- git update-ref refs/remotes/six/HEAD HEAD
+ git update-ref refs/remotes/six/HEAD HEAD &&
mkdir six &&
cd six &&
git init &&
- test_must_fail git fetch .. six:six
-
+ git fetch .. six:six
'
test_expect_success 'create bundle 1' '
@@ -154,13 +284,7 @@ test_expect_success 'unbundle 1' '
test_expect_success 'bundle 1 has only 3 files ' '
cd "$D" &&
- (
- while read x && test -n "$x"
- do
- :;
- done
- cat
- ) <bundle1 >bundle.pack &&
+ convert_bundle_to_pack <bundle1 >bundle.pack &&
git index-pack bundle.pack &&
test_bundle_object_count bundle.pack 3
'
@@ -177,13 +301,7 @@ test_expect_success 'bundle does not prerequisite objects' '
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 &&
+ convert_bundle_to_pack <bundle3 >bundle.pack &&
git index-pack bundle.pack &&
test_bundle_object_count bundle.pack 3
'
@@ -196,42 +314,6 @@ test_expect_success 'bundle should be able to create a full history' '
'
-! 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 &&
@@ -240,6 +322,38 @@ test_expect_success 'fetch with a non-applying branch.<name>.merge' '
git fetch blub
'
+# URL supplied to fetch does not match the url of the configured branch's remote
+test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [1]' '
+ one_head=$(cd one && git rev-parse HEAD) &&
+ this_head=$(git rev-parse HEAD) &&
+ git update-ref -d FETCH_HEAD &&
+ git fetch one &&
+ test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
+ test $this_head = "$(git rev-parse --verify HEAD)"
+'
+
+# URL supplied to fetch matches the url of the configured branch's remote and
+# the merge spec matches the branch the remote HEAD points to
+test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [2]' '
+ one_ref=$(cd one && git symbolic-ref HEAD) &&
+ git config branch.master.remote blub &&
+ git config branch.master.merge "$one_ref" &&
+ git update-ref -d FETCH_HEAD &&
+ git fetch one &&
+ test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
+ test $this_head = "$(git rev-parse --verify HEAD)"
+'
+
+# URL supplied to fetch matches the url of the configured branch's remote, but
+# the merge spec does not match the branch the remote HEAD points to
+test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [3]' '
+ git config branch.master.merge "${one_ref}_not" &&
+ git update-ref -d FETCH_HEAD &&
+ git fetch one &&
+ test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
+ test $this_head = "$(git rev-parse --verify HEAD)"
+'
+
# 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 &&
@@ -260,29 +374,75 @@ test_expect_success 'bundle should record HEAD correctly' '
'
-test_expect_success 'explicit fetch should not update tracking' '
+test_expect_success 'mark initial state of origin/master' '
+ (
+ cd three &&
+ git tag base-origin-master refs/remotes/origin/master
+ )
+'
+
+test_expect_success 'explicit fetch should update tracking' '
cd "$D" &&
git branch -f side &&
(
cd three &&
+ git update-ref refs/remotes/origin/master base-origin-master &&
o=$(git rev-parse --verify refs/remotes/origin/master) &&
git fetch origin master &&
n=$(git rev-parse --verify refs/remotes/origin/master) &&
- test "$o" = "$n" &&
+ test "$o" != "$n" &&
test_must_fail git rev-parse --verify refs/remotes/origin/side
)
'
-test_expect_success 'explicit pull should not update tracking' '
+test_expect_success 'explicit pull should update tracking' '
cd "$D" &&
git branch -f side &&
(
cd three &&
+ git update-ref refs/remotes/origin/master base-origin-master &&
o=$(git rev-parse --verify refs/remotes/origin/master) &&
git pull origin master &&
n=$(git rev-parse --verify refs/remotes/origin/master) &&
+ test "$o" != "$n" &&
+ test_must_fail git rev-parse --verify refs/remotes/origin/side
+ )
+'
+
+test_expect_success 'explicit --refmap is allowed only with command-line refspec' '
+ cd "$D" &&
+ (
+ cd three &&
+ test_must_fail git fetch --refmap="*:refs/remotes/none/*"
+ )
+'
+
+test_expect_success 'explicit --refmap option overrides remote.*.fetch' '
+ cd "$D" &&
+ git branch -f side &&
+ (
+ cd three &&
+ git update-ref refs/remotes/origin/master base-origin-master &&
+ o=$(git rev-parse --verify refs/remotes/origin/master) &&
+ git fetch --refmap="refs/heads/*:refs/remotes/other/*" 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 &&
+ git rev-parse --verify refs/remotes/other/master
+ )
+'
+
+test_expect_success 'explicitly empty --refmap option disables remote.*.fetch' '
+ cd "$D" &&
+ git branch -f side &&
+ (
+ cd three &&
+ git update-ref refs/remotes/origin/master base-origin-master &&
+ o=$(git rev-parse --verify refs/remotes/origin/master) &&
+ git fetch --refmap="" 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
)
@@ -294,6 +454,7 @@ test_expect_success 'configured fetch updates tracking' '
git branch -f side &&
(
cd three &&
+ git update-ref refs/remotes/origin/master base-origin-master &&
o=$(git rev-parse --verify refs/remotes/origin/master) &&
git fetch origin &&
n=$(git rev-parse --verify refs/remotes/origin/master) &&
@@ -302,6 +463,22 @@ test_expect_success 'configured fetch updates tracking' '
)
'
+test_expect_success 'non-matching refspecs do not confuse tracking update' '
+ cd "$D" &&
+ git update-ref refs/odd/location HEAD &&
+ (
+ cd three &&
+ git update-ref refs/remotes/origin/master base-origin-master &&
+ git config --add remote.origin.fetch \
+ refs/odd/location:refs/remotes/origin/odd &&
+ o=$(git rev-parse --verify refs/remotes/origin/master) &&
+ git fetch origin master &&
+ n=$(git rev-parse --verify refs/remotes/origin/master) &&
+ test "$o" != "$n" &&
+ test_must_fail git rev-parse --verify refs/remotes/origin/odd
+ )
+'
+
test_expect_success 'pushing nonexistent branch by mistake should not segv' '
cd "$D" &&
@@ -349,14 +526,166 @@ test_expect_success 'fetch --dry-run' '
'
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
+ 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
+ )
+'
+
+# configured prune tests
+
+set_config_tristate () {
+ # var=$1 val=$2
+ case "$2" in
+ unset) test_unconfig "$1" ;;
+ *) git config "$1" "$2" ;;
+ esac
+}
+
+test_configured_prune () {
+ fetch_prune=$1 remote_origin_prune=$2 cmdline=$3 expected=$4
+
+ test_expect_success "prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; $4" '
+ # make sure a newbranch is there in . and also in one
+ git branch -f newbranch &&
+ (
+ cd one &&
+ test_unconfig fetch.prune &&
+ test_unconfig remote.origin.prune &&
+ git fetch &&
+ git rev-parse --verify refs/remotes/origin/newbranch
+ ) &&
+
+ # now remove it
+ git branch -d newbranch &&
+
+ # then test
+ (
+ cd one &&
+ set_config_tristate fetch.prune $fetch_prune &&
+ set_config_tristate remote.origin.prune $remote_origin_prune &&
+
+ git fetch $cmdline &&
+ case "$expected" in
+ pruned)
+ test_must_fail git rev-parse --verify refs/remotes/origin/newbranch
+ ;;
+ kept)
+ git rev-parse --verify refs/remotes/origin/newbranch
+ ;;
+ esac
+ )
+ '
+}
+
+test_configured_prune unset unset "" kept
+test_configured_prune unset unset "--no-prune" kept
+test_configured_prune unset unset "--prune" pruned
+
+test_configured_prune false unset "" kept
+test_configured_prune false unset "--no-prune" kept
+test_configured_prune false unset "--prune" pruned
+
+test_configured_prune true unset "" pruned
+test_configured_prune true unset "--prune" pruned
+test_configured_prune true unset "--no-prune" kept
+
+test_configured_prune unset false "" kept
+test_configured_prune unset false "--no-prune" kept
+test_configured_prune unset false "--prune" pruned
+
+test_configured_prune false false "" kept
+test_configured_prune false false "--no-prune" kept
+test_configured_prune false false "--prune" pruned
+
+test_configured_prune true false "" kept
+test_configured_prune true false "--prune" pruned
+test_configured_prune true false "--no-prune" kept
+
+test_configured_prune unset true "" pruned
+test_configured_prune unset true "--no-prune" kept
+test_configured_prune unset true "--prune" pruned
+
+test_configured_prune false true "" pruned
+test_configured_prune false true "--no-prune" kept
+test_configured_prune false true "--prune" pruned
+
+test_configured_prune true true "" pruned
+test_configured_prune true true "--prune" pruned
+test_configured_prune true true "--no-prune" kept
+
+test_expect_success 'all boundary commits are excluded' '
+ test_commit base &&
+ test_commit oneside &&
+ git checkout HEAD^ &&
+ test_commit otherside &&
+ git checkout master &&
+ test_tick &&
+ git merge otherside &&
+ ad=$(git log --no-walk --format=%ad HEAD) &&
+ git bundle create twoside-boundary.bdl master --since="$ad" &&
+ convert_bundle_to_pack <twoside-boundary.bdl >twoside-boundary.pack &&
+ pack=$(git index-pack --fix-thin --stdin <twoside-boundary.pack) &&
+ test_bundle_object_count .git/objects/pack/pack-${pack##pack }.pack 3
+'
+
+test_expect_success 'fetch --prune prints the remotes url' '
+ git branch goodbye &&
+ git clone . only-prunes &&
+ git branch -D goodbye &&
+ (
+ cd only-prunes &&
+ git fetch --prune origin 2>&1 | head -n1 >../actual
+ ) &&
+ echo "From ${D}/." >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'branchname D/F conflict resolved by --prune' '
+ git branch dir/file &&
+ git clone . prune-df-conflict &&
+ git branch -D dir/file &&
+ git branch dir &&
+ (
+ cd prune-df-conflict &&
+ git fetch --prune &&
+ git rev-parse origin/dir >../actual
+ ) &&
+ git rev-parse dir >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fetching a one-level ref works' '
+ test_commit extra &&
+ git reset --hard HEAD^ &&
+ git update-ref refs/foo extra &&
+ git init one-level &&
+ (
+ cd one-level &&
+ git fetch .. HEAD refs/foo
+ )
+'
+
+test_expect_success 'fetching with auto-gc does not lock up' '
+ write_script askyesno <<-\EOF &&
+ echo "$*" &&
+ false
+ EOF
+ git clone "file://$D" auto-gc &&
+ test_commit test2 &&
+ (
+ cd auto-gc &&
+ git config gc.autoPackLimit 1 &&
+ git config gc.autoDetach false &&
+ GIT_ASK_YESNO="$D/askyesno" git fetch >fetch.out 2>&1 &&
+ ! grep "Should I try again" fetch.out
+ )
'
test_done
diff --git a/t/t5511-refspec.sh b/t/t5511-refspec.sh
index c289322..f541f30 100755
--- a/t/t5511-refspec.sh
+++ b/t/t5511-refspec.sh
@@ -5,7 +5,6 @@ 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 &&
@@ -72,16 +71,24 @@ 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/*-blah'
+test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*-blah'
-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_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/*g*/for-linus:refs/remotes/mine/*' invalid
+test_refspec push 'refs/heads/*g*/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/*'
+good=$(printf '\303\204')
+test_refspec fetch "refs/heads/${good}"
+bad=$(printf '\011tab')
+test_refspec fetch "refs/heads/${bad}" invalid
+
test_done
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
index d191235..819b9dd 100755
--- a/t/t5512-ls-remote.sh
+++ b/t/t5512-ls-remote.sh
@@ -5,7 +5,6 @@ test_description='git ls-remote'
. ./test-lib.sh
test_expect_success setup '
-
>file &&
git add file &&
test_tick &&
@@ -18,45 +17,33 @@ test_expect_success setup '
) >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 &&
@@ -65,18 +52,14 @@ test_expect_success 'use "origin" when no remote specified' '
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.
@@ -99,28 +82,130 @@ test_expect_success 'use branch.<name>.remote if possible' '
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
- #
+ cat >exp <<-\EOF &&
+ fatal: '\''refs*master'\'' does not appear to be a git repository
+ fatal: Could not read from remote repository.
+ Please make sure you have the correct access rights
+ and the repository exists.
+ EOF
+ #
+ # Do not expect "git ls-remote <pattern>" to work; ls-remote needs
+ # <remote> if you want to feed <pattern>, just like you cannot say
+ # fetch <branch>.
# We could just as easily have used "master"; the "*" emphasizes its
# role as a pattern.
test_must_fail git ls-remote refs*master >actual 2>&1 &&
test_cmp exp actual
+'
+
+test_expect_success 'die with non-2 for wrong repository even with --exit-code' '
+ {
+ git ls-remote --exit-code ./no-such-repository
+ status=$?
+ } &&
+ test $status != 2 && test $status != 0
+'
+
+test_expect_success 'Report success even when nothing matches' '
+ git ls-remote other.git "refs/nsn/*" >actual &&
+ >expect &&
+ test_cmp expect actual
+'
+test_expect_success 'Report no-match with --exit-code' '
+ test_expect_code 2 git ls-remote --exit-code other.git "refs/nsn/*" >actual &&
+ >expect &&
+ test_cmp expect actual
'
+test_expect_success 'Report match with --exit-code' '
+ git ls-remote --exit-code other.git "refs/tags/*" >actual &&
+ git ls-remote . tags/mark >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'set up some extra tags for ref hiding' '
+ git tag magic/one &&
+ git tag magic/two
+'
+
+for configsection in transfer uploadpack
+do
+ test_expect_success "Hide some refs with $configsection.hiderefs" '
+ test_config $configsection.hiderefs refs/tags &&
+ git ls-remote . >actual &&
+ test_unconfig $configsection.hiderefs &&
+ git ls-remote . |
+ sed -e "/ refs\/tags\//d" >expect &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "Override hiding of $configsection.hiderefs" '
+ test_when_finished "test_unconfig $configsection.hiderefs" &&
+ git config --add $configsection.hiderefs refs/tags &&
+ git config --add $configsection.hiderefs "!refs/tags/magic" &&
+ git config --add $configsection.hiderefs refs/tags/magic/one &&
+ git ls-remote . >actual &&
+ grep refs/tags/magic/two actual &&
+ ! grep refs/tags/magic/one actual
+ '
+
+done
+
+test_expect_success 'overrides work between mixed transfer/upload-pack hideRefs' '
+ test_config uploadpack.hiderefs refs/tags &&
+ test_config transfer.hiderefs "!refs/tags/magic" &&
+ git ls-remote . >actual &&
+ grep refs/tags/magic actual
+'
+
+test_expect_success 'ls-remote --symref' '
+ cat >expect <<-\EOF &&
+ ref: refs/heads/master HEAD
+ 1bd44cb9d13204b0fe1958db0082f5028a16eb3a HEAD
+ 1bd44cb9d13204b0fe1958db0082f5028a16eb3a refs/heads/master
+ 1bd44cb9d13204b0fe1958db0082f5028a16eb3a refs/remotes/origin/HEAD
+ 1bd44cb9d13204b0fe1958db0082f5028a16eb3a refs/remotes/origin/master
+ 1bd44cb9d13204b0fe1958db0082f5028a16eb3a refs/tags/mark
+ EOF
+ git ls-remote --symref >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'ls-remote with filtered symref (refname)' '
+ cat >expect <<-\EOF &&
+ ref: refs/heads/master HEAD
+ 1bd44cb9d13204b0fe1958db0082f5028a16eb3a HEAD
+ EOF
+ git ls-remote --symref . HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'ls-remote with filtered symref (--heads)' '
+ git symbolic-ref refs/heads/foo refs/tags/mark &&
+ cat >expect <<-\EOF &&
+ ref: refs/tags/mark refs/heads/foo
+ 1bd44cb9d13204b0fe1958db0082f5028a16eb3a refs/heads/foo
+ 1bd44cb9d13204b0fe1958db0082f5028a16eb3a refs/heads/master
+ EOF
+ git ls-remote --symref --heads . >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'ls-remote --symref omits filtered-out matches' '
+ cat >expect <<-\EOF &&
+ 1bd44cb9d13204b0fe1958db0082f5028a16eb3a refs/heads/foo
+ 1bd44cb9d13204b0fe1958db0082f5028a16eb3a refs/heads/master
+ EOF
+ git ls-remote --symref --heads . >actual &&
+ test_cmp expect actual &&
+ git ls-remote --symref . "refs/heads/*" >actual &&
+ test_cmp expect actual
+'
+
+
test_done
diff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh
index 9e74862..65d1e05 100755
--- a/t/t5513-fetch-track.sh
+++ b/t/t5513-fetch-track.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-test_description='fetch follows remote tracking branches correctly'
+test_description='fetch follows remote-tracking branches correctly'
. ./test-lib.sh
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh
index b737332..4b4b667 100755
--- a/t/t5514-fetch-multiple.sh
+++ b/t/t5514-fetch-multiple.sh
@@ -27,7 +27,7 @@ test_expect_success setup '
(
cd two && git branch another
) &&
- git clone --mirror two three
+ git clone --mirror two three &&
git clone one test
'
@@ -120,7 +120,7 @@ test_expect_success 'git fetch --all (skipFetchAll)' '
(cd test4 &&
for b in $(git branch -r)
do
- git branch -r -d $b || break
+ git branch -r -d $b || exit 1
done &&
git remote add three ../three &&
git config remote.three.skipFetchAll true &&
@@ -144,11 +144,41 @@ test_expect_success 'git fetch --multiple (ignoring skipFetchAll)' '
(cd test4 &&
for b in $(git branch -r)
do
- git branch -r -d $b || break
+ git branch -r -d $b || exit 1
done &&
git fetch --multiple one two three &&
git branch -r > output &&
test_cmp ../expect output)
'
+test_expect_success 'git fetch --all --no-tags' '
+ >expect &&
+ git clone one test5 &&
+ git clone test5 test6 &&
+ (cd test5 && git tag test-tag) &&
+ (
+ cd test6 &&
+ git fetch --all --no-tags &&
+ git tag >output
+ ) &&
+ test_cmp expect test6/output
+'
+
+test_expect_success 'git fetch --all --tags' '
+ echo test-tag >expect &&
+ git clone one test7 &&
+ git clone test7 test8 &&
+ (
+ cd test7 &&
+ test_commit test-tag &&
+ git reset --hard HEAD^
+ ) &&
+ (
+ cd test8 &&
+ git fetch --all --tags &&
+ git tag >output
+ ) &&
+ test_cmp expect test8/output
+'
+
test_done
diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
index dbb927d..36b0dbc 100755
--- a/t/t5515-fetch-merge-logic.sh
+++ b/t/t5515-fetch-merge-logic.sh
@@ -128,8 +128,8 @@ do
case "$cmd" in
'' | '#'*) continue ;;
esac
- test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'`
- pfx=`printf "%04d" $test_count`
+ 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"
diff --git a/t/t5515/fetch.br-branches-default b/t/t5515/fetch.br-branches-default
index 2e0414f..a1bc3d5 100644
--- a/t/t5515/fetch.br-branches-default
+++ b/t/t5515/fetch.br-branches-default
@@ -1,8 +1,8 @@
# br-branches-default
754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index ca2cc1d..12ab08e 100644
--- a/t/t5515/fetch.br-branches-default-merge
+++ b/t/t5515/fetch.br-branches-default-merge
@@ -1,9 +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 ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 7d947cd..5442752 100644
--- a/t/t5515/fetch.br-branches-default-merge_branches-default
+++ b/t/t5515/fetch.br-branches-default-merge_branches-default
@@ -1,9 +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 ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index ec39c54..498a761 100644
--- a/t/t5515/fetch.br-branches-default-octopus
+++ b/t/t5515/fetch.br-branches-default-octopus
@@ -1,10 +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 ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 6bf42e2..0857f13 100644
--- a/t/t5515/fetch.br-branches-default-octopus_branches-default
+++ b/t/t5515/fetch.br-branches-default-octopus_branches-default
@@ -1,10 +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 ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 4a2bf3c..8cbd718 100644
--- a/t/t5515/fetch.br-branches-default_branches-default
+++ b/t/t5515/fetch.br-branches-default_branches-default
@@ -1,8 +1,8 @@
# br-branches-default branches-default
754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one b/t/t5515/fetch.br-branches-one
index 12ac8d2..c98f670 100644
--- a/t/t5515/fetch.br-branches-one
+++ b/t/t5515/fetch.br-branches-one
@@ -1,8 +1,8 @@
# br-branches-one
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index b4b3b35..54a7742 100644
--- a/t/t5515/fetch.br-branches-one-merge
+++ b/t/t5515/fetch.br-branches-one-merge
@@ -1,9 +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 branch 'one' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 2ecef38..b4d1bb0 100644
--- a/t/t5515/fetch.br-branches-one-merge_branches-one
+++ b/t/t5515/fetch.br-branches-one-merge_branches-one
@@ -1,9 +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 branch 'one' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 96e3029..97c4b54 100644
--- a/t/t5515/fetch.br-branches-one-octopus
+++ b/t/t5515/fetch.br-branches-one-octopus
@@ -1,9 +1,9 @@
# br-branches-one-octopus
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 55e0bad..df705f7 100644
--- a/t/t5515/fetch.br-branches-one-octopus_branches-one
+++ b/t/t5515/fetch.br-branches-one-octopus_branches-one
@@ -1,9 +1,9 @@
# br-branches-one-octopus branches-one
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 281fa09..96890e5 100644
--- a/t/t5515/fetch.br-branches-one_branches-one
+++ b/t/t5515/fetch.br-branches-one_branches-one
@@ -1,8 +1,8 @@
# br-branches-one branches-one
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit b/t/t5515/fetch.br-config-explicit
index e2fa9c8..68fc927 100644
--- a/t/t5515/fetch.br-config-explicit
+++ b/t/t5515/fetch.br-config-explicit
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index ec1a723..5ce764a 100644
--- a/t/t5515/fetch.br-config-explicit-merge
+++ b/t/t5515/fetch.br-config-explicit-merge
@@ -1,11 +1,11 @@
# br-config-explicit-merge
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 54f6891..b1152b7 100644
--- a/t/t5515/fetch.br-config-explicit-merge_config-explicit
+++ b/t/t5515/fetch.br-config-explicit-merge_config-explicit
@@ -1,11 +1,11 @@
# br-config-explicit-merge config-explicit
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 7011dfc..110577b 100644
--- a/t/t5515/fetch.br-config-explicit-octopus
+++ b/t/t5515/fetch.br-config-explicit-octopus
@@ -1,11 +1,11 @@
# br-config-explicit-octopus
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index bdad51f..a29dd8b 100644
--- a/t/t5515/fetch.br-config-explicit-octopus_config-explicit
+++ b/t/t5515/fetch.br-config-explicit-octopus_config-explicit
@@ -1,11 +1,11 @@
# br-config-explicit-octopus config-explicit
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 1b237dd..b19b016 100644
--- a/t/t5515/fetch.br-config-explicit_config-explicit
+++ b/t/t5515/fetch.br-config-explicit_config-explicit
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob b/t/t5515/fetch.br-config-glob
index e75ec2f..946d70c 100644
--- a/t/t5515/fetch.br-config-glob
+++ b/t/t5515/fetch.br-config-glob
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index ce8f739..89f2596 100644
--- a/t/t5515/fetch.br-config-glob-merge
+++ b/t/t5515/fetch.br-config-glob-merge
@@ -1,11 +1,11 @@
# br-config-glob-merge
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 5817bed..2ba4832 100644
--- a/t/t5515/fetch.br-config-glob-merge_config-glob
+++ b/t/t5515/fetch.br-config-glob-merge_config-glob
@@ -1,11 +1,11 @@
# br-config-glob-merge config-glob
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 938e532..64994df 100644
--- a/t/t5515/fetch.br-config-glob-octopus
+++ b/t/t5515/fetch.br-config-glob-octopus
@@ -1,11 +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 ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index c9225bf..681a725 100644
--- a/t/t5515/fetch.br-config-glob-octopus_config-glob
+++ b/t/t5515/fetch.br-config-glob-octopus_config-glob
@@ -1,11 +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 ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index a6c20f9..19daf0c 100644
--- a/t/t5515/fetch.br-config-glob_config-glob
+++ b/t/t5515/fetch.br-config-glob_config-glob
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit b/t/t5515/fetch.br-remote-explicit
index 83534d2..ab44bc5 100644
--- a/t/t5515/fetch.br-remote-explicit
+++ b/t/t5515/fetch.br-remote-explicit
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index a9064dd..d018b35 100644
--- a/t/t5515/fetch.br-remote-explicit-merge
+++ b/t/t5515/fetch.br-remote-explicit-merge
@@ -1,11 +1,11 @@
# br-remote-explicit-merge
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 732a37e..0d3d780 100644
--- a/t/t5515/fetch.br-remote-explicit-merge_remote-explicit
+++ b/t/t5515/fetch.br-remote-explicit-merge_remote-explicit
@@ -1,11 +1,11 @@
# br-remote-explicit-merge remote-explicit
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index ecf020d..6f84304 100644
--- a/t/t5515/fetch.br-remote-explicit-octopus
+++ b/t/t5515/fetch.br-remote-explicit-octopus
@@ -1,11 +1,11 @@
# br-remote-explicit-octopus
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index af77531..3546a83 100644
--- a/t/t5515/fetch.br-remote-explicit-octopus_remote-explicit
+++ b/t/t5515/fetch.br-remote-explicit-octopus_remote-explicit
@@ -1,11 +1,11 @@
# br-remote-explicit-octopus remote-explicit
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 51fae56..01e014e 100644
--- a/t/t5515/fetch.br-remote-explicit_remote-explicit
+++ b/t/t5515/fetch.br-remote-explicit_remote-explicit
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob b/t/t5515/fetch.br-remote-glob
index 94e6ad3..09bfcee 100644
--- a/t/t5515/fetch.br-remote-glob
+++ b/t/t5515/fetch.br-remote-glob
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 09362e2..7e1a433 100644
--- a/t/t5515/fetch.br-remote-glob-merge
+++ b/t/t5515/fetch.br-remote-glob-merge
@@ -1,11 +1,11 @@
# br-remote-glob-merge
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index e2eabec..53571bb 100644
--- a/t/t5515/fetch.br-remote-glob-merge_remote-glob
+++ b/t/t5515/fetch.br-remote-glob-merge_remote-glob
@@ -1,11 +1,11 @@
# br-remote-glob-merge remote-glob
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index b08e046..c7c8b6d 100644
--- a/t/t5515/fetch.br-remote-glob-octopus
+++ b/t/t5515/fetch.br-remote-glob-octopus
@@ -1,11 +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 ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index d4d547c..36076fb 100644
--- a/t/t5515/fetch.br-remote-glob-octopus_remote-glob
+++ b/t/t5515/fetch.br-remote-glob-octopus_remote-glob
@@ -1,11 +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 ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 646dbc8..20ba5cb 100644
--- a/t/t5515/fetch.br-remote-glob_remote-glob
+++ b/t/t5515/fetch.br-remote-glob_remote-glob
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig b/t/t5515/fetch.br-unconfig
index 65ce6d9..887ccfc 100644
--- a/t/t5515/fetch.br-unconfig
+++ b/t/t5515/fetch.br-unconfig
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 8258c80..0f70f66 100644
--- a/t/t5515/fetch.br-unconfig_--tags_.._.git
+++ b/t/t5515/fetch.br-unconfig_--tags_.._.git
@@ -1,7 +1,8 @@
# br-unconfig --tags ../.git
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' 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
index f02bab2..7411536 100644
--- 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
@@ -2,7 +2,7 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-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
index 85de411..7726983 100644
--- 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
@@ -1,7 +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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 0da2337..7b3750c 100644
--- 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
@@ -1,7 +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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 tag 'tag-three' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index fc7041e..da30e3c 100644
--- a/t/t5515/fetch.br-unconfig_branches-default
+++ b/t/t5515/fetch.br-unconfig_branches-default
@@ -1,8 +1,8 @@
# br-unconfig branches-default
754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index e94cde7..e461431 100644
--- a/t/t5515/fetch.br-unconfig_branches-one
+++ b/t/t5515/fetch.br-unconfig_branches-one
@@ -1,8 +1,8 @@
# br-unconfig branches-one
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 01a283e..ed323c9 100644
--- a/t/t5515/fetch.br-unconfig_config-explicit
+++ b/t/t5515/fetch.br-unconfig_config-explicit
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 3a556c5..2372ed0 100644
--- a/t/t5515/fetch.br-unconfig_config-glob
+++ b/t/t5515/fetch.br-unconfig_config-glob
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index db216df..6318dd1 100644
--- a/t/t5515/fetch.br-unconfig_remote-explicit
+++ b/t/t5515/fetch.br-unconfig_remote-explicit
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index aee65c2..1d9afad 100644
--- a/t/t5515/fetch.br-unconfig_remote-glob
+++ b/t/t5515/fetch.br-unconfig_remote-glob
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master b/t/t5515/fetch.master
index 950fd07..9b29d67 100644
--- a/t/t5515/fetch.master
+++ b/t/t5515/fetch.master
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_--tags_.._.git b/t/t5515/fetch.master_--tags_.._.git
index 0e59950..ab473a6 100644
--- a/t/t5515/fetch.master_--tags_.._.git
+++ b/t/t5515/fetch.master_--tags_.._.git
@@ -1,7 +1,8 @@
# master --tags ../.git
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' 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
index 8286852..0672d12 100644
--- 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
@@ -2,7 +2,7 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-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
index 2e133ef..0fd737c 100644
--- 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
@@ -1,7 +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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba 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
index 92b18b4..e488986 100644
--- a/t/t5515/fetch.master_.._.git_tag_tag-one_tag_tag-three
+++ b/t/t5515/fetch.master_.._.git_tag_tag-one_tag_tag-three
@@ -1,7 +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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 tag 'tag-three' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_branches-default b/t/t5515/fetch.master_branches-default
index 603d6d2..2eedd3b 100644
--- a/t/t5515/fetch.master_branches-default
+++ b/t/t5515/fetch.master_branches-default
@@ -1,8 +1,8 @@
# master branches-default
754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_branches-one b/t/t5515/fetch.master_branches-one
index fe9bb0b..901ce21 100644
--- a/t/t5515/fetch.master_branches-one
+++ b/t/t5515/fetch.master_branches-one
@@ -1,8 +1,8 @@
# master branches-one
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_config-explicit b/t/t5515/fetch.master_config-explicit
index 4be97c7..251c826 100644
--- a/t/t5515/fetch.master_config-explicit
+++ b/t/t5515/fetch.master_config-explicit
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_config-glob b/t/t5515/fetch.master_config-glob
index cb0726f..27c158e 100644
--- a/t/t5515/fetch.master_config-glob
+++ b/t/t5515/fetch.master_config-glob
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_remote-explicit b/t/t5515/fetch.master_remote-explicit
index 44a1ca8..b3cfe6b 100644
--- a/t/t5515/fetch.master_remote-explicit
+++ b/t/t5515/fetch.master_remote-explicit
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_remote-glob b/t/t5515/fetch.master_remote-glob
index 724e8db..118befd 100644
--- a/t/t5515/fetch.master_remote-glob
+++ b/t/t5515/fetch.master_remote-glob
@@ -3,9 +3,9 @@
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 ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 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 ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index b11da79..26b2caf 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -1,16 +1,29 @@
#!/bin/sh
-test_description='fetching and pushing, with or without wildcard'
+test_description='Basic fetch/push functionality.
+
+This test checks the following functionality:
+
+* command-line syntax
+* refspecs
+* fast-forward detection, and overriding it
+* configuration
+* hooks
+* --porcelain output format
+* hiderefs
+* reflogs
+'
. ./test-lib.sh
-D=`pwd`
+D=$(pwd)
mk_empty () {
- rm -fr testrepo &&
- mkdir testrepo &&
+ repo_name="$1"
+ rm -fr "$repo_name" &&
+ mkdir "$repo_name" &&
(
- cd testrepo &&
+ cd "$repo_name" &&
git init &&
git config receive.denyCurrentBranch warn &&
mv .git/hooks .git/hooks-disabled
@@ -18,45 +31,81 @@ mk_empty () {
}
mk_test () {
- mk_empty &&
+ repo_name="$1"
+ shift
+
+ mk_empty "$repo_name" &&
(
for ref in "$@"
do
- git push testrepo $the_first_commit:refs/$ref || {
- echo "Oops, push refs/$ref failure"
- exit 1
- }
+ git push "$repo_name" $the_first_commit:refs/$ref ||
+ exit
done &&
- cd testrepo &&
+ cd "$repo_name" &&
for ref in "$@"
do
- r=$(git show-ref -s --verify refs/$ref) &&
- test "z$r" = "z$the_first_commit" || {
- echo "Oops, refs/$ref is wrong"
- exit 1
- }
+ echo "$the_first_commit" >expect &&
+ git show-ref -s --verify refs/$ref >actual &&
+ test_cmp expect actual ||
+ exit
done &&
git fsck --full
)
}
+mk_test_with_hooks() {
+ repo_name=$1
+ mk_test "$@" &&
+ (
+ cd "$repo_name" &&
+ mkdir .git/hooks &&
+ cd .git/hooks &&
+
+ cat >pre-receive <<-'EOF' &&
+ #!/bin/sh
+ cat - >>pre-receive.actual
+ EOF
+
+ cat >update <<-'EOF' &&
+ #!/bin/sh
+ printf "%s %s %s\n" "$@" >>update.actual
+ EOF
+
+ cat >post-receive <<-'EOF' &&
+ #!/bin/sh
+ cat - >>post-receive.actual
+ EOF
+
+ cat >post-update <<-'EOF' &&
+ #!/bin/sh
+ for ref in "$@"
+ do
+ printf "%s\n" "$ref" >>post-update.actual
+ done
+ EOF
+
+ chmod +x pre-receive update post-receive post-update
+ )
+}
+
mk_child() {
- rm -rf "$1" &&
- git clone testrepo "$1"
+ rm -rf "$2" &&
+ git clone "$1" "$2"
}
check_push_result () {
+ repo_name="$1"
+ shift
+
(
- cd testrepo &&
- it="$1" &&
- shift
+ cd "$repo_name" &&
+ echo "$1" >expect &&
+ shift &&
for ref in "$@"
do
- r=$(git show-ref -s --verify refs/$ref) &&
- test "z$r" = "z$it" || {
- echo "Oops, refs/$ref is wrong"
- exit 1
- }
+ git show-ref -s --verify refs/$ref >actual &&
+ test_cmp expect actual ||
+ exit
done &&
git fsck --full
)
@@ -79,35 +128,33 @@ test_expect_success setup '
'
test_expect_success 'fetch without wildcard' '
- mk_empty &&
+ mk_empty testrepo &&
(
cd testrepo &&
git fetch .. refs/heads/master:refs/remotes/origin/master &&
- r=$(git show-ref -s --verify refs/remotes/origin/master) &&
- test "z$r" = "z$the_commit" &&
-
- test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ echo "$the_commit commit refs/remotes/origin/master" >expect &&
+ git for-each-ref refs/remotes/origin >actual &&
+ test_cmp expect actual
)
'
test_expect_success 'fetch with wildcard' '
- mk_empty &&
+ mk_empty testrepo &&
(
cd testrepo &&
git config remote.up.url .. &&
git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
git fetch up &&
- r=$(git show-ref -s --verify refs/remotes/origin/master) &&
- test "z$r" = "z$the_commit" &&
-
- test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ echo "$the_commit commit refs/remotes/origin/master" >expect &&
+ git for-each-ref refs/remotes/origin >actual &&
+ test_cmp expect actual
)
'
test_expect_success 'fetch with insteadOf' '
- mk_empty &&
+ mk_empty testrepo &&
(
TRASH=$(pwd)/ &&
cd testrepo &&
@@ -116,15 +163,14 @@ test_expect_success 'fetch with insteadOf' '
git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
git fetch up &&
- r=$(git show-ref -s --verify refs/remotes/origin/master) &&
- test "z$r" = "z$the_commit" &&
-
- test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ echo "$the_commit commit refs/remotes/origin/master" >expect &&
+ git for-each-ref refs/remotes/origin >actual &&
+ test_cmp expect actual
)
'
test_expect_success 'fetch with pushInsteadOf (should not rewrite)' '
- mk_empty &&
+ mk_empty testrepo &&
(
TRASH=$(pwd)/ &&
cd testrepo &&
@@ -133,321 +179,310 @@ test_expect_success 'fetch with pushInsteadOf (should not rewrite)' '
git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
git fetch up &&
- r=$(git show-ref -s --verify refs/remotes/origin/master) &&
- test "z$r" = "z$the_commit" &&
-
- test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ echo "$the_commit commit refs/remotes/origin/master" >expect &&
+ git for-each-ref refs/remotes/origin >actual &&
+ test_cmp expect actual
)
'
test_expect_success 'push without wildcard' '
- mk_empty &&
+ mk_empty testrepo &&
git push testrepo refs/heads/master:refs/remotes/origin/master &&
(
cd testrepo &&
- r=$(git show-ref -s --verify refs/remotes/origin/master) &&
- test "z$r" = "z$the_commit" &&
-
- test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ echo "$the_commit commit refs/remotes/origin/master" >expect &&
+ git for-each-ref refs/remotes/origin >actual &&
+ test_cmp expect actual
)
'
test_expect_success 'push with wildcard' '
- mk_empty &&
+ mk_empty testrepo &&
git push testrepo "refs/heads/*:refs/remotes/origin/*" &&
(
cd testrepo &&
- r=$(git show-ref -s --verify refs/remotes/origin/master) &&
- test "z$r" = "z$the_commit" &&
-
- test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ echo "$the_commit commit refs/remotes/origin/master" >expect &&
+ git for-each-ref refs/remotes/origin >actual &&
+ test_cmp expect actual
)
'
test_expect_success 'push with insteadOf' '
- mk_empty &&
+ mk_empty testrepo &&
TRASH="$(pwd)/" &&
- git config "url.$TRASH.insteadOf" trash/ &&
+ test_config "url.$TRASH.insteadOf" trash/ &&
git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
(
cd testrepo &&
- r=$(git show-ref -s --verify refs/remotes/origin/master) &&
- test "z$r" = "z$the_commit" &&
-
- test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ echo "$the_commit commit refs/remotes/origin/master" >expect &&
+ git for-each-ref refs/remotes/origin >actual &&
+ test_cmp expect actual
)
'
test_expect_success 'push with pushInsteadOf' '
- mk_empty &&
+ mk_empty testrepo &&
TRASH="$(pwd)/" &&
- git config "url.$TRASH.pushInsteadOf" trash/ &&
+ test_config "url.$TRASH.pushInsteadOf" trash/ &&
git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
(
cd testrepo &&
- r=$(git show-ref -s --verify refs/remotes/origin/master) &&
- test "z$r" = "z$the_commit" &&
-
- test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ echo "$the_commit commit refs/remotes/origin/master" >expect &&
+ git for-each-ref refs/remotes/origin >actual &&
+ test_cmp expect actual
)
'
test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf should not rewrite)' '
- mk_empty &&
- TRASH="$(pwd)/" &&
- git config "url.trash2/.pushInsteadOf" trash/ &&
- git config remote.r.url trash/wrong &&
- git config remote.r.pushurl "$TRASH/testrepo" &&
+ mk_empty testrepo &&
+ test_config "url.trash2/.pushInsteadOf" testrepo/ &&
+ test_config "url.trash3/.pushInsteadOf" trash/wrong &&
+ test_config remote.r.url trash/wrong &&
+ test_config remote.r.pushurl "testrepo/" &&
git push r refs/heads/master:refs/remotes/origin/master &&
(
cd testrepo &&
- r=$(git show-ref -s --verify refs/remotes/origin/master) &&
- test "z$r" = "z$the_commit" &&
-
- test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ echo "$the_commit commit refs/remotes/origin/master" >expect &&
+ git for-each-ref refs/remotes/origin >actual &&
+ test_cmp expect actual
)
'
test_expect_success 'push with matching heads' '
- mk_test heads/master &&
- git push testrepo &&
- check_push_result $the_commit heads/master
+ mk_test testrepo heads/master &&
+ git push testrepo : &&
+ check_push_result testrepo $the_commit heads/master
'
test_expect_success 'push with matching heads on the command line' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git push testrepo : &&
- check_push_result $the_commit heads/master
+ check_push_result testrepo $the_commit heads/master
'
test_expect_success 'failed (non-fast-forward) push with matching heads' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git push testrepo : &&
git commit --amend -massaged &&
test_must_fail git push testrepo &&
- check_push_result $the_commit heads/master &&
+ check_push_result testrepo $the_commit heads/master &&
git reset --hard $the_commit
'
test_expect_success 'push --force with matching heads' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git push testrepo : &&
git commit --amend -massaged &&
- git push --force testrepo &&
- ! check_push_result $the_commit heads/master &&
+ git push --force testrepo : &&
+ ! check_push_result testrepo $the_commit heads/master &&
git reset --hard $the_commit
'
test_expect_success 'push with matching heads and forced update' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git push testrepo : &&
git commit --amend -massaged &&
git push testrepo +: &&
- ! check_push_result $the_commit heads/master &&
+ ! check_push_result testrepo $the_commit heads/master &&
git reset --hard $the_commit
'
test_expect_success 'push with no ambiguity (1)' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git push testrepo master:master &&
- check_push_result $the_commit heads/master
+ check_push_result testrepo $the_commit heads/master
'
test_expect_success 'push with no ambiguity (2)' '
- mk_test remotes/origin/master &&
+ mk_test testrepo remotes/origin/master &&
git push testrepo master:origin/master &&
- check_push_result $the_commit remotes/origin/master
+ check_push_result testrepo $the_commit remotes/origin/master
'
test_expect_success 'push with colon-less refspec, no ambiguity' '
- mk_test heads/master heads/t/master &&
+ mk_test testrepo heads/master heads/t/master &&
git branch -f t/master master &&
git push testrepo master &&
- check_push_result $the_commit heads/master &&
- check_push_result $the_first_commit heads/t/master
+ check_push_result testrepo $the_commit heads/master &&
+ check_push_result testrepo $the_first_commit heads/t/master
'
test_expect_success 'push with weak ambiguity (1)' '
- mk_test heads/master remotes/origin/master &&
+ mk_test testrepo heads/master remotes/origin/master &&
git push testrepo master:master &&
- check_push_result $the_commit heads/master &&
- check_push_result $the_first_commit remotes/origin/master
+ check_push_result testrepo $the_commit heads/master &&
+ check_push_result testrepo $the_first_commit remotes/origin/master
'
test_expect_success 'push with weak ambiguity (2)' '
- mk_test heads/master remotes/origin/master remotes/another/master &&
+ mk_test testrepo heads/master remotes/origin/master remotes/another/master &&
git push testrepo master:master &&
- check_push_result $the_commit heads/master &&
- check_push_result $the_first_commit remotes/origin/master remotes/another/master
+ check_push_result testrepo $the_commit heads/master &&
+ check_push_result testrepo $the_first_commit remotes/origin/master remotes/another/master
'
test_expect_success 'push with ambiguity' '
- mk_test heads/frotz tags/frotz &&
- if git push testrepo master:frotz
- then
- echo "Oops, should have failed"
- false
- else
- check_push_result $the_first_commit heads/frotz tags/frotz
- fi
+ mk_test testrepo heads/frotz tags/frotz &&
+ test_must_fail git push testrepo master:frotz &&
+ check_push_result testrepo $the_first_commit heads/frotz tags/frotz
'
test_expect_success 'push with colon-less refspec (1)' '
- mk_test heads/frotz tags/frotz &&
+ mk_test testrepo heads/frotz tags/frotz &&
git branch -f frotz master &&
git push testrepo frotz &&
- check_push_result $the_commit heads/frotz &&
- check_push_result $the_first_commit tags/frotz
+ check_push_result testrepo $the_commit heads/frotz &&
+ check_push_result testrepo $the_first_commit tags/frotz
'
test_expect_success 'push with colon-less refspec (2)' '
- mk_test heads/frotz tags/frotz &&
+ mk_test testrepo heads/frotz tags/frotz &&
if git show-ref --verify -q refs/heads/frotz
then
git branch -D frotz
fi &&
git tag -f frotz &&
- git push testrepo frotz &&
- check_push_result $the_commit tags/frotz &&
- check_push_result $the_first_commit heads/frotz
+ git push -f testrepo frotz &&
+ check_push_result testrepo $the_commit tags/frotz &&
+ check_push_result testrepo $the_first_commit heads/frotz
'
test_expect_success 'push with colon-less refspec (3)' '
- mk_test &&
+ mk_test testrepo &&
if git show-ref --verify -q refs/tags/frotz
then
git tag -d frotz
fi &&
git branch -f frotz master &&
git push testrepo frotz &&
- check_push_result $the_commit heads/frotz &&
+ check_push_result testrepo $the_commit heads/frotz &&
test 1 = $( cd testrepo && git show-ref | wc -l )
'
test_expect_success 'push with colon-less refspec (4)' '
- mk_test &&
+ mk_test testrepo &&
if git show-ref --verify -q refs/heads/frotz
then
git branch -D frotz
fi &&
git tag -f frotz &&
git push testrepo frotz &&
- check_push_result $the_commit tags/frotz &&
+ check_push_result testrepo $the_commit tags/frotz &&
test 1 = $( cd testrepo && git show-ref | wc -l )
'
-test_expect_success 'push head with non-existant, incomplete dest' '
+test_expect_success 'push head with non-existent, incomplete dest' '
- mk_test &&
+ mk_test testrepo &&
git push testrepo master:branch &&
- check_push_result $the_commit heads/branch
+ check_push_result testrepo $the_commit heads/branch
'
-test_expect_success 'push tag with non-existant, incomplete dest' '
+test_expect_success 'push tag with non-existent, incomplete dest' '
- mk_test &&
+ mk_test testrepo &&
git tag -f v1.0 &&
git push testrepo v1.0:tag &&
- check_push_result $the_commit tags/tag
+ check_push_result testrepo $the_commit tags/tag
'
-test_expect_success 'push sha1 with non-existant, incomplete dest' '
+test_expect_success 'push sha1 with non-existent, incomplete dest' '
- mk_test &&
- test_must_fail git push testrepo `git rev-parse master`:foo
+ mk_test testrepo &&
+ test_must_fail git push testrepo $(git rev-parse master):foo
'
-test_expect_success 'push ref expression with non-existant, incomplete dest' '
+test_expect_success 'push ref expression with non-existent, incomplete dest' '
- mk_test &&
+ mk_test testrepo &&
test_must_fail git push testrepo master^:branch
'
test_expect_success 'push with HEAD' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git checkout master &&
git push testrepo HEAD &&
- check_push_result $the_commit heads/master
+ check_push_result testrepo $the_commit heads/master
'
test_expect_success 'push with HEAD nonexisting at remote' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git checkout -b local master &&
git push testrepo HEAD &&
- check_push_result $the_commit heads/local
+ check_push_result testrepo $the_commit heads/local
'
test_expect_success 'push with +HEAD' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git checkout master &&
git branch -D local &&
git checkout -b local &&
git push testrepo master local &&
- check_push_result $the_commit heads/master &&
- check_push_result $the_commit heads/local &&
+ check_push_result testrepo $the_commit heads/master &&
+ check_push_result testrepo $the_commit heads/local &&
# Without force rewinding should fail
git reset --hard HEAD^ &&
test_must_fail git push testrepo HEAD &&
- check_push_result $the_commit heads/local &&
+ check_push_result testrepo $the_commit heads/local &&
# With force rewinding should succeed
git push testrepo +HEAD &&
- check_push_result $the_first_commit heads/local
+ check_push_result testrepo $the_first_commit heads/local
'
-test_expect_success 'push HEAD with non-existant, incomplete dest' '
+test_expect_success 'push HEAD with non-existent, incomplete dest' '
- mk_test &&
+ mk_test testrepo &&
git checkout master &&
git push testrepo HEAD:branch &&
- check_push_result $the_commit heads/branch
+ check_push_result testrepo $the_commit heads/branch
'
test_expect_success 'push with config remote.*.push = HEAD' '
- mk_test heads/local &&
+ mk_test testrepo heads/local &&
git checkout master &&
git branch -f local $the_commit &&
(
@@ -455,46 +490,81 @@ test_expect_success 'push with config remote.*.push = HEAD' '
git checkout local &&
git reset --hard $the_first_commit
) &&
- git config remote.there.url testrepo &&
- git config remote.there.push HEAD &&
- git config branch.master.remote there &&
+ test_config remote.there.url testrepo &&
+ test_config remote.there.push HEAD &&
+ test_config branch.master.remote there &&
git push &&
- check_push_result $the_commit heads/master &&
- check_push_result $the_first_commit heads/local
+ check_push_result testrepo $the_commit heads/master &&
+ check_push_result testrepo $the_first_commit heads/local
'
-# 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 remote.pushdefault' '
+ mk_test up_repo heads/master &&
+ mk_test down_repo heads/master &&
+ test_config remote.up.url up_repo &&
+ test_config remote.down.url down_repo &&
+ test_config branch.master.remote up &&
+ test_config remote.pushdefault down &&
+ test_config push.default matching &&
+ git push &&
+ check_push_result up_repo $the_first_commit heads/master &&
+ check_push_result down_repo $the_commit heads/master
+'
test_expect_success 'push with config remote.*.pushurl' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git checkout master &&
- git config remote.there.url test2repo &&
- git config remote.there.pushurl testrepo &&
- git push there &&
- check_push_result $the_commit heads/master
+ test_config remote.there.url test2repo &&
+ test_config remote.there.pushurl testrepo &&
+ git push there : &&
+ check_push_result testrepo $the_commit heads/master
'
-# clean up the cruft left with the previous one
-git config --remove-section remote.there
+test_expect_success 'push with config branch.*.pushremote' '
+ mk_test up_repo heads/master &&
+ mk_test side_repo heads/master &&
+ mk_test down_repo heads/master &&
+ test_config remote.up.url up_repo &&
+ test_config remote.pushdefault side_repo &&
+ test_config remote.down.url down_repo &&
+ test_config branch.master.remote up &&
+ test_config branch.master.pushremote down &&
+ test_config push.default matching &&
+ git push &&
+ check_push_result up_repo $the_first_commit heads/master &&
+ check_push_result side_repo $the_first_commit heads/master &&
+ check_push_result down_repo $the_commit heads/master
+'
+
+test_expect_success 'branch.*.pushremote config order is irrelevant' '
+ mk_test one_repo heads/master &&
+ mk_test two_repo heads/master &&
+ test_config remote.one.url one_repo &&
+ test_config remote.two.url two_repo &&
+ test_config branch.master.pushremote two_repo &&
+ test_config remote.pushdefault one_repo &&
+ test_config push.default matching &&
+ git push &&
+ check_push_result one_repo $the_first_commit heads/master &&
+ check_push_result two_repo $the_commit heads/master
+'
test_expect_success 'push with dry-run' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
(
cd testrepo &&
old_commit=$(git show-ref -s --verify refs/heads/master)
) &&
- git push --dry-run testrepo &&
- check_push_result $old_commit heads/master
+ git push --dry-run testrepo : &&
+ check_push_result testrepo $old_commit heads/master
'
test_expect_success 'push updates local refs' '
- mk_test heads/master &&
- mk_child child &&
+ mk_test testrepo heads/master &&
+ mk_child testrepo child &&
(
cd child &&
git pull .. master &&
@@ -507,9 +577,9 @@ test_expect_success 'push updates local refs' '
test_expect_success 'push updates up-to-date local refs' '
- mk_test heads/master &&
- mk_child child1 &&
- mk_child child2 &&
+ mk_test testrepo heads/master &&
+ mk_child testrepo child1 &&
+ mk_child testrepo child2 &&
(cd child1 && git pull .. master && git push) &&
(
cd child2 &&
@@ -523,8 +593,8 @@ test_expect_success 'push updates up-to-date local refs' '
test_expect_success 'push preserves up-to-date packed refs' '
- mk_test heads/master &&
- mk_child child &&
+ mk_test testrepo heads/master &&
+ mk_child testrepo child &&
(
cd child &&
git push &&
@@ -535,8 +605,8 @@ test_expect_success 'push preserves up-to-date packed refs' '
test_expect_success 'push does not update local refs on failure' '
- mk_test heads/master &&
- mk_child child &&
+ mk_test testrepo heads/master &&
+ mk_child testrepo child &&
mkdir testrepo/.git/hooks &&
echo "#!/no/frobnication/today" >testrepo/.git/hooks/pre-receive &&
chmod +x testrepo/.git/hooks/pre-receive &&
@@ -552,22 +622,185 @@ test_expect_success 'push does not update local refs on failure' '
test_expect_success 'allow deleting an invalid remote ref' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
rm -f testrepo/.git/objects/??/* &&
git push testrepo :refs/heads/master &&
(cd testrepo && test_must_fail git rev-parse --verify refs/heads/master)
'
+test_expect_success 'pushing valid refs triggers post-receive and post-update hooks' '
+ mk_test_with_hooks testrepo heads/master heads/next &&
+ orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) &&
+ newmaster=$(git show-ref -s --verify refs/heads/master) &&
+ orgnext=$(cd testrepo && git show-ref -s --verify refs/heads/next) &&
+ newnext=$_z40 &&
+ git push testrepo refs/heads/master:refs/heads/master :refs/heads/next &&
+ (
+ cd testrepo/.git &&
+ cat >pre-receive.expect <<-EOF &&
+ $orgmaster $newmaster refs/heads/master
+ $orgnext $newnext refs/heads/next
+ EOF
+
+ cat >update.expect <<-EOF &&
+ refs/heads/master $orgmaster $newmaster
+ refs/heads/next $orgnext $newnext
+ EOF
+
+ cat >post-receive.expect <<-EOF &&
+ $orgmaster $newmaster refs/heads/master
+ $orgnext $newnext refs/heads/next
+ EOF
+
+ cat >post-update.expect <<-EOF &&
+ refs/heads/master
+ refs/heads/next
+ EOF
+
+ test_cmp pre-receive.expect pre-receive.actual &&
+ test_cmp update.expect update.actual &&
+ test_cmp post-receive.expect post-receive.actual &&
+ test_cmp post-update.expect post-update.actual
+ )
+'
+
+test_expect_success 'deleting dangling ref triggers hooks with correct args' '
+ mk_test_with_hooks testrepo heads/master &&
+ rm -f testrepo/.git/objects/??/* &&
+ git push testrepo :refs/heads/master &&
+ (
+ cd testrepo/.git &&
+ cat >pre-receive.expect <<-EOF &&
+ $_z40 $_z40 refs/heads/master
+ EOF
+
+ cat >update.expect <<-EOF &&
+ refs/heads/master $_z40 $_z40
+ EOF
+
+ cat >post-receive.expect <<-EOF &&
+ $_z40 $_z40 refs/heads/master
+ EOF
+
+ cat >post-update.expect <<-EOF &&
+ refs/heads/master
+ EOF
+
+ test_cmp pre-receive.expect pre-receive.actual &&
+ test_cmp update.expect update.actual &&
+ test_cmp post-receive.expect post-receive.actual &&
+ test_cmp post-update.expect post-update.actual
+ )
+'
+
+test_expect_success 'deletion of a non-existent ref is not fed to post-receive and post-update hooks' '
+ mk_test_with_hooks testrepo heads/master &&
+ orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) &&
+ newmaster=$(git show-ref -s --verify refs/heads/master) &&
+ git push testrepo master :refs/heads/nonexistent &&
+ (
+ cd testrepo/.git &&
+ cat >pre-receive.expect <<-EOF &&
+ $orgmaster $newmaster refs/heads/master
+ $_z40 $_z40 refs/heads/nonexistent
+ EOF
+
+ cat >update.expect <<-EOF &&
+ refs/heads/master $orgmaster $newmaster
+ refs/heads/nonexistent $_z40 $_z40
+ EOF
+
+ cat >post-receive.expect <<-EOF &&
+ $orgmaster $newmaster refs/heads/master
+ EOF
+
+ cat >post-update.expect <<-EOF &&
+ refs/heads/master
+ EOF
+
+ test_cmp pre-receive.expect pre-receive.actual &&
+ test_cmp update.expect update.actual &&
+ test_cmp post-receive.expect post-receive.actual &&
+ test_cmp post-update.expect post-update.actual
+ )
+'
+
+test_expect_success 'deletion of a non-existent ref alone does trigger post-receive and post-update hooks' '
+ mk_test_with_hooks testrepo heads/master &&
+ git push testrepo :refs/heads/nonexistent &&
+ (
+ cd testrepo/.git &&
+ cat >pre-receive.expect <<-EOF &&
+ $_z40 $_z40 refs/heads/nonexistent
+ EOF
+
+ cat >update.expect <<-EOF &&
+ refs/heads/nonexistent $_z40 $_z40
+ EOF
+
+ test_cmp pre-receive.expect pre-receive.actual &&
+ test_cmp update.expect update.actual &&
+ test_path_is_missing post-receive.actual &&
+ test_path_is_missing post-update.actual
+ )
+'
+
+test_expect_success 'mixed ref updates, deletes, invalid deletes trigger hooks with correct input' '
+ mk_test_with_hooks testrepo heads/master heads/next heads/pu &&
+ orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) &&
+ newmaster=$(git show-ref -s --verify refs/heads/master) &&
+ orgnext=$(cd testrepo && git show-ref -s --verify refs/heads/next) &&
+ newnext=$_z40 &&
+ orgpu=$(cd testrepo && git show-ref -s --verify refs/heads/pu) &&
+ newpu=$(git show-ref -s --verify refs/heads/master) &&
+ git push testrepo refs/heads/master:refs/heads/master \
+ refs/heads/master:refs/heads/pu :refs/heads/next \
+ :refs/heads/nonexistent &&
+ (
+ cd testrepo/.git &&
+ cat >pre-receive.expect <<-EOF &&
+ $orgmaster $newmaster refs/heads/master
+ $orgnext $newnext refs/heads/next
+ $orgpu $newpu refs/heads/pu
+ $_z40 $_z40 refs/heads/nonexistent
+ EOF
+
+ cat >update.expect <<-EOF &&
+ refs/heads/master $orgmaster $newmaster
+ refs/heads/next $orgnext $newnext
+ refs/heads/pu $orgpu $newpu
+ refs/heads/nonexistent $_z40 $_z40
+ EOF
+
+ cat >post-receive.expect <<-EOF &&
+ $orgmaster $newmaster refs/heads/master
+ $orgnext $newnext refs/heads/next
+ $orgpu $newpu refs/heads/pu
+ EOF
+
+ cat >post-update.expect <<-EOF &&
+ refs/heads/master
+ refs/heads/next
+ refs/heads/pu
+ EOF
+
+ test_cmp pre-receive.expect pre-receive.actual &&
+ test_cmp update.expect update.actual &&
+ test_cmp post-receive.expect post-receive.actual &&
+ test_cmp post-update.expect post-update.actual
+ )
+'
+
test_expect_success 'allow deleting a ref using --delete' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
(cd testrepo && git config receive.denyDeleteCurrent warn) &&
git push testrepo --delete master &&
(cd testrepo && test_must_fail git rev-parse --verify refs/heads/master)
'
test_expect_success 'allow deleting a tag using --delete' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git tag -a -m dummy_message deltag heads/master &&
git push testrepo --tags &&
(cd testrepo && git rev-parse --verify -q refs/tags/deltag) &&
@@ -576,17 +809,17 @@ test_expect_success 'allow deleting a tag using --delete' '
'
test_expect_success 'push --delete without args aborts' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
test_must_fail git push testrepo --delete
'
test_expect_success 'push --delete refuses src:dest refspecs' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
test_must_fail git push testrepo --delete master:foo
'
test_expect_success 'warn on push to HEAD of non-bare repository' '
- mk_test heads/master
+ mk_test testrepo heads/master &&
(
cd testrepo &&
git checkout master &&
@@ -597,7 +830,7 @@ test_expect_success 'warn on push to HEAD of non-bare repository' '
'
test_expect_success 'deny push to HEAD of non-bare repository' '
- mk_test heads/master
+ mk_test testrepo heads/master &&
(
cd testrepo &&
git checkout master &&
@@ -607,7 +840,7 @@ test_expect_success 'deny push to HEAD of non-bare repository' '
'
test_expect_success 'allow push to HEAD of bare repository (bare)' '
- mk_test heads/master
+ mk_test testrepo heads/master &&
(
cd testrepo &&
git checkout master &&
@@ -619,7 +852,7 @@ test_expect_success 'allow push to HEAD of bare repository (bare)' '
'
test_expect_success 'allow push to HEAD of non-bare repository (config)' '
- mk_test heads/master
+ mk_test testrepo heads/master &&
(
cd testrepo &&
git checkout master &&
@@ -630,63 +863,63 @@ test_expect_success 'allow push to HEAD of non-bare repository (config)' '
'
test_expect_success 'fetch with branches' '
- mk_empty &&
+ mk_empty testrepo &&
git branch second $the_first_commit &&
git checkout second &&
echo ".." > testrepo/.git/branches/branch1 &&
(
cd testrepo &&
git fetch branch1 &&
- r=$(git show-ref -s --verify refs/heads/branch1) &&
- test "z$r" = "z$the_commit" &&
- test 1 = $(git for-each-ref refs/heads | wc -l)
+ echo "$the_commit commit refs/heads/branch1" >expect &&
+ git for-each-ref refs/heads >actual &&
+ test_cmp expect actual
) &&
git checkout master
'
test_expect_success 'fetch with branches containing #' '
- mk_empty &&
+ mk_empty testrepo &&
echo "..#second" > testrepo/.git/branches/branch2 &&
(
cd testrepo &&
git fetch branch2 &&
- r=$(git show-ref -s --verify refs/heads/branch2) &&
- test "z$r" = "z$the_first_commit" &&
- test 1 = $(git for-each-ref refs/heads | wc -l)
+ echo "$the_first_commit commit refs/heads/branch2" >expect &&
+ git for-each-ref refs/heads >actual &&
+ test_cmp expect actual
) &&
git checkout master
'
test_expect_success 'push with branches' '
- mk_empty &&
+ mk_empty testrepo &&
git checkout second &&
echo "testrepo" > .git/branches/branch1 &&
git push branch1 &&
(
cd testrepo &&
- r=$(git show-ref -s --verify refs/heads/master) &&
- test "z$r" = "z$the_first_commit" &&
- test 1 = $(git for-each-ref refs/heads | wc -l)
+ echo "$the_first_commit commit refs/heads/master" >expect &&
+ git for-each-ref refs/heads >actual &&
+ test_cmp expect actual
)
'
test_expect_success 'push with branches containing #' '
- mk_empty &&
+ mk_empty testrepo &&
echo "testrepo#branch3" > .git/branches/branch2 &&
git push branch2 &&
(
cd testrepo &&
- r=$(git show-ref -s --verify refs/heads/branch3) &&
- test "z$r" = "z$the_first_commit" &&
- test 1 = $(git for-each-ref refs/heads | wc -l)
+ echo "$the_first_commit commit refs/heads/branch3" >expect &&
+ git for-each-ref refs/heads >actual &&
+ test_cmp expect actual
) &&
git checkout master
'
test_expect_success 'push into aliased refs (consistent)' '
- mk_test heads/master &&
- mk_child child1 &&
- mk_child child2 &&
+ mk_test testrepo heads/master &&
+ mk_child testrepo child1 &&
+ mk_child testrepo child2 &&
(
cd child1 &&
git branch foo &&
@@ -706,9 +939,9 @@ test_expect_success 'push into aliased refs (consistent)' '
'
test_expect_success 'push into aliased refs (inconsistent)' '
- mk_test heads/master &&
- mk_child child1 &&
- mk_child child2 &&
+ mk_test testrepo heads/master &&
+ mk_child testrepo child1 &&
+ mk_child testrepo child2 &&
(
cd child1 &&
git branch foo &&
@@ -732,29 +965,50 @@ test_expect_success 'push into aliased refs (inconsistent)' '
)
'
+test_expect_success 'push requires --force to update lightweight tag' '
+ mk_test testrepo heads/master &&
+ mk_child testrepo child1 &&
+ mk_child testrepo child2 &&
+ (
+ cd child1 &&
+ git tag Tag &&
+ git push ../child2 Tag &&
+ git push ../child2 Tag &&
+ >file1 &&
+ git add file1 &&
+ git commit -m "file1" &&
+ git tag -f Tag &&
+ test_must_fail git push ../child2 Tag &&
+ git push --force ../child2 Tag &&
+ git tag -f Tag &&
+ test_must_fail git push ../child2 Tag HEAD~ &&
+ git push --force ../child2 Tag
+ )
+'
+
test_expect_success 'push --porcelain' '
- mk_empty &&
+ mk_empty testrepo &&
echo >.git/foo "To testrepo" &&
echo >>.git/foo "* refs/heads/master:refs/remotes/origin/master [new branch]" &&
echo >>.git/foo "Done" &&
git push >.git/bar --porcelain testrepo refs/heads/master:refs/remotes/origin/master &&
(
cd testrepo &&
- r=$(git show-ref -s --verify refs/remotes/origin/master) &&
- test "z$r" = "z$the_commit" &&
- test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ echo "$the_commit commit refs/remotes/origin/master" >expect &&
+ git for-each-ref refs/remotes/origin >actual &&
+ test_cmp expect actual
) &&
test_cmp .git/foo .git/bar
'
test_expect_success 'push --porcelain bad url' '
- mk_empty &&
+ mk_empty testrepo &&
test_must_fail git push >.git/bar --porcelain asdfasdfasd refs/heads/master:refs/remotes/origin/master &&
test_must_fail grep -q Done .git/bar
'
test_expect_success 'push --porcelain rejected' '
- mk_empty &&
+ mk_empty testrepo &&
git push testrepo refs/heads/master:refs/remotes/origin/master &&
(cd testrepo &&
git reset --hard origin/master^
@@ -768,7 +1022,7 @@ test_expect_success 'push --porcelain rejected' '
'
test_expect_success 'push --porcelain --dry-run rejected' '
- mk_empty &&
+ mk_empty testrepo &&
git push testrepo refs/heads/master:refs/remotes/origin/master &&
(cd testrepo &&
git reset --hard origin/master
@@ -782,4 +1036,580 @@ test_expect_success 'push --porcelain --dry-run rejected' '
test_cmp .git/foo .git/bar
'
+test_expect_success 'push --prune' '
+ mk_test testrepo heads/master heads/second heads/foo heads/bar &&
+ git push --prune testrepo : &&
+ check_push_result testrepo $the_commit heads/master &&
+ check_push_result testrepo $the_first_commit heads/second &&
+ ! check_push_result testrepo $the_first_commit heads/foo heads/bar
+'
+
+test_expect_success 'push --prune refspec' '
+ mk_test testrepo tmp/master tmp/second tmp/foo tmp/bar &&
+ git push --prune testrepo "refs/heads/*:refs/tmp/*" &&
+ check_push_result testrepo $the_commit tmp/master &&
+ check_push_result testrepo $the_first_commit tmp/second &&
+ ! check_push_result testrepo $the_first_commit tmp/foo tmp/bar
+'
+
+for configsection in transfer receive
+do
+ test_expect_success "push to update a ref hidden by $configsection.hiderefs" '
+ mk_test testrepo heads/master hidden/one hidden/two hidden/three &&
+ (
+ cd testrepo &&
+ git config $configsection.hiderefs refs/hidden
+ ) &&
+
+ # push to unhidden ref succeeds normally
+ git push testrepo master:refs/heads/master &&
+ check_push_result testrepo $the_commit heads/master &&
+
+ # push to update a hidden ref should fail
+ test_must_fail git push testrepo master:refs/hidden/one &&
+ check_push_result testrepo $the_first_commit hidden/one &&
+
+ # push to delete a hidden ref should fail
+ test_must_fail git push testrepo :refs/hidden/two &&
+ check_push_result testrepo $the_first_commit hidden/two &&
+
+ # idempotent push to update a hidden ref should fail
+ test_must_fail git push testrepo $the_first_commit:refs/hidden/three &&
+ check_push_result testrepo $the_first_commit hidden/three
+ '
+done
+
+test_expect_success 'fetch exact SHA1' '
+ mk_test testrepo heads/master hidden/one &&
+ git push testrepo master:refs/hidden/one &&
+ (
+ cd testrepo &&
+ git config transfer.hiderefs refs/hidden
+ ) &&
+ check_push_result testrepo $the_commit hidden/one &&
+
+ mk_child testrepo child &&
+ (
+ cd child &&
+
+ # make sure $the_commit does not exist here
+ git repack -a -d &&
+ git prune &&
+ test_must_fail git cat-file -t $the_commit &&
+
+ # fetching the hidden object should fail by default
+ test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy &&
+ test_must_fail git rev-parse --verify refs/heads/copy &&
+
+ # the server side can allow it to succeed
+ (
+ cd ../testrepo &&
+ git config uploadpack.allowtipsha1inwant true
+ ) &&
+
+ git fetch -v ../testrepo $the_commit:refs/heads/copy master:refs/heads/extra &&
+ cat >expect <<-EOF &&
+ $the_commit
+ $the_first_commit
+ EOF
+ {
+ git rev-parse --verify refs/heads/copy &&
+ git rev-parse --verify refs/heads/extra
+ } >actual &&
+ test_cmp expect actual
+ )
+'
+
+for configallowtipsha1inwant in true false
+do
+ test_expect_success "shallow fetch reachable SHA1 (but not a ref), allowtipsha1inwant=$configallowtipsha1inwant" '
+ mk_empty testrepo &&
+ (
+ cd testrepo &&
+ git config uploadpack.allowtipsha1inwant $configallowtipsha1inwant &&
+ git commit --allow-empty -m foo &&
+ git commit --allow-empty -m bar
+ ) &&
+ SHA1=$(git --git-dir=testrepo/.git rev-parse HEAD^) &&
+ mk_empty shallow &&
+ (
+ cd shallow &&
+ test_must_fail git fetch --depth=1 ../testrepo/.git $SHA1 &&
+ git --git-dir=../testrepo/.git config uploadpack.allowreachablesha1inwant true &&
+ git fetch --depth=1 ../testrepo/.git $SHA1 &&
+ git cat-file commit $SHA1
+ )
+ '
+
+ test_expect_success "deny fetch unreachable SHA1, allowtipsha1inwant=$configallowtipsha1inwant" '
+ mk_empty testrepo &&
+ (
+ cd testrepo &&
+ git config uploadpack.allowtipsha1inwant $configallowtipsha1inwant &&
+ git commit --allow-empty -m foo &&
+ git commit --allow-empty -m bar &&
+ git commit --allow-empty -m xyz
+ ) &&
+ SHA1_1=$(git --git-dir=testrepo/.git rev-parse HEAD^^) &&
+ SHA1_2=$(git --git-dir=testrepo/.git rev-parse HEAD^) &&
+ SHA1_3=$(git --git-dir=testrepo/.git rev-parse HEAD) &&
+ (
+ cd testrepo &&
+ git reset --hard $SHA1_2 &&
+ git cat-file commit $SHA1_1 &&
+ git cat-file commit $SHA1_3
+ ) &&
+ mk_empty shallow &&
+ (
+ cd shallow &&
+ test_must_fail ok=sigpipe git fetch ../testrepo/.git $SHA1_3 &&
+ test_must_fail ok=sigpipe git fetch ../testrepo/.git $SHA1_1 &&
+ git --git-dir=../testrepo/.git config uploadpack.allowreachablesha1inwant true &&
+ git fetch ../testrepo/.git $SHA1_1 &&
+ git cat-file commit $SHA1_1 &&
+ test_must_fail git cat-file commit $SHA1_2 &&
+ git fetch ../testrepo/.git $SHA1_2 &&
+ git cat-file commit $SHA1_2 &&
+ test_must_fail ok=sigpipe git fetch ../testrepo/.git $SHA1_3
+ )
+ '
+done
+
+test_expect_success 'fetch follows tags by default' '
+ mk_test testrepo heads/master &&
+ rm -fr src dst &&
+ git init src &&
+ (
+ cd src &&
+ git pull ../testrepo master &&
+ git tag -m "annotated" tag &&
+ git for-each-ref >tmp1 &&
+ (
+ cat tmp1
+ sed -n "s|refs/heads/master$|refs/remotes/origin/master|p" tmp1
+ ) |
+ sort -k 3 >../expect
+ ) &&
+ git init dst &&
+ (
+ cd dst &&
+ git remote add origin ../src &&
+ git config branch.master.remote origin &&
+ git config branch.master.merge refs/heads/master &&
+ git pull &&
+ git for-each-ref >../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pushing a specific ref applies remote.$name.push as refmap' '
+ mk_test testrepo heads/master &&
+ rm -fr src dst &&
+ git init src &&
+ git init --bare dst &&
+ (
+ cd src &&
+ git pull ../testrepo master &&
+ git branch next &&
+ git config remote.dst.url ../dst &&
+ git config remote.dst.push "+refs/heads/*:refs/remotes/src/*" &&
+ git push dst master &&
+ git show-ref refs/heads/master |
+ sed -e "s|refs/heads/|refs/remotes/src/|" >../dst/expect
+ ) &&
+ (
+ cd dst &&
+ test_must_fail git show-ref refs/heads/next &&
+ test_must_fail git show-ref refs/heads/master &&
+ git show-ref refs/remotes/src/master >actual
+ ) &&
+ test_cmp dst/expect dst/actual
+'
+
+test_expect_success 'with no remote.$name.push, it is not used as refmap' '
+ mk_test testrepo heads/master &&
+ rm -fr src dst &&
+ git init src &&
+ git init --bare dst &&
+ (
+ cd src &&
+ git pull ../testrepo master &&
+ git branch next &&
+ git config remote.dst.url ../dst &&
+ git config push.default matching &&
+ git push dst master &&
+ git show-ref refs/heads/master >../dst/expect
+ ) &&
+ (
+ cd dst &&
+ test_must_fail git show-ref refs/heads/next &&
+ git show-ref refs/heads/master >actual
+ ) &&
+ test_cmp dst/expect dst/actual
+'
+
+test_expect_success 'with no remote.$name.push, upstream mapping is used' '
+ mk_test testrepo heads/master &&
+ rm -fr src dst &&
+ git init src &&
+ git init --bare dst &&
+ (
+ cd src &&
+ git pull ../testrepo master &&
+ git branch next &&
+ git config remote.dst.url ../dst &&
+ git config remote.dst.fetch "+refs/heads/*:refs/remotes/dst/*" &&
+ git config push.default upstream &&
+
+ git config branch.master.merge refs/heads/trunk &&
+ git config branch.master.remote dst &&
+
+ git push dst master &&
+ git show-ref refs/heads/master |
+ sed -e "s|refs/heads/master|refs/heads/trunk|" >../dst/expect
+ ) &&
+ (
+ cd dst &&
+ test_must_fail git show-ref refs/heads/master &&
+ test_must_fail git show-ref refs/heads/next &&
+ git show-ref refs/heads/trunk >actual
+ ) &&
+ test_cmp dst/expect dst/actual
+'
+
+test_expect_success 'push does not follow tags by default' '
+ mk_test testrepo heads/master &&
+ rm -fr src dst &&
+ git init src &&
+ git init --bare dst &&
+ (
+ cd src &&
+ git pull ../testrepo master &&
+ git tag -m "annotated" tag &&
+ git checkout -b another &&
+ git commit --allow-empty -m "future commit" &&
+ git tag -m "future" future &&
+ git checkout master &&
+ git for-each-ref refs/heads/master >../expect &&
+ git push ../dst master
+ ) &&
+ (
+ cd dst &&
+ git for-each-ref >../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push --follow-tag only pushes relevant tags' '
+ mk_test testrepo heads/master &&
+ rm -fr src dst &&
+ git init src &&
+ git init --bare dst &&
+ (
+ cd src &&
+ git pull ../testrepo master &&
+ git tag -m "annotated" tag &&
+ git checkout -b another &&
+ git commit --allow-empty -m "future commit" &&
+ git tag -m "future" future &&
+ git checkout master &&
+ git for-each-ref refs/heads/master refs/tags/tag >../expect
+ git push --follow-tag ../dst master
+ ) &&
+ (
+ cd dst &&
+ git for-each-ref >../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push --no-thin must produce non-thin pack' '
+ cat >>path1 <<\EOF &&
+keep base version of path1 big enough, compared to the new changes
+later, in order to pass size heuristics in
+builtin/pack-objects.c:try_delta()
+EOF
+ git commit -am initial &&
+ git init no-thin &&
+ git --git-dir=no-thin/.git config receive.unpacklimit 0 &&
+ git push no-thin/.git refs/heads/master:refs/heads/foo &&
+ echo modified >> path1 &&
+ git commit -am modified &&
+ git repack -adf &&
+ rcvpck="git receive-pack --reject-thin-pack-for-testing" &&
+ git push --no-thin --receive-pack="$rcvpck" no-thin/.git refs/heads/master:refs/heads/foo
+'
+
+test_expect_success 'pushing a tag pushes the tagged object' '
+ rm -rf dst.git &&
+ blob=$(echo unreferenced | git hash-object -w --stdin) &&
+ git tag -m foo tag-of-blob $blob &&
+ git init --bare dst.git &&
+ git push dst.git tag-of-blob &&
+ # the receiving index-pack should have noticed
+ # any problems, but we double check
+ echo unreferenced >expect &&
+ git --git-dir=dst.git cat-file blob tag-of-blob >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push into bare respects core.logallrefupdates' '
+ rm -rf dst.git &&
+ git init --bare dst.git &&
+ git -C dst.git config core.logallrefupdates true &&
+
+ # double push to test both with and without
+ # the actual pack transfer
+ git push dst.git master:one &&
+ echo "one@{0} push" >expect &&
+ git -C dst.git log -g --format="%gd %gs" one >actual &&
+ test_cmp expect actual &&
+
+ git push dst.git master:two &&
+ echo "two@{0} push" >expect &&
+ git -C dst.git log -g --format="%gd %gs" two >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fetch into bare respects core.logallrefupdates' '
+ rm -rf dst.git &&
+ git init --bare dst.git &&
+ (
+ cd dst.git &&
+ git config core.logallrefupdates true &&
+
+ # as above, we double-fetch to test both
+ # with and without pack transfer
+ git fetch .. master:one &&
+ echo "one@{0} fetch .. master:one: storing head" >expect &&
+ git log -g --format="%gd %gs" one >actual &&
+ test_cmp expect actual &&
+
+ git fetch .. master:two &&
+ echo "two@{0} fetch .. master:two: storing head" >expect &&
+ git log -g --format="%gd %gs" two >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'receive.denyCurrentBranch = updateInstead' '
+ git push testrepo master &&
+ (
+ cd testrepo &&
+ git reset --hard &&
+ git config receive.denyCurrentBranch updateInstead
+ ) &&
+ test_commit third path2 &&
+
+ # Try pushing into a repository with pristine working tree
+ git push testrepo master &&
+ (
+ cd testrepo &&
+ git update-index -q --refresh &&
+ git diff-files --quiet -- &&
+ git diff-index --quiet --cached HEAD -- &&
+ test third = "$(cat path2)" &&
+ test $(git -C .. rev-parse HEAD) = $(git rev-parse HEAD)
+ ) &&
+
+ # Try pushing into a repository with working tree needing a refresh
+ (
+ cd testrepo &&
+ git reset --hard HEAD^ &&
+ test $(git -C .. rev-parse HEAD^) = $(git rev-parse HEAD) &&
+ test-chmtime +100 path1
+ ) &&
+ git push testrepo master &&
+ (
+ cd testrepo &&
+ git update-index -q --refresh &&
+ git diff-files --quiet -- &&
+ git diff-index --quiet --cached HEAD -- &&
+ test_cmp ../path1 path1 &&
+ test third = "$(cat path2)" &&
+ test $(git -C .. rev-parse HEAD) = $(git rev-parse HEAD)
+ ) &&
+
+ # Update what is to be pushed
+ test_commit fourth path2 &&
+
+ # Try pushing into a repository with a dirty working tree
+ # (1) the working tree updated
+ (
+ cd testrepo &&
+ echo changed >path1
+ ) &&
+ test_must_fail git push testrepo master &&
+ (
+ cd testrepo &&
+ test $(git -C .. rev-parse HEAD^) = $(git rev-parse HEAD) &&
+ git diff --quiet --cached &&
+ test changed = "$(cat path1)"
+ ) &&
+
+ # (2) the index updated
+ (
+ cd testrepo &&
+ echo changed >path1 &&
+ git add path1
+ ) &&
+ test_must_fail git push testrepo master &&
+ (
+ cd testrepo &&
+ test $(git -C .. rev-parse HEAD^) = $(git rev-parse HEAD) &&
+ git diff --quiet &&
+ test changed = "$(cat path1)"
+ ) &&
+
+ # Introduce a new file in the update
+ test_commit fifth path3 &&
+
+ # (3) the working tree has an untracked file that would interfere
+ (
+ cd testrepo &&
+ git reset --hard &&
+ echo changed >path3
+ ) &&
+ test_must_fail git push testrepo master &&
+ (
+ cd testrepo &&
+ test $(git -C .. rev-parse HEAD^^) = $(git rev-parse HEAD) &&
+ git diff --quiet &&
+ git diff --quiet --cached &&
+ test changed = "$(cat path3)"
+ ) &&
+
+ # (4) the target changes to what gets pushed but it still is a change
+ (
+ cd testrepo &&
+ git reset --hard &&
+ echo fifth >path3 &&
+ git add path3
+ ) &&
+ test_must_fail git push testrepo master &&
+ (
+ cd testrepo &&
+ test $(git -C .. rev-parse HEAD^^) = $(git rev-parse HEAD) &&
+ git diff --quiet &&
+ test fifth = "$(cat path3)"
+ ) &&
+
+ # (5) push into void
+ rm -fr void &&
+ git init void &&
+ (
+ cd void &&
+ git config receive.denyCurrentBranch updateInstead
+ ) &&
+ git push void master &&
+ (
+ cd void &&
+ test $(git -C .. rev-parse master) = $(git rev-parse HEAD) &&
+ git diff --quiet &&
+ git diff --cached --quiet
+ )
+'
+
+test_expect_success 'updateInstead with push-to-checkout hook' '
+ rm -fr testrepo &&
+ git init testrepo &&
+ (
+ cd testrepo &&
+ git pull .. master &&
+ git reset --hard HEAD^^ &&
+ git tag initial &&
+ git config receive.denyCurrentBranch updateInstead &&
+ write_script .git/hooks/push-to-checkout <<-\EOF
+ echo >&2 updating from $(git rev-parse HEAD)
+ echo >&2 updating to "$1"
+
+ git update-index -q --refresh &&
+ git read-tree -u -m HEAD "$1" || {
+ status=$?
+ echo >&2 read-tree failed
+ exit $status
+ }
+ EOF
+ ) &&
+
+ # Try pushing into a pristine
+ git push testrepo master &&
+ (
+ cd testrepo &&
+ git diff --quiet &&
+ git diff HEAD --quiet &&
+ test $(git -C .. rev-parse HEAD) = $(git rev-parse HEAD)
+ ) &&
+
+ # Try pushing into a repository with conflicting change
+ (
+ cd testrepo &&
+ git reset --hard initial &&
+ echo conflicting >path2
+ ) &&
+ test_must_fail git push testrepo master &&
+ (
+ cd testrepo &&
+ test $(git rev-parse initial) = $(git rev-parse HEAD) &&
+ test conflicting = "$(cat path2)" &&
+ git diff-index --quiet --cached HEAD
+ ) &&
+
+ # Try pushing into a repository with unrelated change
+ (
+ cd testrepo &&
+ git reset --hard initial &&
+ echo unrelated >path1 &&
+ echo irrelevant >path5 &&
+ git add path5
+ ) &&
+ git push testrepo master &&
+ (
+ cd testrepo &&
+ test "$(cat path1)" = unrelated &&
+ test "$(cat path5)" = irrelevant &&
+ test "$(git diff --name-only --cached HEAD)" = path5 &&
+ test $(git -C .. rev-parse HEAD) = $(git rev-parse HEAD)
+ ) &&
+
+ # push into void
+ rm -fr void &&
+ git init void &&
+ (
+ cd void &&
+ git config receive.denyCurrentBranch updateInstead &&
+ write_script .git/hooks/push-to-checkout <<-\EOF
+ if git rev-parse --quiet --verify HEAD
+ then
+ has_head=yes
+ echo >&2 updating from $(git rev-parse HEAD)
+ else
+ has_head=no
+ echo >&2 pushing into void
+ fi
+ echo >&2 updating to "$1"
+
+ git update-index -q --refresh &&
+ case "$has_head" in
+ yes)
+ git read-tree -u -m HEAD "$1" ;;
+ no)
+ git read-tree -u -m "$1" ;;
+ esac || {
+ status=$?
+ echo >&2 read-tree failed
+ exit $status
+ }
+ EOF
+ ) &&
+
+ git push void master &&
+ (
+ cd void &&
+ git diff --quiet &&
+ git diff --cached --quiet &&
+ test $(git -C .. rev-parse HEAD) = $(git rev-parse HEAD)
+ )
+'
+
test_done
diff --git a/t/t5517-push-mirror.sh b/t/t5517-push-mirror.sh
index e2ad260..02f160a 100755
--- a/t/t5517-push-mirror.sh
+++ b/t/t5517-push-mirror.sh
@@ -4,7 +4,7 @@ test_description='pushing to a mirror repository'
. ./test-lib.sh
-D=`pwd`
+D=$(pwd)
invert () {
if "$@"; then
@@ -256,7 +256,7 @@ test_expect_success 'remote.foo.mirror=no has no effect' '
git branch keep master &&
git push --mirror up &&
git branch -D keep &&
- git push up
+ git push up :
) &&
(
cd mirror &&
diff --git a/t/t5519-push-alternates.sh b/t/t5519-push-alternates.sh
index 96be523..11fcd37 100755
--- a/t/t5519-push-alternates.sh
+++ b/t/t5519-push-alternates.sh
@@ -40,7 +40,7 @@ test_expect_success 'alice works and pushes' '
cd alice-work &&
echo more >file &&
git commit -a -m second &&
- git push ../alice-pub
+ git push ../alice-pub :
)
'
@@ -57,7 +57,7 @@ test_expect_success 'bob fetches from alice, works and pushes' '
git pull ../alice-pub master &&
echo more bob >file &&
git commit -a -m third &&
- git push ../bob-pub
+ git push ../bob-pub :
) &&
# Check that the second commit by Alice is not sent
@@ -86,7 +86,7 @@ test_expect_success 'alice works and pushes again' '
cd alice-work &&
echo more alice >file &&
git commit -a -m fourth &&
- git push ../alice-pub
+ git push ../alice-pub :
)
'
@@ -99,7 +99,7 @@ test_expect_success 'bob works and pushes' '
cd bob-work &&
echo yet more bob >file &&
git commit -a -m fifth &&
- git push ../bob-pub
+ git push ../bob-pub :
)
'
@@ -115,7 +115,7 @@ test_expect_success 'alice works and pushes yet again' '
git commit -a -m sixth.2 &&
echo more and more alice >>file &&
git commit -a -m sixth.3 &&
- git push ../alice-pub
+ git push ../alice-pub :
)
'
@@ -123,7 +123,7 @@ 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
@@ -136,7 +136,7 @@ test_expect_success 'bob works and pushes again' '
git hash-object -t commit -w commit &&
echo even more bob >file &&
git commit -a -m seventh &&
- git push ../bob-pub
+ git push ../bob-pub :
)
'
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 319e389..739c089 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -4,36 +4,50 @@ test_description='pulling into void'
. ./test-lib.sh
-D=`pwd`
+modify () {
+ sed -e "$1" <"$2" >"$2.x" &&
+ mv "$2.x" "$2"
+}
-test_expect_success setup '
+test_pull_autostash () {
+ git reset --hard before-rebase &&
+ echo dirty >new_file &&
+ git add new_file &&
+ git pull "$@" . copy &&
+ test_cmp_rev HEAD^ copy &&
+ test "$(cat new_file)" = dirty &&
+ test "$(cat file)" = "modified again"
+}
+test_pull_autostash_fail () {
+ git reset --hard before-rebase &&
+ echo dirty >new_file &&
+ git add new_file &&
+ test_must_fail git pull "$@" . copy 2>err &&
+ test_i18ngrep "uncommitted changes." err
+}
+
+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' '
+ git init cloned &&
+ (
+ cd cloned &&
+ git pull ..
+ ) &&
test -f file &&
test -f cloned/file &&
test_cmp file cloned/file
'
test_expect_success 'pulling into void using master:master' '
- mkdir cloned-uho &&
+ git init cloned-uho &&
(
cd cloned-uho &&
- git init &&
git pull .. master:master
) &&
test -f file &&
@@ -41,17 +55,68 @@ test_expect_success 'pulling into void using master:master' '
test_cmp file cloned-uho/file
'
-test_expect_success 'test . as a remote' '
+test_expect_success 'pulling into void does not overwrite untracked files' '
+ git init cloned-untracked &&
+ (
+ cd cloned-untracked &&
+ echo untracked >file &&
+ test_must_fail git pull .. master &&
+ echo untracked >expect &&
+ test_cmp expect file
+ )
+'
+
+test_expect_success 'pulling into void does not overwrite staged files' '
+ git init cloned-staged-colliding &&
+ (
+ cd cloned-staged-colliding &&
+ echo "alternate content" >file &&
+ git add file &&
+ test_must_fail git pull .. master &&
+ echo "alternate content" >expect &&
+ test_cmp expect file &&
+ git cat-file blob :file >file.index &&
+ test_cmp expect file.index
+ )
+'
+test_expect_success 'pulling into void does not remove new staged files' '
+ git init cloned-staged-new &&
+ (
+ cd cloned-staged-new &&
+ echo "new tracked file" >newfile &&
+ git add newfile &&
+ git pull .. master &&
+ echo "new tracked file" >expect &&
+ test_cmp expect newfile &&
+ git cat-file blob :newfile >newfile.index &&
+ test_cmp expect newfile.index
+ )
+'
+
+test_expect_success 'pulling into void must not create an octopus' '
+ git init cloned-octopus &&
+ (
+ cd cloned-octopus &&
+ test_must_fail git pull .. master master &&
+ ! test -f 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 &&
+ test "$(cat file)" = file &&
git pull &&
- test `cat file` = updated
+ test "$(cat file)" = updated &&
+ git reflog -1 >reflog.actual &&
+ sed "s/^[0-9a-f][0-9a-f]*/OBJID/" reflog.actual >reflog.fuzzy &&
+ echo "OBJID HEAD@{0}: pull: Fast-forward" >reflog.expected &&
+ test_cmp reflog.expected reflog.fuzzy
'
test_expect_success 'the default remote . should not break explicit pull' '
@@ -60,9 +125,120 @@ test_expect_success 'the default remote . should not break explicit pull' '
git commit -a -m modified &&
git checkout copy &&
git reset --hard HEAD^ &&
- test `cat file` = file &&
+ test "$(cat file)" = file &&
git pull . second &&
- test `cat file` = modified
+ test "$(cat file)" = modified &&
+ git reflog -1 >reflog.actual &&
+ sed "s/^[0-9a-f][0-9a-f]*/OBJID/" reflog.actual >reflog.fuzzy &&
+ echo "OBJID HEAD@{0}: pull . second: Fast-forward" >reflog.expected &&
+ test_cmp reflog.expected reflog.fuzzy
+'
+
+test_expect_success 'fail if wildcard spec does not match any refs' '
+ git checkout -b test copy^ &&
+ test_when_finished "git checkout -f copy && git branch -D test" &&
+ test "$(cat file)" = file &&
+ test_must_fail git pull . "refs/nonexisting1/*:refs/nonexisting2/*" 2>err &&
+ test_i18ngrep "no candidates for merging" err &&
+ test "$(cat file)" = file
+'
+
+test_expect_success 'fail if no branches specified with non-default remote' '
+ git remote add test_remote . &&
+ test_when_finished "git remote remove test_remote" &&
+ git checkout -b test copy^ &&
+ test_when_finished "git checkout -f copy && git branch -D test" &&
+ test "$(cat file)" = file &&
+ test_config branch.test.remote origin &&
+ test_must_fail git pull test_remote 2>err &&
+ test_i18ngrep "specify a branch on the command line" err &&
+ test "$(cat file)" = file
+'
+
+test_expect_success 'fail if not on a branch' '
+ git remote add origin . &&
+ test_when_finished "git remote remove origin" &&
+ git checkout HEAD^ &&
+ test_when_finished "git checkout -f copy" &&
+ test "$(cat file)" = file &&
+ test_must_fail git pull 2>err &&
+ test_i18ngrep "not currently on a branch" err &&
+ test "$(cat file)" = file
+'
+
+test_expect_success 'fail if no configuration for current branch' '
+ git remote add test_remote . &&
+ test_when_finished "git remote remove test_remote" &&
+ git checkout -b test copy^ &&
+ test_when_finished "git checkout -f copy && git branch -D test" &&
+ test_config branch.test.remote test_remote &&
+ test "$(cat file)" = file &&
+ test_must_fail git pull 2>err &&
+ test_i18ngrep "no tracking information" err &&
+ test "$(cat file)" = file
+'
+
+test_expect_success 'pull --all: fail if no configuration for current branch' '
+ git remote add test_remote . &&
+ test_when_finished "git remote remove test_remote" &&
+ git checkout -b test copy^ &&
+ test_when_finished "git checkout -f copy && git branch -D test" &&
+ test_config branch.test.remote test_remote &&
+ test "$(cat file)" = file &&
+ test_must_fail git pull --all 2>err &&
+ test_i18ngrep "There is no tracking information" err &&
+ test "$(cat file)" = file
+'
+
+test_expect_success 'fail if upstream branch does not exist' '
+ git checkout -b test copy^ &&
+ test_when_finished "git checkout -f copy && git branch -D test" &&
+ test_config branch.test.remote . &&
+ test_config branch.test.merge refs/heads/nonexisting &&
+ test "$(cat file)" = file &&
+ test_must_fail git pull 2>err &&
+ test_i18ngrep "no such ref was fetched" err &&
+ test "$(cat file)" = file
+'
+
+test_expect_success 'fail if the index has unresolved entries' '
+ git checkout -b third second^ &&
+ test_when_finished "git checkout -f copy && git branch -D third" &&
+ test "$(cat file)" = file &&
+ test_commit modified2 file &&
+ test -z "$(git ls-files -u)" &&
+ test_must_fail git pull . second &&
+ test -n "$(git ls-files -u)" &&
+ cp file expected &&
+ test_must_fail git pull . second 2>err &&
+ test_i18ngrep "Pull is not possible because you have unmerged files" err &&
+ test_cmp expected file &&
+ git add file &&
+ test -z "$(git ls-files -u)" &&
+ test_must_fail git pull . second 2>err &&
+ test_i18ngrep "You have not concluded your merge" err &&
+ test_cmp expected file
+'
+
+test_expect_success 'fast-forwards working tree if branch head is updated' '
+ git checkout -b third second^ &&
+ test_when_finished "git checkout -f copy && git branch -D third" &&
+ test "$(cat file)" = file &&
+ git pull . second:third 2>err &&
+ test_i18ngrep "fetch updated the current branch head" err &&
+ test "$(cat file)" = modified &&
+ test "$(git rev-parse third)" = "$(git rev-parse second)"
+'
+
+test_expect_success 'fast-forward fails with conflicting work tree' '
+ git checkout -b third second^ &&
+ test_when_finished "git checkout -f copy && git branch -D third" &&
+ test "$(cat file)" = file &&
+ echo conflict >file &&
+ test_must_fail git pull . second:third 2>err &&
+ test_i18ngrep "Cannot fast-forward your working tree" err &&
+ test "$(cat file)" = conflict &&
+ test "$(git rev-parse third)" = "$(git rev-parse second)"
'
test_expect_success '--rebase' '
@@ -75,17 +251,193 @@ test_expect_success '--rebase' '
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 "$(git rev-parse HEAD^)" = "$(git rev-parse copy)" &&
+ test new = "$(git show HEAD:file2)"
+'
+
+test_expect_success '--rebase fails with multiple branches' '
+ git reset --hard before-rebase &&
+ test_must_fail git pull --rebase . copy master 2>err &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse before-rebase)" &&
+ test_i18ngrep "Cannot rebase onto multiple branches" err &&
+ test modified = "$(git show HEAD:file)"
+'
+
+test_expect_success 'pull --rebase succeeds with dirty working directory and rebase.autostash set' '
+ test_config rebase.autostash true &&
+ test_pull_autostash --rebase
+'
+
+test_expect_success 'pull --rebase --autostash & rebase.autostash=true' '
+ test_config rebase.autostash true &&
+ test_pull_autostash --rebase --autostash
+'
+
+test_expect_success 'pull --rebase --autostash & rebase.autostash=false' '
+ test_config rebase.autostash false &&
+ test_pull_autostash --rebase --autostash
+'
+
+test_expect_success 'pull --rebase --autostash & rebase.autostash unset' '
+ test_unconfig rebase.autostash &&
+ test_pull_autostash --rebase --autostash
+'
+
+test_expect_success 'pull --rebase --no-autostash & rebase.autostash=true' '
+ test_config rebase.autostash true &&
+ test_pull_autostash_fail --rebase --no-autostash
+'
+
+test_expect_success 'pull --rebase --no-autostash & rebase.autostash=false' '
+ test_config rebase.autostash false &&
+ test_pull_autostash_fail --rebase --no-autostash
+'
+
+test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '
+ test_unconfig rebase.autostash &&
+ test_pull_autostash_fail --rebase --no-autostash
+'
+
+for i in --autostash --no-autostash
+do
+ test_expect_success "pull $i (without --rebase) is illegal" '
+ test_must_fail git pull $i . copy 2>err &&
+ test_i18ngrep "only valid with --rebase" err
+ '
+done
+
+test_expect_success 'pull.rebase' '
+ git reset --hard before-rebase &&
+ test_config pull.rebase true &&
+ git pull . copy &&
+ test "$(git rev-parse HEAD^)" = "$(git rev-parse copy)" &&
+ test new = "$(git show HEAD:file2)"
+'
+
+test_expect_success 'pull --autostash & pull.rebase=true' '
+ test_config pull.rebase true &&
+ test_pull_autostash --autostash
+'
+
+test_expect_success 'pull --no-autostash & pull.rebase=true' '
+ test_config pull.rebase true &&
+ test_pull_autostash_fail --no-autostash
'
test_expect_success 'branch.to-rebase.rebase' '
git reset --hard before-rebase &&
- git config branch.to-rebase.rebase 1 &&
+ test_config branch.to-rebase.rebase true &&
+ git pull . copy &&
+ test "$(git rev-parse HEAD^)" = "$(git rev-parse copy)" &&
+ test new = "$(git show HEAD:file2)"
+'
+
+test_expect_success 'branch.to-rebase.rebase should override pull.rebase' '
+ git reset --hard before-rebase &&
+ test_config pull.rebase true &&
+ test_config branch.to-rebase.rebase false &&
+ git pull . copy &&
+ test "$(git rev-parse HEAD^)" != "$(git rev-parse copy)" &&
+ test new = "$(git show HEAD:file2)"
+'
+
+# add a feature branch, keep-merge, that is merged into master, so the
+# test can try preserving the merge commit (or not) with various
+# --rebase flags/pull.rebase settings.
+test_expect_success 'preserve merge setup' '
+ git reset --hard before-rebase &&
+ git checkout -b keep-merge second^ &&
+ test_commit file3 &&
+ git checkout to-rebase &&
+ git merge keep-merge &&
+ git tag before-preserve-rebase
+'
+
+test_expect_success 'pull.rebase=false create a new merge commit' '
+ git reset --hard before-preserve-rebase &&
+ test_config pull.rebase false &&
+ git pull . copy &&
+ test "$(git rev-parse HEAD^1)" = "$(git rev-parse before-preserve-rebase)" &&
+ test "$(git rev-parse HEAD^2)" = "$(git rev-parse copy)" &&
+ test file3 = "$(git show HEAD:file3.t)"
+'
+
+test_expect_success 'pull.rebase=true flattens keep-merge' '
+ git reset --hard before-preserve-rebase &&
+ test_config pull.rebase true &&
git pull . copy &&
- git config branch.to-rebase.rebase 0 &&
- test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
- test new = $(git show HEAD:file2)
+ test "$(git rev-parse HEAD^^)" = "$(git rev-parse copy)" &&
+ test file3 = "$(git show HEAD:file3.t)"
+'
+
+test_expect_success 'pull.rebase=1 is treated as true and flattens keep-merge' '
+ git reset --hard before-preserve-rebase &&
+ test_config pull.rebase 1 &&
+ git pull . copy &&
+ test "$(git rev-parse HEAD^^)" = "$(git rev-parse copy)" &&
+ test file3 = "$(git show HEAD:file3.t)"
+'
+
+test_expect_success 'pull.rebase=preserve rebases and merges keep-merge' '
+ git reset --hard before-preserve-rebase &&
+ test_config pull.rebase preserve &&
+ git pull . copy &&
+ test "$(git rev-parse HEAD^^)" = "$(git rev-parse copy)" &&
+ test "$(git rev-parse HEAD^2)" = "$(git rev-parse keep-merge)"
+'
+
+test_expect_success 'pull.rebase=interactive' '
+ write_script "$TRASH_DIRECTORY/fake-editor" <<-\EOF &&
+ echo I was here >fake.out &&
+ false
+ EOF
+ test_set_editor "$TRASH_DIRECTORY/fake-editor" &&
+ test_must_fail git pull --rebase=interactive . copy &&
+ test "I was here" = "$(cat fake.out)"
+'
+
+test_expect_success 'pull.rebase=invalid fails' '
+ git reset --hard before-preserve-rebase &&
+ test_config pull.rebase invalid &&
+ ! git pull . copy
+'
+
+test_expect_success '--rebase=false create a new merge commit' '
+ git reset --hard before-preserve-rebase &&
+ test_config pull.rebase true &&
+ git pull --rebase=false . copy &&
+ test "$(git rev-parse HEAD^1)" = "$(git rev-parse before-preserve-rebase)" &&
+ test "$(git rev-parse HEAD^2)" = "$(git rev-parse copy)" &&
+ test file3 = "$(git show HEAD:file3.t)"
+'
+
+test_expect_success '--rebase=true rebases and flattens keep-merge' '
+ git reset --hard before-preserve-rebase &&
+ test_config pull.rebase preserve &&
+ git pull --rebase=true . copy &&
+ test "$(git rev-parse HEAD^^)" = "$(git rev-parse copy)" &&
+ test file3 = "$(git show HEAD:file3.t)"
+'
+
+test_expect_success '--rebase=preserve rebases and merges keep-merge' '
+ git reset --hard before-preserve-rebase &&
+ test_config pull.rebase true &&
+ git pull --rebase=preserve . copy &&
+ test "$(git rev-parse HEAD^^)" = "$(git rev-parse copy)" &&
+ test "$(git rev-parse HEAD^2)" = "$(git rev-parse keep-merge)"
+'
+
+test_expect_success '--rebase=invalid fails' '
+ git reset --hard before-preserve-rebase &&
+ ! git pull --rebase=invalid . copy
+'
+
+test_expect_success '--rebase overrides pull.rebase=preserve and flattens keep-merge' '
+ git reset --hard before-preserve-rebase &&
+ test_config pull.rebase preserve &&
+ git pull --rebase . copy &&
+ test "$(git rev-parse HEAD^^)" = "$(git rev-parse copy)" &&
+ test file3 = "$(git show HEAD:file3.t)"
'
test_expect_success '--rebase with rebased upstream' '
@@ -102,8 +454,16 @@ test_expect_success '--rebase with rebased upstream' '
git tag to-rebase-orig &&
git pull --rebase me copy &&
test "conflicting modification" = "$(cat file)" &&
- test file = $(cat file2)
+ test file = "$(cat file2)"
+
+'
+test_expect_success '--rebase -f with rebased upstream' '
+ test_when_finished "test_might_fail git rebase --abort" &&
+ git reset --hard to-rebase-orig &&
+ git pull --rebase -f me copy &&
+ test "conflicting modification" = "$(cat file)" &&
+ test file = "$(cat file2)"
'
test_expect_success '--rebase with rebased default upstream' '
@@ -113,7 +473,7 @@ test_expect_success '--rebase with rebased default upstream' '
git reset --hard to-rebase-orig &&
git pull --rebase &&
test "conflicting modification" = "$(cat file)" &&
- test file = $(cat file2)
+ test file = "$(cat file2)"
'
@@ -134,18 +494,18 @@ 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) &&
+ COPY="$(git rev-parse --verify me/copy)" &&
git rebase --onto $COPY copy &&
- git config branch.to-rebase.remote me &&
- git config branch.to-rebase.merge refs/heads/copy &&
- git config branch.to-rebase.rebase true &&
+ test_config branch.to-rebase.remote me &&
+ test_config branch.to-rebase.merge refs/heads/copy &&
+ test_config branch.to-rebase.rebase true &&
echo dirty >> file &&
git add file &&
test_must_fail git pull &&
- test $COPY = $(git rev-parse --verify me/copy) &&
+ test "$COPY" = "$(git rev-parse --verify me/copy)" &&
git checkout HEAD -- file &&
git pull &&
- test $COPY != $(git rev-parse --verify me/copy)
+ test "$COPY" != "$(git rev-parse --verify me/copy)"
'
@@ -160,4 +520,83 @@ test_expect_success 'pull --rebase works on branch yet to be born' '
test_cmp expect actual
'
+test_expect_success 'pull --rebase fails on unborn branch with staged changes' '
+ test_when_finished "rm -rf empty_repo2" &&
+ git init empty_repo2 &&
+ (
+ cd empty_repo2 &&
+ echo staged-file >staged-file &&
+ git add staged-file &&
+ test "$(git ls-files)" = staged-file &&
+ test_must_fail git pull --rebase .. master 2>err &&
+ test "$(git ls-files)" = staged-file &&
+ test "$(git show :staged-file)" = staged-file &&
+ test_i18ngrep "unborn branch with changes added to the index" err
+ )
+'
+
+test_expect_success 'setup for detecting upstreamed changes' '
+ mkdir src &&
+ (cd src &&
+ git init &&
+ printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" > stuff &&
+ git add stuff &&
+ git commit -m "Initial revision"
+ ) &&
+ git clone src dst &&
+ (cd src &&
+ modify s/5/43/ stuff &&
+ git commit -a -m "5->43" &&
+ modify s/6/42/ stuff &&
+ git commit -a -m "Make it bigger"
+ ) &&
+ (cd dst &&
+ modify s/5/43/ stuff &&
+ git commit -a -m "Independent discovery of 5->43"
+ )
+'
+
+test_expect_success 'git pull --rebase detects upstreamed changes' '
+ (cd dst &&
+ git pull --rebase &&
+ test -z "$(git ls-files -u)"
+ )
+'
+
+test_expect_success 'setup for avoiding reapplying old patches' '
+ (cd dst &&
+ test_might_fail git rebase --abort &&
+ git reset --hard origin/master
+ ) &&
+ git clone --bare src src-replace.git &&
+ rm -rf src &&
+ mv src-replace.git src &&
+ (cd dst &&
+ modify s/2/22/ stuff &&
+ git commit -a -m "Change 2" &&
+ modify s/3/33/ stuff &&
+ git commit -a -m "Change 3" &&
+ modify s/4/44/ stuff &&
+ git commit -a -m "Change 4" &&
+ git push &&
+
+ modify s/44/55/ stuff &&
+ git commit --amend -a -m "Modified Change 4"
+ )
+'
+
+test_expect_success 'git pull --rebase does not reapply old patches' '
+ (cd dst &&
+ test_must_fail git pull --rebase &&
+ test 1 = $(find .git/rebase-apply -name "000*" | wc -l)
+ )
+'
+
+test_expect_success 'git pull --rebase against local branch' '
+ git checkout -b copy2 to-rebase-orig &&
+ git pull --rebase . to-rebase &&
+ test "conflicting modification" = "$(cat file)" &&
+ test file = "$(cat file2)"
+'
+
test_done
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
index 1b06691..ded8f98 100755
--- a/t/t5521-pull-options.sh
+++ b/t/t5521-pull-options.sh
@@ -15,8 +15,19 @@ test_expect_success 'git pull -q' '
mkdir clonedq &&
(cd clonedq && git init &&
git pull -q "../parent" >out 2>err &&
- test ! -s err &&
- test ! -s out)
+ test_must_be_empty err &&
+ test_must_be_empty out)
+'
+
+test_expect_success 'git pull -q --rebase' '
+ mkdir clonedqrb &&
+ (cd clonedqrb && git init &&
+ git pull -q --rebase "../parent" >out 2>err &&
+ test_must_be_empty err &&
+ test_must_be_empty out &&
+ git pull -q --rebase "../parent" >out 2>err &&
+ test_must_be_empty err &&
+ test_must_be_empty out)
'
test_expect_success 'git pull' '
@@ -24,7 +35,15 @@ test_expect_success 'git pull' '
(cd cloned && git init &&
git pull "../parent" >out 2>err &&
test -s err &&
- test ! -s out)
+ test_must_be_empty out)
+'
+
+test_expect_success 'git pull --rebase' '
+ mkdir clonedrb &&
+ (cd clonedrb && git init &&
+ git pull --rebase "../parent" >out 2>err &&
+ test -s err &&
+ test_must_be_empty out)
'
test_expect_success 'git pull -v' '
@@ -32,22 +51,30 @@ test_expect_success 'git pull -v' '
(cd clonedv && git init &&
git pull -v "../parent" >out 2>err &&
test -s err &&
- test ! -s out)
+ test_must_be_empty out)
+'
+
+test_expect_success 'git pull -v --rebase' '
+ mkdir clonedvrb &&
+ (cd clonedvrb && git init &&
+ git pull -v --rebase "../parent" >out 2>err &&
+ test -s err &&
+ test_must_be_empty out)
'
test_expect_success 'git pull -v -q' '
mkdir clonedvq &&
(cd clonedvq && git init &&
git pull -v -q "../parent" >out 2>err &&
- test ! -s out &&
- test ! -s err)
+ test_must_be_empty out &&
+ test_must_be_empty err)
'
test_expect_success 'git pull -q -v' '
mkdir clonedqv &&
(cd clonedqv && git init &&
git pull -q -v "../parent" >out 2>err &&
- test ! -s out &&
+ test_must_be_empty out &&
test -s err)
'
@@ -90,4 +117,52 @@ test_expect_success 'git pull --all' '
)
'
+test_expect_success 'git pull --dry-run' '
+ test_when_finished "rm -rf clonedry" &&
+ git init clonedry &&
+ (
+ cd clonedry &&
+ git pull --dry-run ../parent &&
+ test_path_is_missing .git/FETCH_HEAD &&
+ test_path_is_missing .git/refs/heads/master &&
+ test_path_is_missing .git/index &&
+ test_path_is_missing file
+ )
+'
+
+test_expect_success 'git pull --all --dry-run' '
+ test_when_finished "rm -rf cloneddry" &&
+ git init clonedry &&
+ (
+ cd clonedry &&
+ git remote add origin ../parent &&
+ git pull --all --dry-run &&
+ test_path_is_missing .git/FETCH_HEAD &&
+ test_path_is_missing .git/refs/remotes/origin/master &&
+ test_path_is_missing .git/index &&
+ test_path_is_missing file
+ )
+'
+
+test_expect_success 'git pull --allow-unrelated-histories' '
+ test_when_finished "rm -fr src dst" &&
+ git init src &&
+ (
+ cd src &&
+ test_commit one &&
+ test_commit two
+ ) &&
+ git clone src dst &&
+ (
+ cd src &&
+ git checkout --orphan side HEAD^ &&
+ test_commit three
+ ) &&
+ (
+ cd dst &&
+ test_must_fail git pull ../src side &&
+ git pull --allow-unrelated-histories ../src side
+ )
+'
+
test_done
diff --git a/t/t5522-pull-symlink.sh b/t/t5522-pull-symlink.sh
index 298200f..bcff460 100755
--- a/t/t5522-pull-symlink.sh
+++ b/t/t5522-pull-symlink.sh
@@ -4,12 +4,6 @@ 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/
@@ -20,7 +14,7 @@ fi
#
# The working directory is subdir-link.
-test_expect_success setup '
+test_expect_success SYMLINKS setup '
mkdir subdir &&
echo file >subdir/file &&
git add subdir/file &&
@@ -36,7 +30,7 @@ test_expect_success setup '
# Demonstrate that things work if we just avoid the symlink
#
-test_expect_success 'pulling from real subdir' '
+test_expect_success SYMLINKS 'pulling from real subdir' '
(
echo real >subdir/file &&
git commit -m real subdir/file &&
@@ -60,11 +54,11 @@ test_expect_success 'pulling from real subdir' '
# 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
+# "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' '
+test_expect_success SYMLINKS 'pulling from symlinked subdir' '
(
echo link >subdir/file &&
git commit -m link subdir/file &&
@@ -77,7 +71,7 @@ test_expect_success 'pulling from symlinked subdir' '
# 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' '
+test_expect_success SYMLINKS 'pushing from symlinked subdir' '
(
cd subdir-link/ &&
echo push >file &&
diff --git a/t/t5523-push-upstream.sh b/t/t5523-push-upstream.sh
index 00da707..3683df1 100755
--- a/t/t5523-push-upstream.sh
+++ b/t/t5523-push-upstream.sh
@@ -2,9 +2,14 @@
test_description='push with --set-upstream'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
+
+ensure_fresh_upstream() {
+ rm -rf parent && git init --bare parent
+}
test_expect_success 'setup bare parent' '
- git init --bare parent &&
+ ensure_fresh_upstream &&
git remote add upstream parent
'
@@ -66,4 +71,49 @@ test_expect_success 'push -u HEAD' '
check_config headbranch upstream refs/heads/headbranch
'
+test_expect_success TTY 'progress messages go to tty' '
+ ensure_fresh_upstream &&
+
+ test_terminal git push -u upstream master >out 2>err &&
+ grep "Writing objects" err
+'
+
+test_expect_success 'progress messages do not go to non-tty' '
+ ensure_fresh_upstream &&
+
+ # skip progress messages, since stderr is non-tty
+ git push -u upstream master >out 2>err &&
+ ! grep "Writing objects" err
+'
+
+test_expect_success 'progress messages go to non-tty (forced)' '
+ ensure_fresh_upstream &&
+
+ # force progress messages to stderr, even though it is non-tty
+ git push -u --progress upstream master >out 2>err &&
+ grep "Writing objects" err
+'
+
+test_expect_success TTY 'push -q suppresses progress' '
+ ensure_fresh_upstream &&
+
+ test_terminal git push -u -q upstream master >out 2>err &&
+ ! grep "Writing objects" err
+'
+
+test_expect_success TTY 'push --no-progress suppresses progress' '
+ ensure_fresh_upstream &&
+
+ test_terminal git push -u --no-progress upstream master >out 2>err &&
+ ! grep "Unpacking objects" err &&
+ ! grep "Writing objects" err
+'
+
+test_expect_success TTY 'quiet push' '
+ ensure_fresh_upstream &&
+
+ test_terminal git push --quiet --no-progress upstream master 2>&1 | tee output &&
+ test_cmp /dev/null output
+'
+
test_done
diff --git a/t/t5524-pull-msg.sh b/t/t5524-pull-msg.sh
index 8cccecc..c278ada 100755
--- a/t/t5524-pull-msg.sh
+++ b/t/t5524-pull-msg.sh
@@ -17,6 +17,9 @@ test_expect_success setup '
git commit -m "add bfile"
) &&
test_tick && test_tick &&
+ echo "second" >afile &&
+ git add afile &&
+ git commit -m "second commit" &&
echo "original $dollar" >afile &&
git add afile &&
git commit -m "do not clobber $dollar signs"
@@ -32,4 +35,18 @@ test_expect_success pull '
)
'
+test_expect_success '--log=1 limits shortlog length' '
+(
+ cd cloned &&
+ git reset --hard HEAD^ &&
+ test "$(cat afile)" = original &&
+ test "$(cat bfile)" = added &&
+ git pull --log=1 &&
+ git log -3 &&
+ git cat-file commit HEAD >result &&
+ grep Dollar result &&
+ ! grep "second commit" result
+)
+'
+
test_done
diff --git a/t/t5525-fetch-tagopt.sh b/t/t5525-fetch-tagopt.sh
new file mode 100755
index 0000000..45815f7
--- /dev/null
+++ b/t/t5525-fetch-tagopt.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+test_description='tagopt variable affects "git fetch" and is overridden by commandline.'
+
+. ./test-lib.sh
+
+setup_clone () {
+ git clone --mirror . $1 &&
+ git remote add remote_$1 $1 &&
+ (cd $1 &&
+ git tag tag_$1 &&
+ git branch branch_$1)
+}
+
+test_expect_success setup '
+ test_commit test &&
+ setup_clone one &&
+ git config remote.remote_one.tagopt --no-tags &&
+ setup_clone two &&
+ git config remote.remote_two.tagopt --tags
+ '
+
+test_expect_success "fetch with tagopt=--no-tags does not get tag" '
+ git fetch remote_one &&
+ test_must_fail git show-ref tag_one &&
+ git show-ref remote_one/branch_one
+ '
+
+test_expect_success "fetch --tags with tagopt=--no-tags gets tag" '
+ (
+ cd one &&
+ git branch second_branch_one
+ ) &&
+ git fetch --tags remote_one &&
+ git show-ref tag_one &&
+ git show-ref remote_one/second_branch_one
+ '
+
+test_expect_success "fetch --no-tags with tagopt=--tags does not get tag" '
+ git fetch --no-tags remote_two &&
+ test_must_fail git show-ref tag_two &&
+ git show-ref remote_two/branch_two
+ '
+
+test_expect_success "fetch with tagopt=--tags gets tag" '
+ (
+ cd two &&
+ git branch second_branch_two
+ ) &&
+ git fetch remote_two &&
+ git show-ref tag_two &&
+ git show-ref remote_two/second_branch_two
+ '
+test_done
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
new file mode 100755
index 0000000..954d0e4
--- /dev/null
+++ b/t/t5526-fetch-submodules.sh
@@ -0,0 +1,488 @@
+#!/bin/sh
+# Copyright (c) 2010, Jens Lehmann
+
+test_description='Recursive "git fetch" for submodules'
+
+. ./test-lib.sh
+
+pwd=$(pwd)
+
+add_upstream_commit() {
+ (
+ cd submodule &&
+ head1=$(git rev-parse --short HEAD) &&
+ echo new >> subfile &&
+ test_tick &&
+ git add subfile &&
+ git commit -m new subfile &&
+ head2=$(git rev-parse --short HEAD) &&
+ echo "Fetching submodule submodule" > ../expect.err &&
+ echo "From $pwd/submodule" >> ../expect.err &&
+ echo " $head1..$head2 master -> origin/master" >> ../expect.err
+ ) &&
+ (
+ cd deepsubmodule &&
+ head1=$(git rev-parse --short HEAD) &&
+ echo new >> deepsubfile &&
+ test_tick &&
+ git add deepsubfile &&
+ git commit -m new deepsubfile &&
+ head2=$(git rev-parse --short HEAD) &&
+ echo "Fetching submodule submodule/subdir/deepsubmodule" >> ../expect.err
+ echo "From $pwd/deepsubmodule" >> ../expect.err &&
+ echo " $head1..$head2 master -> origin/master" >> ../expect.err
+ )
+}
+
+test_expect_success setup '
+ mkdir deepsubmodule &&
+ (
+ cd deepsubmodule &&
+ git init &&
+ echo deepsubcontent > deepsubfile &&
+ git add deepsubfile &&
+ git commit -m new deepsubfile
+ ) &&
+ mkdir submodule &&
+ (
+ cd submodule &&
+ git init &&
+ echo subcontent > subfile &&
+ git add subfile &&
+ git submodule add "$pwd/deepsubmodule" subdir/deepsubmodule &&
+ git commit -a -m new
+ ) &&
+ git submodule add "$pwd/submodule" submodule &&
+ git commit -am initial &&
+ git clone . downstream &&
+ (
+ cd downstream &&
+ git submodule update --init --recursive
+ )
+'
+
+test_expect_success "fetch --recurse-submodules recurses into submodules" '
+ add_upstream_commit &&
+ (
+ cd downstream &&
+ git fetch --recurse-submodules >../actual.out 2>../actual.err
+ ) &&
+ test_must_be_empty actual.out &&
+ test_i18ncmp expect.err actual.err
+'
+
+test_expect_success "fetch --recurse-submodules -j2 has the same output behaviour" '
+ add_upstream_commit &&
+ (
+ cd downstream &&
+ GIT_TRACE=$(pwd)/../trace.out git fetch --recurse-submodules -j2 2>../actual.err
+ ) &&
+ test_must_be_empty actual.out &&
+ test_i18ncmp expect.err actual.err &&
+ grep "2 tasks" trace.out
+'
+
+test_expect_success "fetch alone only fetches superproject" '
+ add_upstream_commit &&
+ (
+ cd downstream &&
+ git fetch >../actual.out 2>../actual.err
+ ) &&
+ ! test -s actual.out &&
+ ! test -s actual.err
+'
+
+test_expect_success "fetch --no-recurse-submodules only fetches superproject" '
+ (
+ cd downstream &&
+ git fetch --no-recurse-submodules >../actual.out 2>../actual.err
+ ) &&
+ ! test -s actual.out &&
+ ! test -s actual.err
+'
+
+test_expect_success "using fetchRecurseSubmodules=true in .gitmodules recurses into submodules" '
+ (
+ cd downstream &&
+ git config -f .gitmodules submodule.submodule.fetchRecurseSubmodules true &&
+ git fetch >../actual.out 2>../actual.err
+ ) &&
+ test_must_be_empty actual.out &&
+ test_i18ncmp expect.err actual.err
+'
+
+test_expect_success "--no-recurse-submodules overrides .gitmodules config" '
+ add_upstream_commit &&
+ (
+ cd downstream &&
+ git fetch --no-recurse-submodules >../actual.out 2>../actual.err
+ ) &&
+ ! test -s actual.out &&
+ ! test -s actual.err
+'
+
+test_expect_success "using fetchRecurseSubmodules=false in .git/config overrides setting in .gitmodules" '
+ (
+ cd downstream &&
+ git config submodule.submodule.fetchRecurseSubmodules false &&
+ git fetch >../actual.out 2>../actual.err
+ ) &&
+ ! test -s actual.out &&
+ ! test -s actual.err
+'
+
+test_expect_success "--recurse-submodules overrides fetchRecurseSubmodules setting from .git/config" '
+ (
+ cd downstream &&
+ git fetch --recurse-submodules >../actual.out 2>../actual.err &&
+ git config --unset -f .gitmodules submodule.submodule.fetchRecurseSubmodules &&
+ git config --unset submodule.submodule.fetchRecurseSubmodules
+ ) &&
+ test_must_be_empty actual.out &&
+ test_i18ncmp expect.err actual.err
+'
+
+test_expect_success "--quiet propagates to submodules" '
+ (
+ cd downstream &&
+ git fetch --recurse-submodules --quiet >../actual.out 2>../actual.err
+ ) &&
+ ! test -s actual.out &&
+ ! test -s actual.err
+'
+
+test_expect_success "--quiet propagates to parallel submodules" '
+ (
+ cd downstream &&
+ git fetch --recurse-submodules -j 2 --quiet >../actual.out 2>../actual.err
+ ) &&
+ ! test -s actual.out &&
+ ! test -s actual.err
+'
+
+test_expect_success "--dry-run propagates to submodules" '
+ add_upstream_commit &&
+ (
+ cd downstream &&
+ git fetch --recurse-submodules --dry-run >../actual.out 2>../actual.err
+ ) &&
+ test_must_be_empty actual.out &&
+ test_i18ncmp expect.err actual.err
+'
+
+test_expect_success "Without --dry-run propagates to submodules" '
+ (
+ cd downstream &&
+ git fetch --recurse-submodules >../actual.out 2>../actual.err
+ ) &&
+ test_must_be_empty actual.out &&
+ test_i18ncmp expect.err actual.err
+'
+
+test_expect_success "recurseSubmodules=true propagates into submodules" '
+ add_upstream_commit &&
+ (
+ cd downstream &&
+ git config fetch.recurseSubmodules true
+ git fetch >../actual.out 2>../actual.err
+ ) &&
+ test_must_be_empty actual.out &&
+ test_i18ncmp expect.err actual.err
+'
+
+test_expect_success "--recurse-submodules overrides config in submodule" '
+ add_upstream_commit &&
+ (
+ cd downstream &&
+ (
+ cd submodule &&
+ git config fetch.recurseSubmodules false
+ ) &&
+ git fetch --recurse-submodules >../actual.out 2>../actual.err
+ ) &&
+ test_must_be_empty actual.out &&
+ test_i18ncmp expect.err actual.err
+'
+
+test_expect_success "--no-recurse-submodules overrides config setting" '
+ add_upstream_commit &&
+ (
+ cd downstream &&
+ git config fetch.recurseSubmodules true
+ git fetch --no-recurse-submodules >../actual.out 2>../actual.err
+ ) &&
+ ! test -s actual.out &&
+ ! test -s actual.err
+'
+
+test_expect_success "Recursion doesn't happen when no new commits are fetched in the superproject" '
+ (
+ cd downstream &&
+ (
+ cd submodule &&
+ git config --unset fetch.recurseSubmodules
+ ) &&
+ git config --unset fetch.recurseSubmodules
+ git fetch >../actual.out 2>../actual.err
+ ) &&
+ ! test -s actual.out &&
+ ! test -s actual.err
+'
+
+test_expect_success "Recursion stops when no new submodule commits are fetched" '
+ head1=$(git rev-parse --short HEAD) &&
+ git add submodule &&
+ git commit -m "new submodule" &&
+ head2=$(git rev-parse --short HEAD) &&
+ echo "From $pwd/." > expect.err.sub &&
+ echo " $head1..$head2 master -> origin/master" >>expect.err.sub &&
+ head -3 expect.err >> expect.err.sub &&
+ (
+ cd downstream &&
+ git fetch >../actual.out 2>../actual.err
+ ) &&
+ test_i18ncmp expect.err.sub actual.err &&
+ test_must_be_empty actual.out
+'
+
+test_expect_success "Recursion doesn't happen when new superproject commits don't change any submodules" '
+ add_upstream_commit &&
+ head1=$(git rev-parse --short HEAD) &&
+ echo a > file &&
+ git add file &&
+ git commit -m "new file" &&
+ head2=$(git rev-parse --short HEAD) &&
+ echo "From $pwd/." > expect.err.file &&
+ echo " $head1..$head2 master -> origin/master" >> expect.err.file &&
+ (
+ cd downstream &&
+ git fetch >../actual.out 2>../actual.err
+ ) &&
+ ! test -s actual.out &&
+ test_i18ncmp expect.err.file actual.err
+'
+
+test_expect_success "Recursion picks up config in submodule" '
+ (
+ cd downstream &&
+ git fetch --recurse-submodules &&
+ (
+ cd submodule &&
+ git config fetch.recurseSubmodules true
+ )
+ ) &&
+ add_upstream_commit &&
+ head1=$(git rev-parse --short HEAD) &&
+ git add submodule &&
+ git commit -m "new submodule" &&
+ head2=$(git rev-parse --short HEAD) &&
+ echo "From $pwd/." > expect.err.sub &&
+ echo " $head1..$head2 master -> origin/master" >> expect.err.sub &&
+ cat expect.err >> expect.err.sub &&
+ (
+ cd downstream &&
+ git fetch >../actual.out 2>../actual.err &&
+ (
+ cd submodule &&
+ git config --unset fetch.recurseSubmodules
+ )
+ ) &&
+ test_i18ncmp expect.err.sub actual.err &&
+ test_must_be_empty actual.out
+'
+
+test_expect_success "Recursion picks up all submodules when necessary" '
+ add_upstream_commit &&
+ (
+ cd submodule &&
+ (
+ cd subdir/deepsubmodule &&
+ git fetch &&
+ git checkout -q FETCH_HEAD
+ ) &&
+ head1=$(git rev-parse --short HEAD^) &&
+ git add subdir/deepsubmodule &&
+ git commit -m "new deepsubmodule"
+ head2=$(git rev-parse --short HEAD) &&
+ echo "Fetching submodule submodule" > ../expect.err.sub &&
+ echo "From $pwd/submodule" >> ../expect.err.sub &&
+ echo " $head1..$head2 master -> origin/master" >> ../expect.err.sub
+ ) &&
+ head1=$(git rev-parse --short HEAD) &&
+ git add submodule &&
+ git commit -m "new submodule" &&
+ head2=$(git rev-parse --short HEAD) &&
+ echo "From $pwd/." > expect.err.2 &&
+ echo " $head1..$head2 master -> origin/master" >> expect.err.2 &&
+ cat expect.err.sub >> expect.err.2 &&
+ tail -3 expect.err >> expect.err.2 &&
+ (
+ cd downstream &&
+ git fetch >../actual.out 2>../actual.err
+ ) &&
+ test_i18ncmp expect.err.2 actual.err &&
+ test_must_be_empty actual.out
+'
+
+test_expect_success "'--recurse-submodules=on-demand' doesn't recurse when no new commits are fetched in the superproject (and ignores config)" '
+ add_upstream_commit &&
+ (
+ cd submodule &&
+ (
+ cd subdir/deepsubmodule &&
+ git fetch &&
+ git checkout -q FETCH_HEAD
+ ) &&
+ head1=$(git rev-parse --short HEAD^) &&
+ git add subdir/deepsubmodule &&
+ git commit -m "new deepsubmodule" &&
+ head2=$(git rev-parse --short HEAD) &&
+ echo Fetching submodule submodule > ../expect.err.sub &&
+ echo "From $pwd/submodule" >> ../expect.err.sub &&
+ echo " $head1..$head2 master -> origin/master" >> ../expect.err.sub
+ ) &&
+ (
+ cd downstream &&
+ git config fetch.recurseSubmodules true &&
+ git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err &&
+ git config --unset fetch.recurseSubmodules
+ ) &&
+ ! test -s actual.out &&
+ ! test -s actual.err
+'
+
+test_expect_success "'--recurse-submodules=on-demand' recurses as deep as necessary (and ignores config)" '
+ head1=$(git rev-parse --short HEAD) &&
+ git add submodule &&
+ git commit -m "new submodule" &&
+ head2=$(git rev-parse --short HEAD) &&
+ tail -3 expect.err > expect.err.deepsub &&
+ echo "From $pwd/." > expect.err &&
+ echo " $head1..$head2 master -> origin/master" >>expect.err &&
+ cat expect.err.sub >> expect.err &&
+ cat expect.err.deepsub >> expect.err &&
+ (
+ cd downstream &&
+ git config fetch.recurseSubmodules false &&
+ (
+ cd submodule &&
+ git config -f .gitmodules submodule.subdir/deepsubmodule.fetchRecursive false
+ ) &&
+ git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err &&
+ git config --unset fetch.recurseSubmodules
+ (
+ cd submodule &&
+ git config --unset -f .gitmodules submodule.subdir/deepsubmodule.fetchRecursive
+ )
+ ) &&
+ test_must_be_empty actual.out &&
+ test_i18ncmp expect.err actual.err
+'
+
+test_expect_success "'--recurse-submodules=on-demand' stops when no new submodule commits are found in the superproject (and ignores config)" '
+ add_upstream_commit &&
+ head1=$(git rev-parse --short HEAD) &&
+ echo a >> file &&
+ git add file &&
+ git commit -m "new file" &&
+ head2=$(git rev-parse --short HEAD) &&
+ echo "From $pwd/." > expect.err.file &&
+ echo " $head1..$head2 master -> origin/master" >> expect.err.file &&
+ (
+ cd downstream &&
+ git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err
+ ) &&
+ ! test -s actual.out &&
+ test_i18ncmp expect.err.file actual.err
+'
+
+test_expect_success "'fetch.recurseSubmodules=on-demand' overrides global config" '
+ (
+ cd downstream &&
+ git fetch --recurse-submodules
+ ) &&
+ add_upstream_commit &&
+ git config --global fetch.recurseSubmodules false &&
+ head1=$(git rev-parse --short HEAD) &&
+ git add submodule &&
+ git commit -m "new submodule" &&
+ head2=$(git rev-parse --short HEAD) &&
+ echo "From $pwd/." > expect.err.2 &&
+ echo " $head1..$head2 master -> origin/master" >>expect.err.2 &&
+ head -3 expect.err >> expect.err.2 &&
+ (
+ cd downstream &&
+ git config fetch.recurseSubmodules on-demand &&
+ git fetch >../actual.out 2>../actual.err
+ ) &&
+ git config --global --unset fetch.recurseSubmodules &&
+ (
+ cd downstream &&
+ git config --unset fetch.recurseSubmodules
+ ) &&
+ test_must_be_empty actual.out &&
+ test_i18ncmp expect.err.2 actual.err
+'
+
+test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' overrides fetch.recurseSubmodules" '
+ (
+ cd downstream &&
+ git fetch --recurse-submodules
+ ) &&
+ add_upstream_commit &&
+ git config fetch.recurseSubmodules false &&
+ head1=$(git rev-parse --short HEAD) &&
+ git add submodule &&
+ git commit -m "new submodule" &&
+ head2=$(git rev-parse --short HEAD) &&
+ echo "From $pwd/." > expect.err.2 &&
+ echo " $head1..$head2 master -> origin/master" >>expect.err.2 &&
+ head -3 expect.err >> expect.err.2 &&
+ (
+ cd downstream &&
+ git config submodule.submodule.fetchRecurseSubmodules on-demand &&
+ git fetch >../actual.out 2>../actual.err
+ ) &&
+ git config --unset fetch.recurseSubmodules &&
+ (
+ cd downstream &&
+ git config --unset submodule.submodule.fetchRecurseSubmodules
+ ) &&
+ test_must_be_empty actual.out &&
+ test_i18ncmp expect.err.2 actual.err
+'
+
+test_expect_success "don't fetch submodule when newly recorded commits are already present" '
+ (
+ cd submodule &&
+ git checkout -q HEAD^^
+ ) &&
+ head1=$(git rev-parse --short HEAD) &&
+ git add submodule &&
+ git commit -m "submodule rewound" &&
+ head2=$(git rev-parse --short HEAD) &&
+ echo "From $pwd/." > expect.err &&
+ echo " $head1..$head2 master -> origin/master" >> expect.err &&
+ (
+ cd downstream &&
+ git fetch >../actual.out 2>../actual.err
+ ) &&
+ ! test -s actual.out &&
+ test_i18ncmp expect.err actual.err
+'
+
+test_expect_success 'fetching submodules respects parallel settings' '
+ git config fetch.recurseSubmodules true &&
+ (
+ cd downstream &&
+ GIT_TRACE=$(pwd)/trace.out git fetch --jobs 7 &&
+ grep "7 tasks" trace.out &&
+ git config submodule.fetchJobs 8 &&
+ GIT_TRACE=$(pwd)/trace.out git fetch &&
+ grep "8 tasks" trace.out &&
+ GIT_TRACE=$(pwd)/trace.out git fetch --jobs 9 &&
+ grep "9 tasks" trace.out
+ )
+'
+
+test_done
diff --git a/t/t5527-fetch-odd-refs.sh b/t/t5527-fetch-odd-refs.sh
new file mode 100755
index 0000000..207899a
--- /dev/null
+++ b/t/t5527-fetch-odd-refs.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+test_description='test fetching of oddly-named refs'
+. ./test-lib.sh
+
+# afterwards we will have:
+# HEAD - two
+# refs/for/refs/heads/master - one
+# refs/heads/master - three
+test_expect_success 'setup repo with odd suffix ref' '
+ echo content >file &&
+ git add . &&
+ git commit -m one &&
+ git update-ref refs/for/refs/heads/master HEAD &&
+ echo content >>file &&
+ git commit -a -m two &&
+ echo content >>file &&
+ git commit -a -m three &&
+ git checkout HEAD^
+'
+
+test_expect_success 'suffix ref is ignored during fetch' '
+ git clone --bare file://"$PWD" suffix &&
+ echo three >expect &&
+ git --git-dir=suffix log -1 --format=%s refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'try to create repo with absurdly long refname' '
+ ref240=$_z40/$_z40/$_z40/$_z40/$_z40/$_z40 &&
+ ref1440=$ref240/$ref240/$ref240/$ref240/$ref240/$ref240 &&
+ git init long &&
+ (
+ cd long &&
+ test_commit long &&
+ test_commit master
+ ) &&
+ if git -C long update-ref refs/heads/$ref1440 long; then
+ test_set_prereq LONG_REF
+ else
+ echo >&2 "long refs not supported"
+ fi
+'
+
+test_expect_success LONG_REF 'fetch handles extremely long refname' '
+ git fetch long refs/heads/*:refs/remotes/long/* &&
+ cat >expect <<-\EOF &&
+ long
+ master
+ EOF
+ git for-each-ref --format="%(subject)" refs/remotes/long >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success LONG_REF 'push handles extremely long refname' '
+ git push long :refs/heads/$ref1440 &&
+ git -C long for-each-ref --format="%(subject)" refs/heads >actual &&
+ echo master >expect &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh
new file mode 100755
index 0000000..73f4bb6
--- /dev/null
+++ b/t/t5528-push-default.sh
@@ -0,0 +1,203 @@
+#!/bin/sh
+
+test_description='check various push.default settings'
+. ./test-lib.sh
+
+test_expect_success 'setup bare remotes' '
+ git init --bare repo1 &&
+ git remote add parent1 repo1 &&
+ git init --bare repo2 &&
+ git remote add parent2 repo2 &&
+ test_commit one &&
+ git push parent1 HEAD &&
+ git push parent2 HEAD
+'
+
+# $1 = local revision
+# $2 = remote revision (tested to be equal to the local one)
+# $3 = [optional] repo to check for actual output (repo1 by default)
+check_pushed_commit () {
+ git log -1 --format='%h %s' "$1" >expect &&
+ git --git-dir="${3:-repo1}" log -1 --format='%h %s' "$2" >actual &&
+ test_cmp expect actual
+}
+
+# $1 = push.default value
+# $2 = expected target branch for the push
+# $3 = [optional] repo to check for actual output (repo1 by default)
+test_push_success () {
+ git ${1:+-c} ${1:+push.default="$1"} push &&
+ check_pushed_commit HEAD "$2" "$3"
+}
+
+# $1 = push.default value
+# check that push fails and does not modify any remote branch
+test_push_failure () {
+ git --git-dir=repo1 log --no-walk --format='%h %s' --all >expect &&
+ test_must_fail git ${1:+-c} ${1:+push.default="$1"} push &&
+ git --git-dir=repo1 log --no-walk --format='%h %s' --all >actual &&
+ test_cmp expect actual
+}
+
+# $1 = success or failure
+# $2 = push.default value
+# $3 = branch to check for actual output (master or foo)
+# $4 = [optional] switch to triangular workflow
+test_pushdefault_workflow () {
+ workflow=central
+ pushdefault=parent1
+ if test -n "${4-}"; then
+ workflow=triangular
+ pushdefault=parent2
+ fi
+ test_expect_success "push.default = $2 $1 in $workflow workflows" "
+ test_config branch.master.remote parent1 &&
+ test_config branch.master.merge refs/heads/foo &&
+ test_config remote.pushdefault $pushdefault &&
+ test_commit commit-for-$2${4+-triangular} &&
+ test_push_$1 $2 $3 ${4+repo2}
+ "
+}
+
+test_expect_success '"upstream" pushes to configured upstream' '
+ git checkout master &&
+ test_config branch.master.remote parent1 &&
+ test_config branch.master.merge refs/heads/foo &&
+ test_commit two &&
+ test_push_success upstream foo
+'
+
+test_expect_success '"upstream" does not push on unconfigured remote' '
+ git checkout master &&
+ test_unconfig branch.master.remote &&
+ test_commit three &&
+ test_push_failure upstream
+'
+
+test_expect_success '"upstream" does not push on unconfigured branch' '
+ git checkout master &&
+ test_config branch.master.remote parent1 &&
+ test_unconfig branch.master.merge &&
+ test_commit four &&
+ test_push_failure upstream
+'
+
+test_expect_success '"upstream" does not push when remotes do not match' '
+ git checkout master &&
+ test_config branch.master.remote parent1 &&
+ test_config branch.master.merge refs/heads/foo &&
+ test_config push.default upstream &&
+ test_commit five &&
+ test_must_fail git push parent2
+'
+
+test_expect_success 'push from/to new branch with upstream, matching and simple' '
+ git checkout -b new-branch &&
+ test_push_failure simple &&
+ test_push_failure matching &&
+ test_push_failure upstream
+'
+
+test_expect_success 'push from/to new branch with current creates remote branch' '
+ test_config branch.new-branch.remote repo1 &&
+ git checkout new-branch &&
+ test_push_success current new-branch
+'
+
+test_expect_success 'push to existing branch, with no upstream configured' '
+ test_config branch.master.remote repo1 &&
+ git checkout master &&
+ test_push_failure simple &&
+ test_push_failure upstream
+'
+
+test_expect_success 'push to existing branch, upstream configured with same name' '
+ test_config branch.master.remote repo1 &&
+ test_config branch.master.merge refs/heads/master &&
+ git checkout master &&
+ test_commit six &&
+ test_push_success upstream master &&
+ test_commit seven &&
+ test_push_success simple master
+'
+
+test_expect_success 'push to existing branch, upstream configured with different name' '
+ test_config branch.master.remote repo1 &&
+ test_config branch.master.merge refs/heads/other-name &&
+ git checkout master &&
+ test_commit eight &&
+ test_push_success upstream other-name &&
+ test_commit nine &&
+ test_push_failure simple &&
+ git --git-dir=repo1 log -1 --format="%h %s" "other-name" >expect-other-name &&
+ test_push_success current master &&
+ git --git-dir=repo1 log -1 --format="%h %s" "other-name" >actual-other-name &&
+ test_cmp expect-other-name actual-other-name
+'
+
+# We are on 'master', which integrates with 'foo' from parent1
+# remote (set in test_pushdefault_workflow helper). Push to
+# parent1 in centralized, and push to parent2 in triangular workflow.
+# The parent1 repository has 'master' and 'foo' branches, while
+# the parent2 repository has only 'master' branch.
+#
+# test_pushdefault_workflow() arguments:
+# $1 = success or failure
+# $2 = push.default value
+# $3 = branch to check for actual output (master or foo)
+# $4 = [optional] switch to triangular workflow
+
+# update parent1's master (which is not our upstream)
+test_pushdefault_workflow success current master
+
+# update parent1's foo (which is our upstream)
+test_pushdefault_workflow success upstream foo
+
+# upsream is foo which is not the name of the current branch
+test_pushdefault_workflow failure simple master
+
+# master and foo are updated
+test_pushdefault_workflow success matching master
+
+# master is updated
+test_pushdefault_workflow success current master triangular
+
+# upstream mode cannot be used in triangular
+test_pushdefault_workflow failure upstream foo triangular
+
+# in triangular, 'simple' works as 'current' and update the branch
+# with the same name.
+test_pushdefault_workflow success simple master triangular
+
+# master is updated (parent2 does not have foo)
+test_pushdefault_workflow success matching master triangular
+
+# default tests, when no push-default is specified. This
+# should behave the same as "simple" in non-triangular
+# settings, and as "current" otherwise.
+
+test_expect_success 'default behavior allows "simple" push' '
+ test_config branch.master.remote parent1 &&
+ test_config branch.master.merge refs/heads/master &&
+ test_config remote.pushdefault parent1 &&
+ test_commit default-master-master &&
+ test_push_success "" master
+'
+
+test_expect_success 'default behavior rejects non-simple push' '
+ test_config branch.master.remote parent1 &&
+ test_config branch.master.merge refs/heads/foo &&
+ test_config remote.pushdefault parent1 &&
+ test_commit default-master-foo &&
+ test_push_failure ""
+'
+
+test_expect_success 'default triangular behavior acts like "current"' '
+ test_config branch.master.remote parent1 &&
+ test_config branch.master.merge refs/heads/foo &&
+ test_config remote.pushdefault parent2 &&
+ test_commit default-triangular &&
+ test_push_success "" master repo2
+'
+
+test_done
diff --git a/t/t5529-push-errors.sh b/t/t5529-push-errors.sh
new file mode 100755
index 0000000..9871307
--- /dev/null
+++ b/t/t5529-push-errors.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='detect some push errors early (before contacting remote)'
+. ./test-lib.sh
+
+test_expect_success 'setup commits' '
+ test_commit one
+'
+
+test_expect_success 'setup remote' '
+ git init --bare remote.git &&
+ git remote add origin remote.git
+'
+
+test_expect_success 'setup fake receive-pack' '
+ FAKE_RP_ROOT=$(pwd) &&
+ export FAKE_RP_ROOT &&
+ write_script fake-rp <<-\EOF &&
+ echo yes >"$FAKE_RP_ROOT"/rp-ran
+ exit 1
+ EOF
+ git config remote.origin.receivepack "\"\$FAKE_RP_ROOT/fake-rp\""
+'
+
+test_expect_success 'detect missing branches early' '
+ echo no >rp-ran &&
+ echo no >expect &&
+ test_must_fail git push origin missing &&
+ test_cmp expect rp-ran
+'
+
+test_expect_success 'detect missing sha1 expressions early' '
+ echo no >rp-ran &&
+ echo no >expect &&
+ test_must_fail git push origin master~2:master &&
+ test_cmp expect rp-ran
+'
+
+test_expect_success 'detect ambiguous refs early' '
+ git branch foo &&
+ git tag foo &&
+ echo no >rp-ran &&
+ echo no >expect &&
+ test_must_fail git push origin foo &&
+ test_cmp expect rp-ran
+'
+
+test_done
diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh
index 044603c..4f6e32b 100755
--- a/t/t5530-upload-pack-error.sh
+++ b/t/t5530-upload-pack-error.sh
@@ -4,7 +4,7 @@ test_description='errors in upload-pack'
. ./test-lib.sh
-D=`pwd`
+D=$(pwd)
corrupt_repo () {
object_sha1=$(git rev-parse "$1") &&
@@ -35,8 +35,8 @@ test_expect_success 'upload-pack fails due to error in pack-objects packing' '
printf "0032want %s\n00000009done\n0000" \
$(git rev-parse HEAD) >input &&
test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
- grep "unable to read" output.err &&
- grep "pack-objects died" output.err
+ test_i18ngrep "unable to read" output.err &&
+ test_i18ngrep "pack-objects died" output.err
'
test_expect_success 'corrupt repo differently' '
@@ -54,12 +54,18 @@ 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 error message when bad ref requested' '
+
+ printf "0045want %s multi_ack_detailed\n00000009done\n0000" \
+ "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" >input &&
+ test_must_fail git upload-pack . <input >output 2>output.err &&
+ grep -q "not our ref" output.err &&
+ ! grep -q multi_ack_detailed output.err
+'
+
test_expect_success 'upload-pack fails due to error in pack-objects enumeration' '
printf "0032want %s\n00000009done\n0000" \
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
index 65d8d47..198ce84 100755
--- a/t/t5531-deep-submodule-push.sh
+++ b/t/t5531-deep-submodule-push.sh
@@ -6,16 +6,18 @@ test_description='unpack-objects'
test_expect_success setup '
mkdir pub.git &&
- GIT_DIR=pub.git git init --bare
+ GIT_DIR=pub.git git init --bare &&
GIT_DIR=pub.git git config receive.fsckobjects true &&
mkdir work &&
(
cd work &&
git init &&
+ git config push.default matching &&
mkdir -p gar/bage &&
(
cd gar/bage &&
git init &&
+ git config push.default matching &&
>junk &&
git add junk &&
git commit -m "Initial junk"
@@ -32,4 +34,400 @@ test_expect_success push '
)
'
+test_expect_success 'push if submodule has no remote' '
+ (
+ cd work/gar/bage &&
+ >junk2 &&
+ git add junk2 &&
+ git commit -m "Second junk"
+ ) &&
+ (
+ cd work &&
+ git add gar/bage &&
+ git commit -m "Second commit for gar/bage" &&
+ git push --recurse-submodules=check ../pub.git master
+ )
+'
+
+test_expect_success 'push fails if submodule commit not on remote' '
+ (
+ cd work/gar &&
+ git clone --bare bage ../../submodule.git &&
+ cd bage &&
+ git remote add origin ../../../submodule.git &&
+ git fetch &&
+ >junk3 &&
+ git add junk3 &&
+ git commit -m "Third junk"
+ ) &&
+ (
+ cd work &&
+ git add gar/bage &&
+ git commit -m "Third commit for gar/bage" &&
+ # the push should fail with --recurse-submodules=check
+ # on the command line...
+ test_must_fail git push --recurse-submodules=check ../pub.git master &&
+
+ # ...or if specified in the configuration..
+ test_must_fail git -c push.recurseSubmodules=check push ../pub.git master
+ )
+'
+
+test_expect_success 'push succeeds after commit was pushed to remote' '
+ (
+ cd work/gar/bage &&
+ git push origin master
+ ) &&
+ (
+ cd work &&
+ git push --recurse-submodules=check ../pub.git master
+ )
+'
+
+test_expect_success 'push succeeds if submodule commit not on remote but using on-demand on command line' '
+ (
+ cd work/gar/bage &&
+ >recurse-on-demand-on-command-line &&
+ git add recurse-on-demand-on-command-line &&
+ git commit -m "Recurse on-demand on command line junk"
+ ) &&
+ (
+ cd work &&
+ git add gar/bage &&
+ git commit -m "Recurse on-demand on command line for gar/bage" &&
+ git push --recurse-submodules=on-demand ../pub.git master &&
+ # Check that the supermodule commit got there
+ git fetch ../pub.git &&
+ git diff --quiet FETCH_HEAD master &&
+ # Check that the submodule commit got there too
+ cd gar/bage &&
+ git diff --quiet origin/master master
+ )
+'
+
+test_expect_success 'push succeeds if submodule commit not on remote but using on-demand from config' '
+ (
+ cd work/gar/bage &&
+ >recurse-on-demand-from-config &&
+ git add recurse-on-demand-from-config &&
+ git commit -m "Recurse on-demand from config junk"
+ ) &&
+ (
+ cd work &&
+ git add gar/bage &&
+ git commit -m "Recurse on-demand from config for gar/bage" &&
+ git -c push.recurseSubmodules=on-demand push ../pub.git master &&
+ # Check that the supermodule commit got there
+ git fetch ../pub.git &&
+ git diff --quiet FETCH_HEAD master &&
+ # Check that the submodule commit got there too
+ cd gar/bage &&
+ git diff --quiet origin/master master
+ )
+'
+
+test_expect_success 'push recurse-submodules on command line overrides config' '
+ (
+ cd work/gar/bage &&
+ >recurse-check-on-command-line-overriding-config &&
+ git add recurse-check-on-command-line-overriding-config &&
+ git commit -m "Recurse on command-line overriding config junk"
+ ) &&
+ (
+ cd work &&
+ git add gar/bage &&
+ git commit -m "Recurse on command-line overriding config for gar/bage" &&
+
+ # Ensure that we can override on-demand in the config
+ # to just check submodules
+ test_must_fail git -c push.recurseSubmodules=on-demand push --recurse-submodules=check ../pub.git master &&
+ # Check that the supermodule commit did not get there
+ git fetch ../pub.git &&
+ git diff --quiet FETCH_HEAD master^ &&
+ # Check that the submodule commit did not get there
+ (cd gar/bage && git diff --quiet origin/master master^) &&
+
+ # Ensure that we can override check in the config to
+ # disable submodule recursion entirely
+ (cd gar/bage && git diff --quiet origin/master master^) &&
+ git -c push.recurseSubmodules=on-demand push --recurse-submodules=no ../pub.git master &&
+ git fetch ../pub.git &&
+ git diff --quiet FETCH_HEAD master &&
+ (cd gar/bage && git diff --quiet origin/master master^) &&
+
+ # Ensure that we can override check in the config to
+ # disable submodule recursion entirely (alternative form)
+ git -c push.recurseSubmodules=on-demand push --no-recurse-submodules ../pub.git master &&
+ git fetch ../pub.git &&
+ git diff --quiet FETCH_HEAD master &&
+ (cd gar/bage && git diff --quiet origin/master master^) &&
+
+ # Ensure that we can override check in the config to
+ # push the submodule too
+ git -c push.recurseSubmodules=check push --recurse-submodules=on-demand ../pub.git master &&
+ git fetch ../pub.git &&
+ git diff --quiet FETCH_HEAD master &&
+ (cd gar/bage && git diff --quiet origin/master master)
+ )
+'
+
+test_expect_success 'push recurse-submodules last one wins on command line' '
+ (
+ cd work/gar/bage &&
+ >recurse-check-on-command-line-overriding-earlier-command-line &&
+ git add recurse-check-on-command-line-overriding-earlier-command-line &&
+ git commit -m "Recurse on command-line overridiing earlier command-line junk"
+ ) &&
+ (
+ cd work &&
+ git add gar/bage &&
+ git commit -m "Recurse on command-line overriding earlier command-line for gar/bage" &&
+
+ # should result in "check"
+ test_must_fail git push --recurse-submodules=on-demand --recurse-submodules=check ../pub.git master &&
+ # Check that the supermodule commit did not get there
+ git fetch ../pub.git &&
+ git diff --quiet FETCH_HEAD master^ &&
+ # Check that the submodule commit did not get there
+ (cd gar/bage && git diff --quiet origin/master master^) &&
+
+ # should result in "no"
+ git push --recurse-submodules=on-demand --recurse-submodules=no ../pub.git master &&
+ # Check that the supermodule commit did get there
+ git fetch ../pub.git &&
+ git diff --quiet FETCH_HEAD master &&
+ # Check that the submodule commit did not get there
+ (cd gar/bage && git diff --quiet origin/master master^) &&
+
+ # should result in "no"
+ git push --recurse-submodules=on-demand --no-recurse-submodules ../pub.git master &&
+ # Check that the submodule commit did not get there
+ (cd gar/bage && git diff --quiet origin/master master^) &&
+
+ # But the options in the other order should push the submodule
+ git push --recurse-submodules=check --recurse-submodules=on-demand ../pub.git master &&
+ # Check that the submodule commit did get there
+ git fetch ../pub.git &&
+ (cd gar/bage && git diff --quiet origin/master master)
+ )
+'
+
+test_expect_success 'push succeeds if submodule commit not on remote using on-demand from cmdline overriding config' '
+ (
+ cd work/gar/bage &&
+ >recurse-on-demand-on-command-line-overriding-config &&
+ git add recurse-on-demand-on-command-line-overriding-config &&
+ git commit -m "Recurse on-demand on command-line overriding config junk"
+ ) &&
+ (
+ cd work &&
+ git add gar/bage &&
+ git commit -m "Recurse on-demand on command-line overriding config for gar/bage" &&
+ git -c push.recurseSubmodules=check push --recurse-submodules=on-demand ../pub.git master &&
+ # Check that the supermodule commit got there
+ git fetch ../pub.git &&
+ git diff --quiet FETCH_HEAD master &&
+ # Check that the submodule commit got there
+ cd gar/bage &&
+ git diff --quiet origin/master master
+ )
+'
+
+test_expect_success 'push succeeds if submodule commit disabling recursion from cmdline overriding config' '
+ (
+ cd work/gar/bage &&
+ >recurse-disable-on-command-line-overriding-config &&
+ git add recurse-disable-on-command-line-overriding-config &&
+ git commit -m "Recurse disable on command-line overriding config junk"
+ ) &&
+ (
+ cd work &&
+ git add gar/bage &&
+ git commit -m "Recurse disable on command-line overriding config for gar/bage" &&
+ git -c push.recurseSubmodules=check push --recurse-submodules=no ../pub.git master &&
+ # Check that the supermodule commit got there
+ git fetch ../pub.git &&
+ git diff --quiet FETCH_HEAD master &&
+ # But that the submodule commit did not
+ ( cd gar/bage && git diff --quiet origin/master master^ ) &&
+ # Now push it to avoid confusing future tests
+ git push --recurse-submodules=on-demand ../pub.git master
+ )
+'
+
+test_expect_success 'push succeeds if submodule commit disabling recursion from cmdline (alternative form) overriding config' '
+ (
+ cd work/gar/bage &&
+ >recurse-disable-on-command-line-alt-overriding-config &&
+ git add recurse-disable-on-command-line-alt-overriding-config &&
+ git commit -m "Recurse disable on command-line alternative overriding config junk"
+ ) &&
+ (
+ cd work &&
+ git add gar/bage &&
+ git commit -m "Recurse disable on command-line alternative overriding config for gar/bage" &&
+ git -c push.recurseSubmodules=check push --no-recurse-submodules ../pub.git master &&
+ # Check that the supermodule commit got there
+ git fetch ../pub.git &&
+ git diff --quiet FETCH_HEAD master &&
+ # But that the submodule commit did not
+ ( cd gar/bage && git diff --quiet origin/master master^ ) &&
+ # Now push it to avoid confusing future tests
+ git push --recurse-submodules=on-demand ../pub.git master
+ )
+'
+
+test_expect_success 'push fails if recurse submodules option passed as yes' '
+ (
+ cd work/gar/bage &&
+ >recurse-push-fails-if-recurse-submodules-passed-as-yes &&
+ git add recurse-push-fails-if-recurse-submodules-passed-as-yes &&
+ git commit -m "Recurse push fails if recurse submodules option passed as yes"
+ ) &&
+ (
+ cd work &&
+ git add gar/bage &&
+ git commit -m "Recurse push fails if recurse submodules option passed as yes for gar/bage" &&
+ test_must_fail git push --recurse-submodules=yes ../pub.git master &&
+ test_must_fail git -c push.recurseSubmodules=yes push ../pub.git master &&
+ git push --recurse-submodules=on-demand ../pub.git master
+ )
+'
+
+test_expect_success 'push fails when commit on multiple branches if one branch has no remote' '
+ (
+ cd work/gar/bage &&
+ >junk4 &&
+ git add junk4 &&
+ git commit -m "Fourth junk"
+ ) &&
+ (
+ cd work &&
+ git branch branch2 &&
+ git add gar/bage &&
+ git commit -m "Fourth commit for gar/bage" &&
+ git checkout branch2 &&
+ (
+ cd gar/bage &&
+ git checkout HEAD~1
+ ) &&
+ >junk1 &&
+ git add junk1 &&
+ git commit -m "First junk" &&
+ test_must_fail git push --recurse-submodules=check ../pub.git
+ )
+'
+
+test_expect_success 'push succeeds if submodule has no remote and is on the first superproject commit' '
+ git init --bare a &&
+ git clone a a1 &&
+ (
+ cd a1 &&
+ git init b
+ (
+ cd b &&
+ >junk &&
+ git add junk &&
+ git commit -m "initial"
+ ) &&
+ git add b &&
+ git commit -m "added submodule" &&
+ git push --recurse-submodule=check origin master
+ )
+'
+
+test_expect_success 'push unpushed submodules when not needed' '
+ (
+ cd work &&
+ (
+ cd gar/bage &&
+ git checkout master &&
+ >junk5 &&
+ git add junk5 &&
+ git commit -m "Fifth junk" &&
+ git push &&
+ git rev-parse origin/master >../../../expected
+ ) &&
+ git checkout master &&
+ git add gar/bage &&
+ git commit -m "Fifth commit for gar/bage" &&
+ git push --recurse-submodules=on-demand ../pub.git master
+ ) &&
+ (
+ cd submodule.git &&
+ git rev-parse master >../actual
+ ) &&
+ test_cmp expected actual
+'
+
+test_expect_success 'push unpushed submodules when not needed 2' '
+ (
+ cd submodule.git &&
+ git rev-parse master >../expected
+ ) &&
+ (
+ cd work &&
+ (
+ cd gar/bage &&
+ >junk6 &&
+ git add junk6 &&
+ git commit -m "Sixth junk"
+ ) &&
+ >junk2 &&
+ git add junk2 &&
+ git commit -m "Second junk for work" &&
+ git push --recurse-submodules=on-demand ../pub.git master
+ ) &&
+ (
+ cd submodule.git &&
+ git rev-parse master >../actual
+ ) &&
+ test_cmp expected actual
+'
+
+test_expect_success 'push unpushed submodules recursively' '
+ (
+ cd work &&
+ (
+ cd gar/bage &&
+ git checkout master &&
+ > junk7 &&
+ git add junk7 &&
+ git commit -m "Seventh junk" &&
+ git rev-parse master >../../../expected
+ ) &&
+ git checkout master &&
+ git add gar/bage &&
+ git commit -m "Seventh commit for gar/bage" &&
+ git push --recurse-submodules=on-demand ../pub.git master
+ ) &&
+ (
+ cd submodule.git &&
+ git rev-parse master >../actual
+ ) &&
+ test_cmp expected actual
+'
+
+test_expect_success 'push unpushable submodule recursively fails' '
+ (
+ cd work &&
+ (
+ cd gar/bage &&
+ git rev-parse origin/master >../../../expected &&
+ git checkout master~0 &&
+ > junk8 &&
+ git add junk8 &&
+ git commit -m "Eighth junk"
+ ) &&
+ git add gar/bage &&
+ git commit -m "Eighth commit for gar/bage" &&
+ test_must_fail git push --recurse-submodules=on-demand ../pub.git master
+ ) &&
+ (
+ cd submodule.git &&
+ git rev-parse master >../actual
+ ) &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh
new file mode 100755
index 0000000..51c9669
--- /dev/null
+++ b/t/t5532-fetch-proxy.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='fetching via git:// using core.gitproxy'
+. ./test-lib.sh
+
+test_expect_success 'setup remote repo' '
+ git init remote &&
+ (cd remote &&
+ echo content >file &&
+ git add file &&
+ git commit -m one
+ )
+'
+
+test_expect_success 'setup proxy script' '
+ write_script proxy-get-cmd "$PERL_PATH" <<-\EOF &&
+ read(STDIN, $buf, 4);
+ my $n = hex($buf) - 4;
+ read(STDIN, $buf, $n);
+ my ($cmd, $other) = split /\0/, $buf;
+ # drop absolute-path on repo name
+ $cmd =~ s{ /}{ };
+ print $cmd;
+ EOF
+
+ write_script proxy <<-\EOF
+ echo >&2 "proxying for $*"
+ cmd=$(./proxy-get-cmd)
+ echo >&2 "Running $cmd"
+ exec $cmd
+ EOF
+'
+
+test_expect_success 'setup local repo' '
+ git remote add fake git://example.com/remote &&
+ git config core.gitproxy ./proxy
+'
+
+test_expect_success 'fetch through proxy works' '
+ git fetch fake &&
+ echo one >expect &&
+ git log -1 --format=%s FETCH_HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t5533-push-cas.sh b/t/t5533-push-cas.sh
new file mode 100755
index 0000000..c732012
--- /dev/null
+++ b/t/t5533-push-cas.sh
@@ -0,0 +1,194 @@
+#!/bin/sh
+
+test_description='compare & swap push force/delete safety'
+
+. ./test-lib.sh
+
+setup_srcdst_basic () {
+ rm -fr src dst &&
+ git clone --no-local . src &&
+ git clone --no-local src dst &&
+ (
+ cd src && git checkout HEAD^0
+ )
+}
+
+test_expect_success setup '
+ # create template repository
+ test_commit A &&
+ test_commit B &&
+ test_commit C
+'
+
+test_expect_success 'push to update (protected)' '
+ setup_srcdst_basic &&
+ (
+ cd dst &&
+ test_commit D &&
+ test_must_fail git push --force-with-lease=master:master origin master 2>err &&
+ grep "stale info" err
+ ) &&
+ git ls-remote . refs/heads/master >expect &&
+ git ls-remote src refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push to update (protected, forced)' '
+ setup_srcdst_basic &&
+ (
+ cd dst &&
+ test_commit D &&
+ git push --force --force-with-lease=master:master origin master 2>err &&
+ grep "forced update" err
+ ) &&
+ git ls-remote dst refs/heads/master >expect &&
+ git ls-remote src refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push to update (protected, tracking)' '
+ setup_srcdst_basic &&
+ (
+ cd src &&
+ git checkout master &&
+ test_commit D &&
+ git checkout HEAD^0
+ ) &&
+ git ls-remote src refs/heads/master >expect &&
+ (
+ cd dst &&
+ test_commit E &&
+ git ls-remote . refs/remotes/origin/master >expect &&
+ test_must_fail git push --force-with-lease=master origin master &&
+ git ls-remote . refs/remotes/origin/master >actual &&
+ test_cmp expect actual
+ ) &&
+ git ls-remote src refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push to update (protected, tracking, forced)' '
+ setup_srcdst_basic &&
+ (
+ cd src &&
+ git checkout master &&
+ test_commit D &&
+ git checkout HEAD^0
+ ) &&
+ (
+ cd dst &&
+ test_commit E &&
+ git ls-remote . refs/remotes/origin/master >expect &&
+ git push --force --force-with-lease=master origin master
+ ) &&
+ git ls-remote dst refs/heads/master >expect &&
+ git ls-remote src refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push to update (allowed)' '
+ setup_srcdst_basic &&
+ (
+ cd dst &&
+ test_commit D &&
+ git push --force-with-lease=master:master^ origin master
+ ) &&
+ git ls-remote dst refs/heads/master >expect &&
+ git ls-remote src refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push to update (allowed, tracking)' '
+ setup_srcdst_basic &&
+ (
+ cd dst &&
+ test_commit D &&
+ git push --force-with-lease=master origin master 2>err &&
+ ! grep "forced update" err
+ ) &&
+ git ls-remote dst refs/heads/master >expect &&
+ git ls-remote src refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push to update (allowed even though no-ff)' '
+ setup_srcdst_basic &&
+ (
+ cd dst &&
+ git reset --hard HEAD^ &&
+ test_commit D &&
+ git push --force-with-lease=master origin master 2>err &&
+ grep "forced update" err
+ ) &&
+ git ls-remote dst refs/heads/master >expect &&
+ git ls-remote src refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push to delete (protected)' '
+ setup_srcdst_basic &&
+ git ls-remote src refs/heads/master >expect &&
+ (
+ cd dst &&
+ test_must_fail git push --force-with-lease=master:master^ origin :master
+ ) &&
+ git ls-remote src refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push to delete (protected, forced)' '
+ setup_srcdst_basic &&
+ (
+ cd dst &&
+ git push --force --force-with-lease=master:master^ origin :master
+ ) &&
+ >expect &&
+ git ls-remote src refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push to delete (allowed)' '
+ setup_srcdst_basic &&
+ (
+ cd dst &&
+ git push --force-with-lease=master origin :master 2>err &&
+ grep deleted err
+ ) &&
+ >expect &&
+ git ls-remote src refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cover everything with default force-with-lease (protected)' '
+ setup_srcdst_basic &&
+ (
+ cd src &&
+ git branch naster master^
+ ) &&
+ git ls-remote src refs/heads/\* >expect &&
+ (
+ cd dst &&
+ test_must_fail git push --force-with-lease origin master master:naster
+ ) &&
+ git ls-remote src refs/heads/\* >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cover everything with default force-with-lease (allowed)' '
+ setup_srcdst_basic &&
+ (
+ cd src &&
+ git branch naster master^
+ ) &&
+ (
+ cd dst &&
+ git fetch &&
+ git push --force-with-lease origin master master:naster
+ ) &&
+ git ls-remote dst refs/heads/master |
+ sed -e "s/master/naster/" >expect &&
+ git ls-remote src refs/heads/naster >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh
new file mode 100755
index 0000000..ecb8d44
--- /dev/null
+++ b/t/t5534-push-signed.sh
@@ -0,0 +1,171 @@
+#!/bin/sh
+
+test_description='signed push'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
+
+prepare_dst () {
+ rm -fr dst &&
+ test_create_repo dst &&
+
+ git push dst master:noop master:ff master:noff
+}
+
+test_expect_success setup '
+ # master, ff and noff branches pointing at the same commit
+ test_tick &&
+ git commit --allow-empty -m initial &&
+
+ git checkout -b noop &&
+ git checkout -b ff &&
+ git checkout -b noff &&
+
+ # noop stays the same, ff advances, noff rewrites
+ test_tick &&
+ git commit --allow-empty --amend -m rewritten &&
+ git checkout ff &&
+
+ test_tick &&
+ git commit --allow-empty -m second
+'
+
+test_expect_success 'unsigned push does not send push certificate' '
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ write_script dst/.git/hooks/post-receive <<-\EOF &&
+ # discard the update list
+ cat >/dev/null
+ # record the push certificate
+ if test -n "${GIT_PUSH_CERT-}"
+ then
+ git cat-file blob $GIT_PUSH_CERT >../push-cert
+ fi
+ EOF
+
+ git push dst noop ff +noff &&
+ ! test -f dst/push-cert
+'
+
+test_expect_success 'talking with a receiver without push certificate support' '
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ write_script dst/.git/hooks/post-receive <<-\EOF &&
+ # discard the update list
+ cat >/dev/null
+ # record the push certificate
+ if test -n "${GIT_PUSH_CERT-}"
+ then
+ git cat-file blob $GIT_PUSH_CERT >../push-cert
+ fi
+ EOF
+
+ git push dst noop ff +noff &&
+ ! test -f dst/push-cert
+'
+
+test_expect_success 'push --signed fails with a receiver without push certificate support' '
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ test_must_fail git push --signed dst noop ff +noff 2>err &&
+ test_i18ngrep "the receiving end does not support" err
+'
+
+test_expect_success GPG 'no certificate for a signed push with no update' '
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ write_script dst/.git/hooks/post-receive <<-\EOF &&
+ if test -n "${GIT_PUSH_CERT-}"
+ then
+ git cat-file blob $GIT_PUSH_CERT >../push-cert
+ fi
+ EOF
+ git push dst noop &&
+ ! test -f dst/push-cert
+'
+
+test_expect_success GPG 'signed push sends push certificate' '
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ git -C dst config receive.certnonceseed sekrit &&
+ write_script dst/.git/hooks/post-receive <<-\EOF &&
+ # discard the update list
+ cat >/dev/null
+ # record the push certificate
+ if test -n "${GIT_PUSH_CERT-}"
+ then
+ git cat-file blob $GIT_PUSH_CERT >../push-cert
+ fi &&
+
+ cat >../push-cert-status <<E_O_F
+ SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
+ KEY=${GIT_PUSH_CERT_KEY-nokey}
+ STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
+ NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
+ NONCE=${GIT_PUSH_CERT_NONCE-nononce}
+ E_O_F
+
+ EOF
+
+ git push --signed dst noop ff +noff &&
+
+ (
+ cat <<-\EOF &&
+ SIGNER=C O Mitter <committer@example.com>
+ KEY=13B6F51ECDDE430D
+ STATUS=G
+ NONCE_STATUS=OK
+ EOF
+ sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
+ ) >expect &&
+
+ grep "$(git rev-parse noop ff) refs/heads/ff" dst/push-cert &&
+ grep "$(git rev-parse noop noff) refs/heads/noff" dst/push-cert &&
+ test_cmp expect dst/push-cert-status
+'
+
+test_expect_success GPG 'fail without key and heed user.signingkey' '
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ git -C dst config receive.certnonceseed sekrit &&
+ write_script dst/.git/hooks/post-receive <<-\EOF &&
+ # discard the update list
+ cat >/dev/null
+ # record the push certificate
+ if test -n "${GIT_PUSH_CERT-}"
+ then
+ git cat-file blob $GIT_PUSH_CERT >../push-cert
+ fi &&
+
+ cat >../push-cert-status <<E_O_F
+ SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
+ KEY=${GIT_PUSH_CERT_KEY-nokey}
+ STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
+ NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
+ NONCE=${GIT_PUSH_CERT_NONCE-nononce}
+ E_O_F
+
+ EOF
+
+ unset GIT_COMMITTER_EMAIL &&
+ git config user.email hasnokey@nowhere.com &&
+ test_must_fail git push --signed dst noop ff +noff &&
+ git config user.signingkey committer@example.com &&
+ git push --signed dst noop ff +noff &&
+
+ (
+ cat <<-\EOF &&
+ SIGNER=C O Mitter <committer@example.com>
+ KEY=13B6F51ECDDE430D
+ STATUS=G
+ NONCE_STATUS=OK
+ EOF
+ sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
+ ) >expect &&
+
+ grep "$(git rev-parse noop ff) refs/heads/ff" dst/push-cert &&
+ grep "$(git rev-parse noop noff) refs/heads/noff" dst/push-cert &&
+ test_cmp expect dst/push-cert-status
+'
+
+test_done
diff --git a/t/t5535-fetch-push-symref.sh b/t/t5535-fetch-push-symref.sh
new file mode 100755
index 0000000..8ed58d2
--- /dev/null
+++ b/t/t5535-fetch-push-symref.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='avoiding conflicting update thru symref aliasing'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit one &&
+ git clone . src &&
+ git clone src dst1 &&
+ git clone src dst2 &&
+ test_commit two &&
+ ( cd src && git pull )
+'
+
+test_expect_success 'push' '
+ (
+ cd src &&
+ git push ../dst1 "refs/remotes/*:refs/remotes/*"
+ ) &&
+ git ls-remote src "refs/remotes/*" >expect &&
+ git ls-remote dst1 "refs/remotes/*" >actual &&
+ test_cmp expect actual &&
+ ( cd src && git symbolic-ref refs/remotes/origin/HEAD ) >expect &&
+ ( cd dst1 && git symbolic-ref refs/remotes/origin/HEAD ) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fetch' '
+ (
+ cd dst2 &&
+ git fetch ../src "refs/remotes/*:refs/remotes/*"
+ ) &&
+ git ls-remote src "refs/remotes/*" >expect &&
+ git ls-remote dst2 "refs/remotes/*" >actual &&
+ test_cmp expect actual &&
+ ( cd src && git symbolic-ref refs/remotes/origin/HEAD ) >expect &&
+ ( cd dst2 && git symbolic-ref refs/remotes/origin/HEAD ) >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t5536-fetch-conflicts.sh b/t/t5536-fetch-conflicts.sh
new file mode 100755
index 0000000..6c5d3a4
--- /dev/null
+++ b/t/t5536-fetch-conflicts.sh
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+test_description='fetch handles conflicting refspecs correctly'
+
+. ./test-lib.sh
+
+D=$(pwd)
+
+setup_repository () {
+ git init "$1" && (
+ cd "$1" &&
+ git config remote.origin.url "$D" &&
+ shift &&
+ for refspec in "$@"
+ do
+ git config --add remote.origin.fetch "$refspec"
+ done
+ )
+}
+
+verify_stderr () {
+ cat >expected &&
+ # We're not interested in the error
+ # "fatal: The remote end hung up unexpectedly":
+ grep -E '^(fatal|warning):' <error | grep -v 'hung up' >actual | sort &&
+ test_cmp expected actual
+}
+
+test_expect_success 'setup' '
+ git commit --allow-empty -m "Initial" &&
+ git branch branch1 &&
+ git tag tag1 &&
+ git commit --allow-empty -m "First" &&
+ git branch branch2 &&
+ git tag tag2
+'
+
+test_expect_success 'fetch with no conflict' '
+ setup_repository ok "+refs/heads/*:refs/remotes/origin/*" && (
+ cd ok &&
+ git fetch origin
+ )
+'
+
+test_expect_success 'fetch conflict: config vs. config' '
+ setup_repository ccc \
+ "+refs/heads/branch1:refs/remotes/origin/branch1" \
+ "+refs/heads/branch2:refs/remotes/origin/branch1" && (
+ cd ccc &&
+ test_must_fail git fetch origin 2>error &&
+ verify_stderr <<-\EOF
+ fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1
+ EOF
+ )
+'
+
+test_expect_success 'fetch duplicate: config vs. config' '
+ setup_repository dcc \
+ "+refs/heads/*:refs/remotes/origin/*" \
+ "+refs/heads/branch1:refs/remotes/origin/branch1" && (
+ cd dcc &&
+ git fetch origin
+ )
+'
+
+test_expect_success 'fetch conflict: arg overrides config' '
+ setup_repository aoc \
+ "+refs/heads/*:refs/remotes/origin/*" && (
+ cd aoc &&
+ git fetch origin refs/heads/branch2:refs/remotes/origin/branch1
+ )
+'
+
+test_expect_success 'fetch conflict: arg vs. arg' '
+ setup_repository caa && (
+ cd caa &&
+ test_must_fail git fetch origin \
+ refs/heads/*:refs/remotes/origin/* \
+ refs/heads/branch2:refs/remotes/origin/branch1 2>error &&
+ verify_stderr <<-\EOF
+ fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1
+ EOF
+ )
+'
+
+test_expect_success 'fetch conflict: criss-cross args' '
+ setup_repository xaa \
+ "+refs/heads/*:refs/remotes/origin/*" && (
+ cd xaa &&
+ git fetch origin \
+ refs/heads/branch1:refs/remotes/origin/branch2 \
+ refs/heads/branch2:refs/remotes/origin/branch1 2>error &&
+ verify_stderr <<-\EOF
+ warning: refs/remotes/origin/branch1 usually tracks refs/heads/branch1, not refs/heads/branch2
+ warning: refs/remotes/origin/branch2 usually tracks refs/heads/branch2, not refs/heads/branch1
+ EOF
+ )
+'
+
+test_done
diff --git a/t/t5537-fetch-shallow.sh b/t/t5537-fetch-shallow.sh
new file mode 100755
index 0000000..df8d2f0
--- /dev/null
+++ b/t/t5537-fetch-shallow.sh
@@ -0,0 +1,189 @@
+#!/bin/sh
+
+test_description='fetch/clone from a shallow clone'
+
+. ./test-lib.sh
+
+commit() {
+ echo "$1" >tracked &&
+ git add tracked &&
+ git commit -m "$1"
+}
+
+test_expect_success 'setup' '
+ commit 1 &&
+ commit 2 &&
+ commit 3 &&
+ commit 4 &&
+ git config --global transfer.fsckObjects true
+'
+
+test_expect_success 'setup shallow clone' '
+ git clone --no-local --depth=2 .git shallow &&
+ git --git-dir=shallow/.git log --format=%s >actual &&
+ cat <<EOF >expect &&
+4
+3
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'clone from shallow clone' '
+ git clone --no-local shallow shallow2 &&
+ (
+ cd shallow2 &&
+ git fsck &&
+ git log --format=%s >actual &&
+ cat <<EOF >expect &&
+4
+3
+EOF
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'fetch from shallow clone' '
+ (
+ cd shallow &&
+ commit 5
+ ) &&
+ (
+ cd shallow2 &&
+ git fetch &&
+ git fsck &&
+ git log --format=%s origin/master >actual &&
+ cat <<EOF >expect &&
+5
+4
+3
+EOF
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'fetch --depth from shallow clone' '
+ (
+ cd shallow &&
+ commit 6
+ ) &&
+ (
+ cd shallow2 &&
+ git fetch --depth=2 &&
+ git fsck &&
+ git log --format=%s origin/master >actual &&
+ cat <<EOF >expect &&
+6
+5
+EOF
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'fetch --unshallow from shallow clone' '
+ (
+ cd shallow2 &&
+ git fetch --unshallow &&
+ git fsck &&
+ git log --format=%s origin/master >actual &&
+ cat <<EOF >expect &&
+6
+5
+4
+3
+EOF
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'fetch something upstream has but hidden by clients shallow boundaries' '
+ # the blob "1" is available in .git but hidden by the
+ # shallow2/.git/shallow and it should be resent
+ ! git --git-dir=shallow2/.git cat-file blob $(echo 1|git hash-object --stdin) >/dev/null &&
+ echo 1 >1.t &&
+ git add 1.t &&
+ git commit -m add-1-back &&
+ (
+ cd shallow2 &&
+ git fetch ../.git +refs/heads/master:refs/remotes/top/master &&
+ git fsck &&
+ git log --format=%s top/master >actual &&
+ cat <<EOF >expect &&
+add-1-back
+4
+3
+EOF
+ test_cmp expect actual
+ ) &&
+ git --git-dir=shallow2/.git cat-file blob $(echo 1|git hash-object --stdin) >/dev/null
+
+'
+
+test_expect_success 'fetch that requires changes in .git/shallow is filtered' '
+ (
+ cd shallow &&
+ git checkout --orphan no-shallow &&
+ commit no-shallow
+ ) &&
+ git init notshallow &&
+ (
+ cd notshallow &&
+ git fetch ../shallow/.git refs/heads/*:refs/remotes/shallow/*&&
+ git for-each-ref --format="%(refname)" >actual.refs &&
+ cat <<EOF >expect.refs &&
+refs/remotes/shallow/no-shallow
+EOF
+ test_cmp expect.refs actual.refs &&
+ git log --format=%s shallow/no-shallow >actual &&
+ cat <<EOF >expect &&
+no-shallow
+EOF
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'fetch --update-shallow' '
+ (
+ cd shallow &&
+ git checkout master &&
+ commit 7 &&
+ git tag -m foo heavy-tag HEAD^ &&
+ git tag light-tag HEAD^:tracked
+ ) &&
+ (
+ cd notshallow &&
+ git fetch --update-shallow ../shallow/.git refs/heads/*:refs/remotes/shallow/* &&
+ git fsck &&
+ git for-each-ref --sort=refname --format="%(refname)" >actual.refs &&
+ cat <<EOF >expect.refs &&
+refs/remotes/shallow/master
+refs/remotes/shallow/no-shallow
+refs/tags/heavy-tag
+refs/tags/light-tag
+EOF
+ test_cmp expect.refs actual.refs &&
+ git log --format=%s shallow/master >actual &&
+ cat <<EOF >expect &&
+7
+6
+5
+4
+3
+EOF
+ test_cmp expect actual
+ )
+'
+
+test_expect_success POSIXPERM,SANITY 'shallow fetch from a read-only repo' '
+ cp -R .git read-only.git &&
+ find read-only.git -print | xargs chmod -w &&
+ test_when_finished "find read-only.git -type d -print | xargs chmod +w" &&
+ git clone --no-local --depth=2 read-only.git from-read-only &&
+ git --git-dir=from-read-only/.git log --format=%s >actual &&
+ cat >expect <<EOF &&
+add-1-back
+4
+EOF
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t5538-push-shallow.sh b/t/t5538-push-shallow.sh
new file mode 100755
index 0000000..ecbf84d
--- /dev/null
+++ b/t/t5538-push-shallow.sh
@@ -0,0 +1,123 @@
+#!/bin/sh
+
+test_description='push from/to a shallow clone'
+
+. ./test-lib.sh
+
+commit() {
+ echo "$1" >tracked &&
+ git add tracked &&
+ git commit -m "$1"
+}
+
+test_expect_success 'setup' '
+ git config --global transfer.fsckObjects true &&
+ commit 1 &&
+ commit 2 &&
+ commit 3 &&
+ commit 4 &&
+ git clone . full &&
+ (
+ git init full-abc &&
+ cd full-abc &&
+ commit a &&
+ commit b &&
+ commit c
+ ) &&
+ git clone --no-local --depth=2 .git shallow &&
+ git --git-dir=shallow/.git log --format=%s >actual &&
+ cat <<EOF >expect &&
+4
+3
+EOF
+ test_cmp expect actual &&
+ git clone --no-local --depth=2 full-abc/.git shallow2 &&
+ git --git-dir=shallow2/.git log --format=%s >actual &&
+ cat <<EOF >expect &&
+c
+b
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'push from shallow clone' '
+ (
+ cd shallow &&
+ commit 5 &&
+ git push ../.git +master:refs/remotes/shallow/master
+ ) &&
+ git log --format=%s shallow/master >actual &&
+ git fsck &&
+ cat <<EOF >expect &&
+5
+4
+3
+2
+1
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'push from shallow clone, with grafted roots' '
+ (
+ cd shallow2 &&
+ test_must_fail git push ../.git +master:refs/remotes/shallow2/master 2>err &&
+ grep "shallow2/master.*shallow update not allowed" err
+ ) &&
+ test_must_fail git rev-parse shallow2/master &&
+ git fsck
+'
+
+test_expect_success 'add new shallow root with receive.updateshallow on' '
+ test_config receive.shallowupdate true &&
+ (
+ cd shallow2 &&
+ git push ../.git +master:refs/remotes/shallow2/master
+ ) &&
+ git log --format=%s shallow2/master >actual &&
+ git fsck &&
+ cat <<EOF >expect &&
+c
+b
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'push from shallow to shallow' '
+ (
+ cd shallow &&
+ git --git-dir=../shallow2/.git config receive.shallowupdate true &&
+ git push ../shallow2/.git +master:refs/remotes/shallow/master &&
+ git --git-dir=../shallow2/.git config receive.shallowupdate false
+ ) &&
+ (
+ cd shallow2 &&
+ git log --format=%s shallow/master >actual &&
+ git fsck &&
+ cat <<EOF >expect &&
+5
+4
+3
+EOF
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'push from full to shallow' '
+ ! git --git-dir=shallow2/.git cat-file blob $(echo 1|git hash-object --stdin) &&
+ commit 1 &&
+ git push shallow2/.git +master:refs/remotes/top/master &&
+ (
+ cd shallow2 &&
+ git log --format=%s top/master >actual &&
+ git fsck &&
+ cat <<EOF >expect &&
+1
+4
+3
+EOF
+ test_cmp expect actual &&
+ git cat-file blob $(echo 1|git hash-object --stdin) >/dev/null
+ )
+'
+test_done
diff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh
new file mode 100755
index 0000000..37a4335
--- /dev/null
+++ b/t/t5539-fetch-http-shallow.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+test_description='fetch/clone from a shallow clone over http'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+commit() {
+ echo "$1" >tracked &&
+ git add tracked &&
+ git commit -m "$1"
+}
+
+test_expect_success 'setup shallow clone' '
+ commit 1 &&
+ commit 2 &&
+ commit 3 &&
+ commit 4 &&
+ commit 5 &&
+ commit 6 &&
+ commit 7 &&
+ git clone --no-local --depth=5 .git shallow &&
+ git config --global transfer.fsckObjects true
+'
+
+test_expect_success 'clone http repository' '
+ git clone --bare --no-local shallow "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git clone $HTTPD_URL/smart/repo.git clone &&
+ (
+ cd clone &&
+ git fsck &&
+ git log --format=%s origin/master >actual &&
+ cat <<EOF >expect &&
+7
+6
+5
+4
+3
+EOF
+ test_cmp expect actual
+ )
+'
+
+# This test is tricky. We need large enough "have"s that fetch-pack
+# will put pkt-flush in between. Then we need a "have" the server
+# does not have, it'll send "ACK %s ready"
+test_expect_success 'no shallow lines after receiving ACK ready' '
+ (
+ cd shallow &&
+ test_tick &&
+ for i in $(test_seq 15)
+ do
+ git checkout --orphan unrelated$i &&
+ test_commit unrelated$i &&
+ git push -q "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+ refs/heads/unrelated$i:refs/heads/unrelated$i &&
+ git push -q ../clone/.git \
+ refs/heads/unrelated$i:refs/heads/unrelated$i ||
+ exit 1
+ done &&
+ git checkout master &&
+ test_commit new &&
+ git push "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" master
+ ) &&
+ (
+ cd clone &&
+ git checkout --orphan newnew &&
+ test_commit new-too &&
+ GIT_TRACE_PACKET="$TRASH_DIRECTORY/trace" git fetch --depth=2 &&
+ grep "fetch-pack< ACK .* ready" ../trace &&
+ ! grep "fetch-pack> done" ../trace
+ )
+'
+
+stop_httpd
+test_done
diff --git a/t/t5540-http-push.sh b/t/t5540-http-push-webdav.sh
index a266ca5..88ff5a4 100755
--- a/t/t5540-http-push.sh
+++ b/t/t5540-http-push-webdav.sh
@@ -16,7 +16,6 @@ then
fi
LIB_HTTPD_DAV=t
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'}
. "$TEST_DIRECTORY"/lib-httpd.sh
ROOT_PATH="$PWD"
start_httpd
@@ -40,6 +39,14 @@ test_expect_success 'setup remote repository' '
mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
'
+test_expect_success 'create password-protected repository' '
+ mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb" &&
+ cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+ "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git"
+'
+
+setup_askpass_helper
+
test_expect_success 'clone remote repository' '
cd "$ROOT_PATH" &&
git clone $HTTPD_URL/dumb/test_repo.git test_repo_clone
@@ -101,7 +108,7 @@ test_expect_success 'http-push fetches packed objects' '
# By reset, we force git to retrieve the packed object
(cd "$ROOT_PATH"/test_repo_clone_packed &&
git reset --hard HEAD^ &&
- git remote rm origin &&
+ git remote remove origin &&
git reflog expire --expire=0 --all &&
git prune &&
git push -f -v $HTTPD_URL/dumb/test_repo_packed.git master)
@@ -132,14 +139,43 @@ 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] "
+ sed \
+ -e "s/PUT /OP /" \
+ -e "s/MOVE /OP /" \
+ -e "s|/objects/$x2/${x38}_$x40|WANTED_PATH_REQUEST|" \
+ "$HTTPD_ROOT_PATH"/access.log |
+ grep -e "\"OP .*WANTED_PATH_REQUEST HTTP/[.0-9]*\" 20[0-9] "
'
test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
"$ROOT_PATH"/test_repo_clone master
+test_expect_success 'push to password-protected repository (user in URL)' '
+ test_commit pw-user &&
+ set_askpass user@host pass@host &&
+ git push "$HTTPD_URL_USER/auth/dumb/test_repo.git" HEAD &&
+ git rev-parse --verify HEAD >expect &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
+ rev-parse --verify HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'user was prompted only once for password' '
+ expect_askpass pass user@host
+'
+
+test_expect_failure 'push to password-protected repository (no user in URL)' '
+ test_commit pw-nouser &&
+ set_askpass user@host pass@host &&
+ git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD &&
+ expect_askpass both user@host &&
+ git rev-parse --verify HEAD >expect &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
+ rev-parse --verify HEAD >actual &&
+ test_cmp expect actual
+'
+
stop_httpd
test_done
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
new file mode 100755
index 0000000..fd7d06b
--- /dev/null
+++ b/t/t5541-http-push-smart.sh
@@ -0,0 +1,372 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
+#
+
+test_description='test smart pushing over http via http-backend'
+. ./test-lib.sh
+
+ROOT_PATH="$PWD"
+. "$TEST_DIRECTORY"/lib-gpg.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+. "$TEST_DIRECTORY"/lib-terminal.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 &&
+ git config core.logallrefupdates true &&
+ ORIG_HEAD=$(git rev-parse --verify HEAD) &&
+ cd - &&
+ mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
+'
+
+setup_askpass_helper
+
+cat >exp <<EOF
+GET /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
+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 &&
+ (
+ cd test_repo_clone && git config push.default matching
+ )
+'
+
+test_expect_success 'push to remote repository (standard)' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ : >path2 &&
+ git add path2 &&
+ test_tick &&
+ git commit -m path2 &&
+ HEAD=$(git rev-parse --verify HEAD) &&
+ GIT_CURL_VERBOSE=1 git push -v -v 2>err &&
+ ! grep "Expect: 100-continue" err &&
+ grep "POST git-receive-pack ([0-9]* bytes)" err &&
+ (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 >"$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update" <<EOF
+#!/bin/sh
+exit 1
+EOF
+chmod a+x "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
+
+cat >exp <<EOF
+remote: error: hook declined to update refs/heads/dev2
+To http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git
+ ! [remote rejected] dev2 -> dev2 (hook declined)
+error: failed to push some refs to 'http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git'
+EOF
+
+test_expect_success 'rejected update prints status' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ git checkout -b dev2 &&
+ : >path4 &&
+ git add path4 &&
+ test_tick &&
+ git commit -m dev2 &&
+ test_must_fail git push origin dev2 2>act &&
+ sed -e "/^remote: /s/ *$//" <act >cmp &&
+ test_cmp exp cmp
+'
+rm -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
+
+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
+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 success
+
+test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' '
+ # create a dissimilarly-named remote ref so that git is unable to match the
+ # 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'
+
+test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: remote output' '
+ grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *master -> master (forced update)$" output &&
+ grep "^ ! \[rejected\] *master -> retsam (non-fast-forward)$" output
+'
+
+test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' '
+ test_i18ngrep "Updates were rejected because" \
+ output
+'
+
+test_expect_success 'push (chunked)' '
+ git checkout master &&
+ test_commit commit path3 &&
+ HEAD=$(git rev-parse --verify HEAD) &&
+ test_config http.postbuffer 4 &&
+ git push -v -v origin $BRANCH 2>err &&
+ grep "POST git-receive-pack (chunked)" err &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+ test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'push --all can push to empty repo' '
+ d=$HTTPD_DOCUMENT_ROOT_PATH/empty-all.git &&
+ git init --bare "$d" &&
+ git --git-dir="$d" config http.receivepack true &&
+ git push --all "$HTTPD_URL"/smart/empty-all.git
+'
+
+test_expect_success 'push --mirror can push to empty repo' '
+ d=$HTTPD_DOCUMENT_ROOT_PATH/empty-mirror.git &&
+ git init --bare "$d" &&
+ git --git-dir="$d" config http.receivepack true &&
+ git push --mirror "$HTTPD_URL"/smart/empty-mirror.git
+'
+
+test_expect_success 'push --all to repo with alternates' '
+ s=$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git &&
+ d=$HTTPD_DOCUMENT_ROOT_PATH/alternates-all.git &&
+ git clone --bare --shared "$s" "$d" &&
+ git --git-dir="$d" config http.receivepack true &&
+ git --git-dir="$d" repack -adl &&
+ git push --all "$HTTPD_URL"/smart/alternates-all.git
+'
+
+test_expect_success 'push --mirror to repo with alternates' '
+ s=$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git &&
+ d=$HTTPD_DOCUMENT_ROOT_PATH/alternates-mirror.git &&
+ git clone --bare --shared "$s" "$d" &&
+ git --git-dir="$d" config http.receivepack true &&
+ git --git-dir="$d" repack -adl &&
+ git push --mirror "$HTTPD_URL"/smart/alternates-mirror.git
+'
+
+test_expect_success TTY 'push shows progress when stderr is a tty' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ test_commit noisy &&
+ test_terminal git push >output 2>&1 &&
+ grep "^Writing objects" output
+'
+
+test_expect_success TTY 'push --quiet silences status and progress' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ test_commit quiet &&
+ test_terminal git push --quiet >output 2>&1 &&
+ test_cmp /dev/null output
+'
+
+test_expect_success TTY 'push --no-progress silences progress but not status' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ test_commit no-progress &&
+ test_terminal git push --no-progress >output 2>&1 &&
+ grep "^To http" output &&
+ ! grep "^Writing objects"
+'
+
+test_expect_success 'push --progress shows progress to non-tty' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ test_commit progress &&
+ git push --progress >output 2>&1 &&
+ grep "^To http" output &&
+ grep "^Writing objects" output
+'
+
+test_expect_success 'http push gives sane defaults to reflog' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ test_commit reflog-test &&
+ git push "$HTTPD_URL"/smart/test_repo.git &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+ log -g -1 --format="%gn <%ge>" >actual &&
+ echo "anonymous <anonymous@http.127.0.0.1>" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'http push respects GIT_COMMITTER_* in reflog' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ test_commit custom-reflog-test &&
+ git push "$HTTPD_URL"/smart_custom_env/test_repo.git &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+ log -g -1 --format="%gn <%ge>" >actual &&
+ echo "Custom User <custom@example.com>" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push over smart http with auth' '
+ cd "$ROOT_PATH/test_repo_clone" &&
+ echo push-auth-test >expect &&
+ test_commit push-auth-test &&
+ set_askpass user@host pass@host &&
+ git push "$HTTPD_URL"/auth/smart/test_repo.git &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+ log -1 --format=%s >actual &&
+ expect_askpass both user@host &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push to auth-only-for-push repo' '
+ cd "$ROOT_PATH/test_repo_clone" &&
+ echo push-half-auth >expect &&
+ test_commit push-half-auth &&
+ set_askpass user@host pass@host &&
+ git push "$HTTPD_URL"/auth-push/smart/test_repo.git &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+ log -1 --format=%s >actual &&
+ expect_askpass both user@host &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create repo without http.receivepack set' '
+ cd "$ROOT_PATH" &&
+ git init half-auth &&
+ (
+ cd half-auth &&
+ test_commit one
+ ) &&
+ git clone --bare half-auth "$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git"
+'
+
+test_expect_success 'clone via half-auth-complete does not need password' '
+ cd "$ROOT_PATH" &&
+ set_askpass wrong &&
+ git clone "$HTTPD_URL"/half-auth-complete/smart/half-auth.git \
+ half-auth-clone &&
+ expect_askpass none
+'
+
+test_expect_success 'push into half-auth-complete requires password' '
+ cd "$ROOT_PATH/half-auth-clone" &&
+ echo two >expect &&
+ test_commit two &&
+ set_askpass user@host pass@host &&
+ git push "$HTTPD_URL/half-auth-complete/smart/half-auth.git" &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git" \
+ log -1 --format=%s >actual &&
+ expect_askpass both user@host &&
+ test_cmp expect actual
+'
+
+test_expect_success CMDLINE_LIMIT 'push 2000 tags over http' '
+ sha1=$(git rev-parse HEAD) &&
+ test_seq 2000 |
+ sort |
+ sed "s|.*|$sha1 refs/tags/really-long-tag-name-&|" \
+ >.git/packed-refs &&
+ run_with_limited_cmdline git push --mirror
+'
+
+test_expect_success GPG 'push with post-receive to inspect certificate' '
+ (
+ cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+ mkdir -p hooks &&
+ write_script hooks/post-receive <<-\EOF &&
+ # discard the update list
+ cat >/dev/null
+ # record the push certificate
+ if test -n "${GIT_PUSH_CERT-}"
+ then
+ git cat-file blob $GIT_PUSH_CERT >../push-cert
+ fi &&
+ cat >../push-cert-status <<E_O_F
+ SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
+ KEY=${GIT_PUSH_CERT_KEY-nokey}
+ STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
+ NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
+ NONCE=${GIT_PUSH_CERT_NONCE-nononce}
+ E_O_F
+ EOF
+
+ git config receive.certnonceseed sekrit &&
+ git config receive.certnonceslop 30
+ ) &&
+ cd "$ROOT_PATH/test_repo_clone" &&
+ test_commit cert-test &&
+ git push --signed "$HTTPD_URL/smart/test_repo.git" &&
+ (
+ cd "$HTTPD_DOCUMENT_ROOT_PATH" &&
+ cat <<-\EOF &&
+ SIGNER=C O Mitter <committer@example.com>
+ KEY=13B6F51ECDDE430D
+ STATUS=G
+ NONCE_STATUS=OK
+ EOF
+ sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" push-cert
+ ) >expect &&
+ test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH/push-cert-status"
+'
+
+stop_httpd
+test_done
diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh
deleted file mode 100755
index b0c2a2c..0000000
--- a/t/t5541-http-push.sh
+++ /dev/null
@@ -1,141 +0,0 @@
-#!/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/t5542-push-http-shallow.sh b/t/t5542-push-http-shallow.sh
new file mode 100755
index 0000000..5165833
--- /dev/null
+++ b/t/t5542-push-http-shallow.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+test_description='push from/to a shallow clone over http'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+commit() {
+ echo "$1" >tracked &&
+ git add tracked &&
+ git commit -m "$1"
+}
+
+test_expect_success 'setup' '
+ git config --global transfer.fsckObjects true &&
+ commit 1 &&
+ commit 2 &&
+ commit 3 &&
+ commit 4 &&
+ git clone . full &&
+ (
+ git init full-abc &&
+ cd full-abc &&
+ commit a &&
+ commit b &&
+ commit c
+ ) &&
+ git clone --no-local --depth=2 .git shallow &&
+ git --git-dir=shallow/.git log --format=%s >actual &&
+ cat <<EOF >expect &&
+4
+3
+EOF
+ test_cmp expect actual &&
+ git clone --no-local --depth=2 full-abc/.git shallow2 &&
+ git --git-dir=shallow2/.git log --format=%s >actual &&
+ cat <<EOF >expect &&
+c
+b
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'push to shallow repo via http' '
+ git clone --bare --no-local shallow "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ (
+ cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git config http.receivepack true
+ ) &&
+ (
+ cd full &&
+ commit 9 &&
+ git push $HTTPD_URL/smart/repo.git +master:refs/remotes/top/master
+ ) &&
+ (
+ cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git fsck &&
+ git log --format=%s top/master >actual &&
+ cat <<EOF >expect &&
+9
+4
+3
+EOF
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'push from shallow repo via http' '
+ mv "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" shallow-upstream.git &&
+ git clone --bare --no-local full "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ (
+ cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git config http.receivepack true
+ ) &&
+ commit 10 &&
+ git push $HTTPD_URL/smart/repo.git +master:refs/remotes/top/master &&
+ (
+ cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git fsck &&
+ git log --format=%s top/master >actual &&
+ cat <<EOF >expect &&
+10
+4
+3
+2
+1
+EOF
+ test_cmp expect actual
+ )
+'
+
+stop_httpd
+test_done
diff --git a/t/t5543-atomic-push.sh b/t/t5543-atomic-push.sh
new file mode 100755
index 0000000..3480b33
--- /dev/null
+++ b/t/t5543-atomic-push.sh
@@ -0,0 +1,194 @@
+#!/bin/sh
+
+test_description='pushing to a repository using the atomic push option'
+
+. ./test-lib.sh
+
+mk_repo_pair () {
+ rm -rf workbench upstream &&
+ test_create_repo upstream &&
+ test_create_repo workbench &&
+ (
+ cd upstream &&
+ git config receive.denyCurrentBranch warn
+ ) &&
+ (
+ cd workbench &&
+ git remote add up ../upstream
+ )
+}
+
+# Compare the ref ($1) in upstream with a ref value from workbench ($2)
+# i.e. test_refs second HEAD@{2}
+test_refs () {
+ test $# = 2 &&
+ git -C upstream rev-parse --verify "$1" >expect &&
+ git -C workbench rev-parse --verify "$2" >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'atomic push works for a single branch' '
+ mk_repo_pair &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git push --mirror up &&
+ test_commit two &&
+ git push --atomic up master
+ ) &&
+ test_refs master master
+'
+
+test_expect_success 'atomic push works for two branches' '
+ mk_repo_pair &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git branch second &&
+ git push --mirror up &&
+ test_commit two &&
+ git checkout second &&
+ test_commit three &&
+ git push --atomic up master second
+ ) &&
+ test_refs master master &&
+ test_refs second second
+'
+
+test_expect_success 'atomic push works in combination with --mirror' '
+ mk_repo_pair &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git checkout -b second &&
+ test_commit two &&
+ git push --atomic --mirror up
+ ) &&
+ test_refs master master &&
+ test_refs second second
+'
+
+test_expect_success 'atomic push works in combination with --force' '
+ mk_repo_pair &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git branch second master &&
+ test_commit two_a &&
+ git checkout second &&
+ test_commit two_b &&
+ test_commit three_b &&
+ test_commit four &&
+ git push --mirror up &&
+ # The actual test is below
+ git checkout master &&
+ test_commit three_a &&
+ git checkout second &&
+ git reset --hard HEAD^ &&
+ git push --force --atomic up master second
+ ) &&
+ test_refs master master &&
+ test_refs second second
+'
+
+# set up two branches where master can be pushed but second can not
+# (non-fast-forward). Since second can not be pushed the whole operation
+# will fail and leave master untouched.
+test_expect_success 'atomic push fails if one branch fails' '
+ mk_repo_pair &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git checkout -b second master &&
+ test_commit two &&
+ test_commit three &&
+ test_commit four &&
+ git push --mirror up &&
+ git reset --hard HEAD~2 &&
+ test_commit five &&
+ git checkout master &&
+ test_commit six &&
+ test_must_fail git push --atomic --all up
+ ) &&
+ test_refs master HEAD@{7} &&
+ test_refs second HEAD@{4}
+'
+
+test_expect_success 'atomic push fails if one tag fails remotely' '
+ # prepare the repo
+ mk_repo_pair &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git checkout -b second master &&
+ test_commit two &&
+ git push --mirror up
+ ) &&
+ # a third party modifies the server side:
+ (
+ cd upstream &&
+ git checkout second &&
+ git tag test_tag second
+ ) &&
+ # see if we can now push both branches.
+ (
+ cd workbench &&
+ git checkout master &&
+ test_commit three &&
+ git checkout second &&
+ test_commit four &&
+ git tag test_tag &&
+ test_must_fail git push --tags --atomic up master second
+ ) &&
+ test_refs master HEAD@{3} &&
+ test_refs second HEAD@{1}
+'
+
+test_expect_success 'atomic push obeys update hook preventing a branch to be pushed' '
+ mk_repo_pair &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git checkout -b second master &&
+ test_commit two &&
+ git push --mirror up
+ ) &&
+ (
+ cd upstream &&
+ HOOKDIR="$(git rev-parse --git-dir)/hooks" &&
+ HOOK="$HOOKDIR/update" &&
+ mkdir -p "$HOOKDIR" &&
+ write_script "$HOOK" <<-\EOF
+ # only allow update to master from now on
+ test "$1" = "refs/heads/master"
+ EOF
+ ) &&
+ (
+ cd workbench &&
+ git checkout master &&
+ test_commit three &&
+ git checkout second &&
+ test_commit four &&
+ test_must_fail git push --atomic up master second
+ ) &&
+ test_refs master HEAD@{3} &&
+ test_refs second HEAD@{1}
+'
+
+test_expect_success 'atomic push is not advertised if configured' '
+ mk_repo_pair &&
+ (
+ cd upstream
+ git config receive.advertiseatomic 0
+ ) &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git push --mirror up &&
+ test_commit two &&
+ test_must_fail git push --atomic up master
+ ) &&
+ test_refs master HEAD@{1}
+'
+
+test_done
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
new file mode 100755
index 0000000..3484b6f
--- /dev/null
+++ b/t/t5550-http-fetch-dumb.sh
@@ -0,0 +1,303 @@
+#!/bin/sh
+
+test_description='test dumb fetching over http via static file'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'setup repository' '
+ git config push.default matching &&
+ echo content1 >file &&
+ git add file &&
+ git commit -m one &&
+ echo content2 >file &&
+ git add file &&
+ git commit -m two
+'
+
+test_expect_success 'create http-accessible bare repository with loose objects' '
+ cp -R .git "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git config core.bare true &&
+ mkdir -p hooks &&
+ echo "exec git update-server-info" >hooks/post-update &&
+ chmod +x hooks/post-update &&
+ hooks/post-update
+ ) &&
+ git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git push public master:master
+'
+
+test_expect_success 'clone http repository' '
+ git clone $HTTPD_URL/dumb/repo.git clone-tmpl &&
+ cp -R clone-tmpl clone &&
+ test_cmp file clone/file
+'
+
+test_expect_success 'create password-protected repository' '
+ mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" &&
+ cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+ "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/repo.git"
+'
+
+setup_askpass_helper
+
+test_expect_success 'cloning password-protected repository can fail' '
+ set_askpass wrong &&
+ test_must_fail git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-fail &&
+ expect_askpass both wrong
+'
+
+test_expect_success 'http auth can use user/pass in URL' '
+ set_askpass wrong &&
+ git clone "$HTTPD_URL_USER_PASS/auth/dumb/repo.git" clone-auth-none &&
+ expect_askpass none
+'
+
+test_expect_success 'http auth can use just user in URL' '
+ set_askpass wrong pass@host &&
+ git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-pass &&
+ expect_askpass pass user@host
+'
+
+test_expect_success 'http auth can request both user and pass' '
+ set_askpass user@host pass@host &&
+ git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-both &&
+ expect_askpass both user@host
+'
+
+test_expect_success 'http auth respects credential helper config' '
+ test_config_global credential.helper "!f() {
+ cat >/dev/null
+ echo username=user@host
+ echo password=pass@host
+ }; f" &&
+ set_askpass wrong &&
+ git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-helper &&
+ expect_askpass none
+'
+
+test_expect_success 'http auth can get username from config' '
+ test_config_global "credential.$HTTPD_URL.username" user@host &&
+ set_askpass wrong pass@host &&
+ git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-user &&
+ expect_askpass pass user@host
+'
+
+test_expect_success 'configured username does not override URL' '
+ test_config_global "credential.$HTTPD_URL.username" wrong &&
+ set_askpass wrong pass@host &&
+ git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-user2 &&
+ expect_askpass pass user@host
+'
+
+test_expect_success 'set up repo with http submodules' '
+ git init super &&
+ set_askpass user@host pass@host &&
+ (
+ cd super &&
+ git submodule add "$HTTPD_URL/auth/dumb/repo.git" sub &&
+ git commit -m "add submodule"
+ )
+'
+
+test_expect_success 'cmdline credential config passes to submodule via clone' '
+ set_askpass wrong pass@host &&
+ test_must_fail git clone --recursive super super-clone &&
+ rm -rf super-clone &&
+
+ set_askpass wrong pass@host &&
+ git -c "credential.$HTTPD_URL.username=user@host" \
+ clone --recursive super super-clone &&
+ expect_askpass pass user@host
+'
+
+test_expect_success 'cmdline credential config passes submodule via fetch' '
+ set_askpass wrong pass@host &&
+ test_must_fail git -C super-clone fetch --recurse-submodules &&
+
+ set_askpass wrong pass@host &&
+ git -C super-clone \
+ -c "credential.$HTTPD_URL.username=user@host" \
+ fetch --recurse-submodules &&
+ expect_askpass pass user@host
+'
+
+test_expect_success 'cmdline credential config passes submodule update' '
+ # advance the submodule HEAD so that a fetch is required
+ git commit --allow-empty -m foo &&
+ git push "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/repo.git" HEAD &&
+ sha1=$(git rev-parse HEAD) &&
+ git -C super-clone update-index --cacheinfo 160000,$sha1,sub &&
+
+ set_askpass wrong pass@host &&
+ test_must_fail git -C super-clone submodule update &&
+
+ set_askpass wrong pass@host &&
+ git -C super-clone \
+ -c "credential.$HTTPD_URL.username=user@host" \
+ submodule update &&
+ expect_askpass pass user@host
+'
+
+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 'fetch changes via manual http-fetch' '
+ cp -R clone-tmpl clone2 &&
+
+ HEAD=$(git rev-parse --verify HEAD) &&
+ (cd clone2 &&
+ git http-fetch -a -w heads/master-new $HEAD $(git config remote.origin.url) &&
+ git checkout master-new &&
+ test $HEAD = $(git rev-parse --verify HEAD)) &&
+ test_cmp file clone2/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 -a -d
+ ) &&
+ 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 'fetch can handle previously-fetched .idx files' '
+ git checkout --orphan branch1 &&
+ echo base >file &&
+ git add file &&
+ git commit -m base &&
+ git --bare init "$HTTPD_DOCUMENT_ROOT_PATH"/repo_packed_branches.git &&
+ git push "$HTTPD_DOCUMENT_ROOT_PATH"/repo_packed_branches.git branch1 &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH"/repo_packed_branches.git repack -d &&
+ git checkout -b branch2 branch1 &&
+ echo b2 >>file &&
+ git commit -a -m b2 &&
+ git push "$HTTPD_DOCUMENT_ROOT_PATH"/repo_packed_branches.git branch2 &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH"/repo_packed_branches.git repack -d &&
+ git --bare init clone_packed_branches.git &&
+ git --git-dir=clone_packed_branches.git fetch "$HTTPD_URL"/dumb/repo_packed_branches.git branch1:branch1 &&
+ git --git-dir=clone_packed_branches.git fetch "$HTTPD_URL"/dumb/repo_packed_branches.git branch2:branch2
+'
+
+test_expect_success 'did not use upload-pack service' '
+ test_might_fail grep '/git-upload-pack' <"$HTTPD_ROOT_PATH"/access.log >act &&
+ : >exp &&
+ test_cmp exp act
+'
+
+test_expect_success 'git client shows text/plain errors' '
+ test_must_fail git clone "$HTTPD_URL/error/text" 2>stderr &&
+ grep "this is the error message" stderr
+'
+
+test_expect_success 'git client does not show html errors' '
+ test_must_fail git clone "$HTTPD_URL/error/html" 2>stderr &&
+ ! grep "this is the error message" stderr
+'
+
+test_expect_success 'git client shows text/plain with a charset' '
+ test_must_fail git clone "$HTTPD_URL/error/charset" 2>stderr &&
+ grep "this is the error message" stderr
+'
+
+test_expect_success 'http error messages are reencoded' '
+ test_must_fail git clone "$HTTPD_URL/error/utf16" 2>stderr &&
+ grep "this is the error message" stderr
+'
+
+test_expect_success 'reencoding is robust to whitespace oddities' '
+ test_must_fail git clone "$HTTPD_URL/error/odd-spacing" 2>stderr &&
+ grep "this is the error message" stderr
+'
+
+check_language () {
+ case "$2" in
+ '')
+ >expect
+ ;;
+ ?*)
+ echo "Accept-Language: $1" >expect
+ ;;
+ esac &&
+ GIT_CURL_VERBOSE=1 \
+ LANGUAGE=$2 \
+ git ls-remote "$HTTPD_URL/dumb/repo.git" >output 2>&1 &&
+ tr -d '\015' <output |
+ sort -u |
+ sed -ne '/^Accept-Language:/ p' >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'git client sends Accept-Language based on LANGUAGE' '
+ check_language "ko-KR, *;q=0.9" ko_KR.UTF-8'
+
+test_expect_success 'git client sends Accept-Language correctly with unordinary LANGUAGE' '
+ check_language "ko-KR, *;q=0.9" "ko_KR:" &&
+ check_language "ko-KR, en-US;q=0.9, *;q=0.8" "ko_KR::en_US" &&
+ check_language "ko-KR, *;q=0.9" ":::ko_KR" &&
+ check_language "ko-KR, en-US;q=0.9, *;q=0.8" "ko_KR!!:en_US" &&
+ check_language "ko-KR, ja-JP;q=0.9, *;q=0.8" "ko_KR en_US:ja_JP"'
+
+test_expect_success 'git client sends Accept-Language with many preferred languages' '
+ check_language "ko-KR, en-US;q=0.9, fr-CA;q=0.8, de;q=0.7, sr;q=0.6, \
+ja;q=0.5, zh;q=0.4, sv;q=0.3, pt;q=0.2, *;q=0.1" \
+ ko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt &&
+ check_language "ko-KR, en-US;q=0.99, fr-CA;q=0.98, de;q=0.97, sr;q=0.96, \
+ja;q=0.95, zh;q=0.94, sv;q=0.93, pt;q=0.92, nb;q=0.91, *;q=0.90" \
+ ko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt:nb
+'
+
+test_expect_success 'git client does not send an empty Accept-Language' '
+ GIT_CURL_VERBOSE=1 LANGUAGE= git ls-remote "$HTTPD_URL/dumb/repo.git" 2>stderr &&
+ ! grep "^Accept-Language:" stderr
+'
+
+stop_httpd
+test_done
diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh
deleted file mode 100755
index 2fb48d0..0000000
--- a/t/t5550-http-fetch.sh
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/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-smart.sh b/t/t5551-http-fetch-smart.sh
new file mode 100755
index 0000000..2f375eb
--- /dev/null
+++ b/t/t5551-http-fetch-smart.sh
@@ -0,0 +1,303 @@
+#!/bin/sh
+
+test_description='test smart fetching over http via http-backend'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'setup repository' '
+ git config push.default matching &&
+ 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
+'
+
+setup_askpass_helper
+
+cat >exp <<EOF
+> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
+> Accept: */*
+> Accept-Encoding: gzip
+> Pragma: no-cache
+< HTTP/1.1 200 OK
+< Pragma: no-cache
+< Cache-Control: no-cache, max-age=0, must-revalidate
+< Content-Type: application/x-git-upload-pack-advertisement
+> POST /smart/repo.git/git-upload-pack HTTP/1.1
+> Accept-Encoding: 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
+'
+
+test_expect_success 'follow redirects (301)' '
+ git clone $HTTPD_URL/smart-redir-perm/repo.git --quiet repo-p
+'
+
+test_expect_success 'follow redirects (302)' '
+ git clone $HTTPD_URL/smart-redir-temp/repo.git --quiet repo-t
+'
+
+test_expect_success 'redirects re-root further requests' '
+ git clone $HTTPD_URL/smart-redir-limited/repo.git repo-redir-limited
+'
+
+test_expect_success 'clone from password-protected repository' '
+ echo two >expect &&
+ set_askpass user@host pass@host &&
+ git clone --bare "$HTTPD_URL/auth/smart/repo.git" smart-auth &&
+ expect_askpass both user@host &&
+ git --git-dir=smart-auth log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone from auth-only-for-push repository' '
+ echo two >expect &&
+ set_askpass wrong &&
+ git clone --bare "$HTTPD_URL/auth-push/smart/repo.git" smart-noauth &&
+ expect_askpass none &&
+ git --git-dir=smart-noauth log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone from auth-only-for-objects repository' '
+ echo two >expect &&
+ set_askpass user@host pass@host &&
+ git clone --bare "$HTTPD_URL/auth-fetch/smart/repo.git" half-auth &&
+ expect_askpass both user@host &&
+ git --git-dir=half-auth log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-op half-auth fetch does not require a password' '
+ set_askpass wrong &&
+ git --git-dir=half-auth fetch &&
+ expect_askpass none
+'
+
+test_expect_success 'redirects send auth to new location' '
+ set_askpass user@host pass@host &&
+ git -c credential.useHttpPath=true \
+ clone $HTTPD_URL/smart-redir-auth/repo.git repo-redir-auth &&
+ expect_askpass both user@host auth/smart/repo.git
+'
+
+test_expect_success 'disable dumb http on server' '
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+ config http.getanyfile false
+'
+
+test_expect_success 'GIT_SMART_HTTP can disable smart http' '
+ (GIT_SMART_HTTP=0 &&
+ export GIT_SMART_HTTP &&
+ cd clone &&
+ test_must_fail git fetch)
+'
+
+test_expect_success 'invalid Content-Type rejected' '
+ test_must_fail git clone $HTTPD_URL/broken_smart/repo.git 2>actual &&
+ grep "not valid:" actual
+'
+
+test_expect_success 'create namespaced refs' '
+ test_commit namespaced &&
+ git push public HEAD:refs/namespaces/ns/refs/heads/master &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+ symbolic-ref refs/namespaces/ns/HEAD refs/namespaces/ns/refs/heads/master
+'
+
+test_expect_success 'smart clone respects namespace' '
+ git clone "$HTTPD_URL/smart_namespace/repo.git" ns-smart &&
+ echo namespaced >expect &&
+ git --git-dir=ns-smart/.git log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'dumb clone via http-backend respects namespace' '
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+ config http.getanyfile true &&
+ GIT_SMART_HTTP=0 git clone \
+ "$HTTPD_URL/smart_namespace/repo.git" ns-dumb &&
+ echo namespaced >expect &&
+ git --git-dir=ns-dumb/.git log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+
+cat >cookies.txt <<EOF
+127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue
+EOF
+cat >expect_cookies.txt <<EOF
+
+127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue
+127.0.0.1 FALSE /smart_cookies/repo.git/info/ FALSE 0 name value
+EOF
+test_expect_success 'cookies stored in http.cookiefile when http.savecookies set' '
+ git config http.cookiefile cookies.txt &&
+ git config http.savecookies true &&
+ git ls-remote $HTTPD_URL/smart_cookies/repo.git master &&
+ tail -3 cookies.txt >cookies_tail.txt &&
+ test_cmp expect_cookies.txt cookies_tail.txt
+'
+
+test_expect_success 'transfer.hiderefs works over smart-http' '
+ test_commit hidden &&
+ test_commit visible &&
+ git push public HEAD^:refs/heads/a HEAD:refs/heads/b &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+ config transfer.hiderefs refs/heads/a &&
+ git clone --bare "$HTTPD_URL/smart/repo.git" hidden.git &&
+ test_must_fail git -C hidden.git rev-parse --verify a &&
+ git -C hidden.git rev-parse --verify b
+'
+
+# create an arbitrary number of tags, numbered from tag-$1 to tag-$2
+create_tags () {
+ rm -f marks &&
+ for i in $(test_seq "$1" "$2")
+ do
+ # don't use here-doc, because it requires a process
+ # per loop iteration
+ echo "commit refs/heads/too-many-refs-$1" &&
+ echo "mark :$i" &&
+ echo "committer git <git@example.com> $i +0000" &&
+ echo "data 0" &&
+ echo "M 644 inline bla.txt" &&
+ echo "data 4" &&
+ echo "bla" &&
+ # make every commit dangling by always
+ # rewinding the branch after each commit
+ echo "reset refs/heads/too-many-refs-$1" &&
+ echo "from :$1"
+ done | git fast-import --export-marks=marks &&
+
+ # now assign tags to all the dangling commits we created above
+ tag=$(perl -e "print \"bla\" x 30") &&
+ sed -e "s|^:\([^ ]*\) \(.*\)$|\2 refs/tags/$tag-\1|" <marks >>packed-refs
+}
+
+test_expect_success 'create 2,000 tags in the repo' '
+ (
+ cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ create_tags 1 2000
+ )
+'
+
+test_expect_success CMDLINE_LIMIT \
+ 'clone the 2,000 tag repo to check OS command line overflow' '
+ run_with_limited_cmdline git clone $HTTPD_URL/smart/repo.git too-many-refs &&
+ (
+ cd too-many-refs &&
+ git for-each-ref refs/tags >actual &&
+ test_line_count = 2000 actual
+ )
+'
+
+test_expect_success 'large fetch-pack requests can be split across POSTs' '
+ GIT_CURL_VERBOSE=1 git -c http.postbuffer=65536 \
+ clone --bare "$HTTPD_URL/smart/repo.git" split.git 2>err &&
+ grep "^> POST" err >posts &&
+ test_line_count = 2 posts
+'
+
+test_expect_success EXPENSIVE 'http can handle enormous ref negotiation' '
+ (
+ cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ create_tags 2001 50000
+ ) &&
+ git -C too-many-refs fetch -q --tags &&
+ (
+ cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ create_tags 50001 100000
+ ) &&
+ git -C too-many-refs fetch -q --tags &&
+ git -C too-many-refs for-each-ref refs/tags >tags &&
+ test_line_count = 100000 tags
+'
+
+test_expect_success 'custom http headers' '
+ test_must_fail git -c http.extraheader="x-magic-two: cadabra" \
+ fetch "$HTTPD_URL/smart_headers/repo.git" &&
+ git -c http.extraheader="x-magic-one: abra" \
+ -c http.extraheader="x-magic-two: cadabra" \
+ fetch "$HTTPD_URL/smart_headers/repo.git" &&
+ git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub &&
+ git config -f .gitmodules submodule.sub.path sub &&
+ git config -f .gitmodules submodule.sub.url \
+ "$HTTPD_URL/smart_headers/repo.git" &&
+ git submodule init sub &&
+ test_must_fail git submodule update sub &&
+ git -c http.extraheader="x-magic-one: abra" \
+ -c http.extraheader="x-magic-two: cadabra" \
+ submodule update sub
+'
+
+stop_httpd
+test_done
diff --git a/t/t5551-http-fetch.sh b/t/t5551-http-fetch.sh
deleted file mode 100755
index fd19121..0000000
--- a/t/t5551-http-fetch.sh
+++ /dev/null
@@ -1,105 +0,0 @@
-#!/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
index 44885b8..9fafcf1 100755
--- a/t/t5560-http-backend-noserver.sh
+++ b/t/t5560-http-backend-noserver.sh
@@ -5,18 +5,23 @@ test_description='test git-http-backend-noserver'
HTTPD_DOCUMENT_ROOT_PATH="$TRASH_DIRECTORY"
+if test_have_prereq GREP_STRIPS_CR
+then
+ GREP_OPTIONS=-U
+ export GREP_OPTIONS
+fi
+
run_backend() {
echo "$2" |
- QUERY_STRING="${1#*\?}" \
- GIT_PROJECT_ROOT="$HTTPD_DOCUMENT_ROOT_PATH" \
- PATH_INFO="${1%%\?*}" \
+ QUERY_STRING="${1#*[?]}" \
+ PATH_TRANSLATED="$HTTPD_DOCUMENT_ROOT_PATH/${1%%[?]*}" \
git http-backend >act.out 2>act.err
}
GET() {
- export REQUEST_METHOD="GET" &&
+ REQUEST_METHOD="GET" && export REQUEST_METHOD &&
run_backend "/repo.git/$1" &&
- unset REQUEST_METHOD &&
+ sane_unset REQUEST_METHOD &&
if ! grep "Status" act.out >act
then
printf "Status: 200 OK\r\n" >act
@@ -26,11 +31,11 @@ GET() {
}
POST() {
- export REQUEST_METHOD="POST" &&
- export CONTENT_TYPE="application/x-$1-request" &&
+ REQUEST_METHOD="POST" && export REQUEST_METHOD &&
+ CONTENT_TYPE="application/x-$1-request" && export CONTENT_TYPE &&
run_backend "/repo.git/$1" "$2" &&
- unset REQUEST_METHOD &&
- unset CONTENT_TYPE &&
+ sane_unset REQUEST_METHOD &&
+ sane_unset CONTENT_TYPE &&
if ! grep "Status" act.out >act
then
printf "Status: 200 OK\r\n" >act
@@ -39,14 +44,10 @@ POST() {
test_cmp exp act
}
-log_div() {
- return 0
-}
-
. "$TEST_DIRECTORY"/t556x_common
expect_aliased() {
- export REQUEST_METHOD="GET" &&
+ REQUEST_METHOD="GET" && export REQUEST_METHOD &&
if test $1 = 0; then
run_backend "$2"
else
diff --git a/t/t5561-http-backend.sh b/t/t5561-http-backend.sh
index b5d7fbc..90e0d6f 100755
--- a/t/t5561-http-backend.sh
+++ b/t/t5561-http-backend.sh
@@ -2,13 +2,6 @@
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
@@ -36,15 +29,9 @@ POST() {
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
+grep '^[^#]' >exp <<EOF
### refs/heads/master
###
diff --git a/t/t556x_common b/t/t556x_common
index be024e5..359fcfe 100755
--- a/t/t556x_common
+++ b/t/t556x_common
@@ -50,72 +50,60 @@ get_static_files() {
}
SMART=smart
-export GIT_HTTP_EXPORT_ALL=1
+GIT_HTTP_EXPORT_ALL=1 && export GIT_HTTP_EXPORT_ALL
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
+GIT_HTTP_EXPORT_ALL=1 && export GIT_HTTP_EXPORT_ALL
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/t5570-git-daemon.sh b/t/t5570-git-daemon.sh
new file mode 100755
index 0000000..225a022
--- /dev/null
+++ b/t/t5570-git-daemon.sh
@@ -0,0 +1,190 @@
+#!/bin/sh
+
+test_description='test fetching over git protocol'
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-git-daemon.sh
+start_git_daemon
+
+check_verbose_connect () {
+ grep -F "Looking up 127.0.0.1 ..." stderr &&
+ grep -F "Connecting to 127.0.0.1 (port " stderr &&
+ grep -F "done." stderr
+}
+
+test_expect_success 'setup repository' '
+ git config push.default matching &&
+ echo content >file &&
+ git add file &&
+ git commit -m one
+'
+
+test_expect_success 'create git-accessible bare repository' '
+ mkdir "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" &&
+ (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" &&
+ git --bare init &&
+ : >git-daemon-export-ok
+ ) &&
+ git remote add public "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" &&
+ git push public master:master
+'
+
+test_expect_success 'clone git repository' '
+ git clone -v "$GIT_DAEMON_URL/repo.git" clone 2>stderr &&
+ check_verbose_connect &&
+ test_cmp file clone/file
+'
+
+test_expect_success 'fetch changes via git protocol' '
+ echo content >>file &&
+ git commit -a -m two &&
+ git push public &&
+ (cd clone && git pull -v) 2>stderr &&
+ check_verbose_connect &&
+ test_cmp file clone/file
+'
+
+test_expect_success 'no-op fetch -v stderr is as expected' '
+ (cd clone && git fetch -v) 2>stderr &&
+ check_verbose_connect
+'
+
+test_expect_success 'no-op fetch without "-v" is quiet' '
+ (cd clone && git fetch) 2>stderr &&
+ ! test -s stderr
+'
+
+test_expect_success 'remote detects correct HEAD' '
+ git push public master:other &&
+ (cd clone &&
+ git remote set-head -d origin &&
+ git remote set-head -a origin &&
+ git symbolic-ref refs/remotes/origin/HEAD > output &&
+ echo refs/remotes/origin/master > expect &&
+ test_cmp expect output
+ )
+'
+
+test_expect_success 'prepare pack objects' '
+ cp -R "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo.git "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git &&
+ (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git &&
+ git --bare repack -a -d
+ )
+'
+
+test_expect_success 'fetch notices corrupt pack' '
+ cp -R "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
+ (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
+ p=$(ls objects/pack/pack-*.pack) &&
+ chmod u+w $p &&
+ printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
+ ) &&
+ mkdir repo_bad1.git &&
+ (cd repo_bad1.git &&
+ git --bare init &&
+ test_must_fail git --bare fetch "$GIT_DAEMON_URL/repo_bad1.git" &&
+ test 0 = $(ls objects/pack/pack-*.pack | wc -l)
+ )
+'
+
+test_expect_success 'fetch notices corrupt idx' '
+ cp -R "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+ (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+ p=$(ls objects/pack/pack-*.idx) &&
+ chmod u+w $p &&
+ printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
+ ) &&
+ mkdir repo_bad2.git &&
+ (cd repo_bad2.git &&
+ git --bare init &&
+ test_must_fail git --bare fetch "$GIT_DAEMON_URL/repo_bad2.git" &&
+ test 0 = $(ls objects/pack | wc -l)
+ )
+'
+
+test_remote_error()
+{
+ do_export=YesPlease
+ while test $# -gt 0
+ do
+ case $1 in
+ -x)
+ shift
+ chmod -x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git"
+ ;;
+ -n)
+ shift
+ do_export=
+ ;;
+ *)
+ break
+ esac
+ done
+
+ msg=$1
+ shift
+ cmd=$1
+ shift
+ repo=$1
+ shift || error "invalid number of arguments"
+
+ if test -x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/$repo"
+ then
+ if test -n "$do_export"
+ then
+ : >"$GIT_DAEMON_DOCUMENT_ROOT_PATH/$repo/git-daemon-export-ok"
+ else
+ rm -f "$GIT_DAEMON_DOCUMENT_ROOT_PATH/$repo/git-daemon-export-ok"
+ fi
+ fi
+
+ test_must_fail git "$cmd" "$GIT_DAEMON_URL/$repo" "$@" 2>output &&
+ test_i18ngrep "fatal: remote error: $msg: /$repo" output &&
+ ret=$?
+ chmod +x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git"
+ (exit $ret)
+}
+
+msg="access denied or repository not exported"
+test_expect_success 'clone non-existent' "test_remote_error '$msg' clone nowhere.git "
+test_expect_success 'push disabled' "test_remote_error '$msg' push repo.git master"
+test_expect_success 'read access denied' "test_remote_error -x '$msg' fetch repo.git "
+test_expect_success 'not exported' "test_remote_error -n '$msg' fetch repo.git "
+
+stop_git_daemon
+start_git_daemon --informative-errors
+
+test_expect_success 'clone non-existent' "test_remote_error 'no such repository' clone nowhere.git "
+test_expect_success 'push disabled' "test_remote_error 'service not enabled' push repo.git master"
+test_expect_success 'read access denied' "test_remote_error -x 'no such repository' fetch repo.git "
+test_expect_success 'not exported' "test_remote_error -n 'repository not exported' fetch repo.git "
+
+stop_git_daemon
+start_git_daemon --interpolated-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH/%H%D"
+
+test_expect_success 'access repo via interpolated hostname' '
+ repo="$GIT_DAEMON_DOCUMENT_ROOT_PATH/localhost/interp.git" &&
+ git init --bare "$repo" &&
+ git push "$repo" HEAD &&
+ >"$repo"/git-daemon-export-ok &&
+ rm -rf tmp.git &&
+ GIT_OVERRIDE_VIRTUAL_HOST=localhost \
+ git clone --bare "$GIT_DAEMON_URL/interp.git" tmp.git &&
+ rm -rf tmp.git &&
+ GIT_OVERRIDE_VIRTUAL_HOST=LOCALHOST \
+ git clone --bare "$GIT_DAEMON_URL/interp.git" tmp.git
+'
+
+test_expect_success 'hostname cannot break out of directory' '
+ rm -rf tmp.git &&
+ repo="$GIT_DAEMON_DOCUMENT_ROOT_PATH/../escape.git" &&
+ git init --bare "$repo" &&
+ git push "$repo" HEAD &&
+ >"$repo"/git-daemon-export-ok &&
+ test_must_fail \
+ env GIT_OVERRIDE_VIRTUAL_HOST=.. \
+ git clone --bare "$GIT_DAEMON_URL/escape.git" tmp.git
+'
+
+stop_git_daemon
+test_done
diff --git a/t/t5571-pre-push-hook.sh b/t/t5571-pre-push-hook.sh
new file mode 100755
index 0000000..ba975bb
--- /dev/null
+++ b/t/t5571-pre-push-hook.sh
@@ -0,0 +1,128 @@
+#!/bin/sh
+
+test_description='check pre-push hooks'
+. ./test-lib.sh
+
+# Setup hook that always succeeds
+HOOKDIR="$(git rev-parse --git-dir)/hooks"
+HOOK="$HOOKDIR/pre-push"
+mkdir -p "$HOOKDIR"
+write_script "$HOOK" <<EOF
+cat >/dev/null
+exit 0
+EOF
+
+test_expect_success 'setup' '
+ git config push.default upstream &&
+ git init --bare repo1 &&
+ git remote add parent1 repo1 &&
+ test_commit one &&
+ git push parent1 HEAD:foreign
+'
+write_script "$HOOK" <<EOF
+cat >/dev/null
+exit 1
+EOF
+
+COMMIT1="$(git rev-parse HEAD)"
+export COMMIT1
+
+test_expect_success 'push with failing hook' '
+ test_commit two &&
+ test_must_fail git push parent1 HEAD
+'
+
+test_expect_success '--no-verify bypasses hook' '
+ git push --no-verify parent1 HEAD
+'
+
+COMMIT2="$(git rev-parse HEAD)"
+export COMMIT2
+
+write_script "$HOOK" <<'EOF'
+echo "$1" >actual
+echo "$2" >>actual
+cat >>actual
+EOF
+
+cat >expected <<EOF
+parent1
+repo1
+refs/heads/master $COMMIT2 refs/heads/foreign $COMMIT1
+EOF
+
+test_expect_success 'push with hook' '
+ git push parent1 master:foreign &&
+ diff expected actual
+'
+
+test_expect_success 'add a branch' '
+ git checkout -b other parent1/foreign &&
+ test_commit three
+'
+
+COMMIT3="$(git rev-parse HEAD)"
+export COMMIT3
+
+cat >expected <<EOF
+parent1
+repo1
+refs/heads/other $COMMIT3 refs/heads/foreign $COMMIT2
+EOF
+
+test_expect_success 'push to default' '
+ git push &&
+ diff expected actual
+'
+
+cat >expected <<EOF
+parent1
+repo1
+refs/tags/one $COMMIT1 refs/tags/tag1 $_z40
+HEAD~ $COMMIT2 refs/heads/prev $_z40
+EOF
+
+test_expect_success 'push non-branches' '
+ git push parent1 one:tag1 HEAD~:refs/heads/prev &&
+ diff expected actual
+'
+
+cat >expected <<EOF
+parent1
+repo1
+(delete) $_z40 refs/heads/prev $COMMIT2
+EOF
+
+test_expect_success 'push delete' '
+ git push parent1 :prev &&
+ diff expected actual
+'
+
+cat >expected <<EOF
+repo1
+repo1
+HEAD $COMMIT3 refs/heads/other $_z40
+EOF
+
+test_expect_success 'push to URL' '
+ git push repo1 HEAD &&
+ diff expected actual
+'
+
+test_expect_success 'set up many-ref tests' '
+ {
+ nr=1000
+ while test $nr -lt 2000
+ do
+ nr=$(( $nr + 1 ))
+ echo "create refs/heads/b/$nr $COMMIT3"
+ done
+ } | git update-ref --stdin
+'
+
+test_expect_success 'sigpipe does not cause pre-push hook failure' '
+ echo "exit 0" | write_script "$HOOK" &&
+ git push parent1 "refs/heads/b/*:refs/heads/b/*"
+'
+
+test_done
diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh
new file mode 100755
index 0000000..accfa5c
--- /dev/null
+++ b/t/t5572-pull-submodule.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='pull can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+reset_branch_to_HEAD () {
+ git branch -D "$1" &&
+ git checkout -b "$1" HEAD &&
+ git branch --set-upstream-to="origin/$1" "$1"
+}
+
+git_pull () {
+ reset_branch_to_HEAD "$1" &&
+ git pull
+}
+
+# pulls without conflicts
+test_submodule_switch "git_pull"
+
+git_pull_ff () {
+ reset_branch_to_HEAD "$1" &&
+ git pull --ff
+}
+
+test_submodule_switch "git_pull_ff"
+
+git_pull_ff_only () {
+ reset_branch_to_HEAD "$1" &&
+ git pull --ff-only
+}
+
+test_submodule_switch "git_pull_ff_only"
+
+git_pull_noff () {
+ reset_branch_to_HEAD "$1" &&
+ git pull --no-ff
+}
+
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+test_submodule_switch "git_pull_noff"
+
+test_done
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
index ee06d28..4435693 100755
--- a/t/t5600-clone-fail-cleanup.sh
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -37,6 +37,16 @@ test_expect_success \
test_expect_success \
'successful clone must leave the directory' \
- 'cd bar'
+ 'test -d bar'
+
+test_expect_success 'failed clone --separate-git-dir should not leave any directories' '
+ mkdir foo/.git/objects.bak/ &&
+ mv foo/.git/objects/* foo/.git/objects.bak/ &&
+ test_must_fail git clone --separate-git-dir gitdir foo worktree &&
+ test_must_fail test -e gitdir &&
+ test_must_fail test -e worktree &&
+ mv foo/.git/objects.bak/* foo/.git/objects/ &&
+ rmdir foo/.git/objects.bak
+'
test_done
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 8abb71a..a433394 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -4,15 +4,21 @@ test_description=clone
. ./test-lib.sh
+X=
+test_have_prereq !MINGW || X=.exe
+
test_expect_success setup '
rm -fr .git &&
test_create_repo src &&
(
- cd src
- >file
- git add file
- git commit -m initial
+ cd src &&
+ >file &&
+ git add file &&
+ git commit -m initial &&
+ echo 1 >file &&
+ git add file &&
+ git commit -m updated
)
'
@@ -31,9 +37,9 @@ test_expect_success 'clone with excess parameters (2)' '
'
-test_expect_success 'output from clone' '
+test_expect_success C_LOCALE_OUTPUT 'output from clone' '
rm -fr dst &&
- git clone -n "file://$(pwd)/src" dst >output &&
+ git clone -n "file://$(pwd)/src" dst >output 2>&1 &&
test $(grep Clon output | wc -l) = 1
'
@@ -62,6 +68,29 @@ test_expect_success 'clone respects GIT_WORK_TREE' '
'
+test_expect_success 'clone from hooks' '
+
+ test_create_repo r0 &&
+ cd r0 &&
+ test_commit initial &&
+ cd .. &&
+ git init r1 &&
+ cd r1 &&
+ cat >.git/hooks/pre-commit <<-\EOF &&
+ #!/bin/sh
+ git clone ../r0 ../r2
+ exit 1
+ EOF
+ chmod u+x .git/hooks/pre-commit &&
+ : >file &&
+ git add file &&
+ test_must_fail git commit -m invoke-hook &&
+ cd .. &&
+ test_cmp r0/.git/HEAD r2/.git/HEAD &&
+ test_cmp r0/initial.t r2/initial.t
+
+'
+
test_expect_success 'clone creates intermediate directories' '
git clone src long/path/to/dst &&
@@ -88,6 +117,26 @@ test_expect_success 'clone --mirror' '
'
+test_expect_success 'clone --mirror with detached HEAD' '
+
+ ( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&
+ git clone --mirror src mirror.detached &&
+ ( cd src && git checkout - ) &&
+ GIT_DIR=mirror.detached git rev-parse HEAD >actual &&
+ test_cmp expected actual
+
+'
+
+test_expect_success 'clone --bare with detached HEAD' '
+
+ ( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&
+ git clone --bare src bare.detached &&
+ ( cd src && git checkout - ) &&
+ GIT_DIR=bare.detached git rev-parse HEAD >actual &&
+ test_cmp expected actual
+
+'
+
test_expect_success 'clone --bare names the local repository <name>.git' '
git clone --bare src &&
@@ -163,10 +212,7 @@ test_expect_success 'clone a void' '
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 &&
@@ -178,8 +224,14 @@ test_expect_success 'clone respects global branch.autosetuprebase' '
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
+ git clone "file://$PWD/x+y" xy-url-1 &&
+ git clone "file://$PWD/x%2By" xy-url-2
+'
+
+test_expect_success 'do not query-string-decode + in URLs' '
+ rm -rf x+y &&
+ git init "x y" &&
+ test_must_fail git clone "file://$PWD/x+y" xy-no-plus
'
test_expect_success 'do not respect url-encoding of non-url path' '
@@ -188,4 +240,288 @@ test_expect_success 'do not respect url-encoding of non-url path' '
git clone x+y xy-regular
'
+test_expect_success 'clone separate gitdir' '
+ rm -rf dst &&
+ git clone --separate-git-dir realgitdir src dst &&
+ test -d realgitdir/refs
+'
+
+test_expect_success 'clone separate gitdir: output' '
+ echo "gitdir: $(pwd)/realgitdir" >expected &&
+ test_cmp expected dst/.git
+'
+
+test_expect_success 'clone from .git file' '
+ git clone dst/.git dst2
+'
+
+test_expect_success 'fetch from .git gitfile' '
+ (
+ cd dst2 &&
+ git fetch ../dst/.git
+ )
+'
+
+test_expect_success 'fetch from gitfile parent' '
+ (
+ cd dst2 &&
+ git fetch ../dst
+ )
+'
+
+test_expect_success 'clone separate gitdir where target already exists' '
+ rm -rf dst &&
+ test_must_fail git clone --separate-git-dir realgitdir src dst
+'
+
+test_expect_success 'clone --reference from original' '
+ git clone --shared --bare src src-1 &&
+ git clone --bare src src-2 &&
+ git clone --reference=src-2 --bare src-1 target-8 &&
+ grep /src-2/ target-8/objects/info/alternates
+'
+
+test_expect_success 'clone with more than one --reference' '
+ git clone --bare src src-3 &&
+ git clone --bare src src-4 &&
+ git clone --reference=src-3 --reference=src-4 src target-9 &&
+ grep /src-3/ target-9/.git/objects/info/alternates &&
+ grep /src-4/ target-9/.git/objects/info/alternates
+'
+
+test_expect_success 'clone from original with relative alternate' '
+ mkdir nest &&
+ git clone --bare src nest/src-5 &&
+ echo ../../../src/.git/objects >nest/src-5/objects/info/alternates &&
+ git clone --bare nest/src-5 target-10 &&
+ grep /src/\\.git/objects target-10/objects/info/alternates
+'
+
+test_expect_success 'clone checking out a tag' '
+ git clone --branch=some-tag src dst.tag &&
+ GIT_DIR=src/.git git rev-parse some-tag >expected &&
+ test_cmp expected dst.tag/.git/HEAD &&
+ GIT_DIR=dst.tag/.git git config remote.origin.fetch >fetch.actual &&
+ echo "+refs/heads/*:refs/remotes/origin/*" >fetch.expected &&
+ test_cmp fetch.expected fetch.actual
+'
+
+setup_ssh_wrapper () {
+ test_expect_success 'setup ssh wrapper' '
+ cp "$GIT_BUILD_DIR/t/helper/test-fake-ssh$X" \
+ "$TRASH_DIRECTORY/ssh-wrapper$X" &&
+ GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper$X" &&
+ export GIT_SSH &&
+ export TRASH_DIRECTORY &&
+ >"$TRASH_DIRECTORY"/ssh-output
+ '
+}
+
+copy_ssh_wrapper_as () {
+ cp "$TRASH_DIRECTORY/ssh-wrapper$X" "${1%$X}$X" &&
+ GIT_SSH="${1%$X}$X" &&
+ export GIT_SSH
+}
+
+expect_ssh () {
+ test_when_finished '
+ (cd "$TRASH_DIRECTORY" && rm -f ssh-expect && >ssh-output)
+ ' &&
+ {
+ case "$#" in
+ 1)
+ ;;
+ 2)
+ echo "ssh: $1 git-upload-pack '$2'"
+ ;;
+ 3)
+ echo "ssh: $1 $2 git-upload-pack '$3'"
+ ;;
+ *)
+ echo "ssh: $1 $2 git-upload-pack '$3' $4"
+ esac
+ } >"$TRASH_DIRECTORY/ssh-expect" &&
+ (cd "$TRASH_DIRECTORY" && test_cmp ssh-expect ssh-output)
+}
+
+setup_ssh_wrapper
+
+test_expect_success 'clone myhost:src uses ssh' '
+ git clone myhost:src ssh-clone &&
+ expect_ssh myhost src
+'
+
+test_expect_success !MINGW,!CYGWIN 'clone local path foo:bar' '
+ cp -R src "foo:bar" &&
+ git clone "foo:bar" foobar &&
+ expect_ssh none
+'
+
+test_expect_success 'bracketed hostnames are still ssh' '
+ git clone "[myhost:123]:src" ssh-bracket-clone &&
+ expect_ssh "-p 123" myhost src
+'
+
+test_expect_success 'uplink is not treated as putty' '
+ copy_ssh_wrapper_as "$TRASH_DIRECTORY/uplink" &&
+ git clone "[myhost:123]:src" ssh-bracket-clone-uplink &&
+ expect_ssh "-p 123" myhost src
+'
+
+test_expect_success 'plink is treated specially (as putty)' '
+ copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
+ git clone "[myhost:123]:src" ssh-bracket-clone-plink-0 &&
+ expect_ssh "-P 123" myhost src
+'
+
+test_expect_success 'plink.exe is treated specially (as putty)' '
+ copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink.exe" &&
+ git clone "[myhost:123]:src" ssh-bracket-clone-plink-1 &&
+ expect_ssh "-P 123" myhost src
+'
+
+test_expect_success 'tortoiseplink is like putty, with extra arguments' '
+ copy_ssh_wrapper_as "$TRASH_DIRECTORY/tortoiseplink" &&
+ git clone "[myhost:123]:src" ssh-bracket-clone-plink-2 &&
+ expect_ssh "-batch -P 123" myhost src
+'
+
+# Reset the GIT_SSH environment variable for clone tests.
+setup_ssh_wrapper
+
+counter=0
+# $1 url
+# $2 none|host
+# $3 path
+test_clone_url () {
+ counter=$(($counter + 1))
+ test_might_fail git clone "$1" tmp$counter &&
+ shift &&
+ expect_ssh "$@"
+}
+
+test_expect_success !MINGW 'clone c:temp is ssl' '
+ test_clone_url c:temp c temp
+'
+
+test_expect_success MINGW 'clone c:temp is dos drive' '
+ test_clone_url c:temp none
+'
+
+#ip v4
+for repo in rep rep/home/project 123
+do
+ test_expect_success "clone host:$repo" '
+ test_clone_url host:$repo host $repo
+ '
+done
+
+#ipv6
+for repo in rep rep/home/project 123
+do
+ test_expect_success "clone [::1]:$repo" '
+ test_clone_url [::1]:$repo ::1 "$repo"
+ '
+done
+#home directory
+test_expect_success "clone host:/~repo" '
+ test_clone_url host:/~repo host "~repo"
+'
+
+test_expect_success "clone [::1]:/~repo" '
+ test_clone_url [::1]:/~repo ::1 "~repo"
+'
+
+# Corner cases
+for url in foo/bar:baz [foo]bar/baz:qux [foo/bar]:baz
+do
+ test_expect_success "clone $url is not ssh" '
+ test_clone_url $url none
+ '
+done
+
+#with ssh:// scheme
+#ignore trailing colon
+for tcol in "" :
+do
+ test_expect_success "clone ssh://host.xz$tcol/home/user/repo" '
+ test_clone_url "ssh://host.xz$tcol/home/user/repo" host.xz /home/user/repo
+ '
+ # from home directory
+ test_expect_success "clone ssh://host.xz$tcol/~repo" '
+ test_clone_url "ssh://host.xz$tcol/~repo" host.xz "~repo"
+'
+done
+
+# with port number
+test_expect_success 'clone ssh://host.xz:22/home/user/repo' '
+ test_clone_url "ssh://host.xz:22/home/user/repo" "-p 22 host.xz" "/home/user/repo"
+'
+
+# from home directory with port number
+test_expect_success 'clone ssh://host.xz:22/~repo' '
+ test_clone_url "ssh://host.xz:22/~repo" "-p 22 host.xz" "~repo"
+'
+
+#IPv6
+for tuah in ::1 [::1] [::1]: user@::1 user@[::1] user@[::1]: [user@::1] [user@::1]:
+do
+ ehost=$(echo $tuah | sed -e "s/1]:/1]/" | tr -d "[]")
+ test_expect_success "clone ssh://$tuah/home/user/repo" "
+ test_clone_url ssh://$tuah/home/user/repo $ehost /home/user/repo
+ "
+done
+
+#IPv6 from home directory
+for tuah in ::1 [::1] user@::1 user@[::1] [user@::1]
+do
+ euah=$(echo $tuah | tr -d "[]")
+ test_expect_success "clone ssh://$tuah/~repo" "
+ test_clone_url ssh://$tuah/~repo $euah '~repo'
+ "
+done
+
+#IPv6 with port number
+for tuah in [::1] user@[::1] [user@::1]
+do
+ euah=$(echo $tuah | tr -d "[]")
+ test_expect_success "clone ssh://$tuah:22/home/user/repo" "
+ test_clone_url ssh://$tuah:22/home/user/repo '-p 22' $euah /home/user/repo
+ "
+done
+
+#IPv6 from home directory with port number
+for tuah in [::1] user@[::1] [user@::1]
+do
+ euah=$(echo $tuah | tr -d "[]")
+ test_expect_success "clone ssh://$tuah:22/~repo" "
+ test_clone_url ssh://$tuah:22/~repo '-p 22' $euah '~repo'
+ "
+done
+
+test_expect_success 'clone from a repository with two identical branches' '
+
+ (
+ cd src &&
+ git checkout -b another master
+ ) &&
+ git clone src target-11 &&
+ test "z$( cd target-11 && git symbolic-ref HEAD )" = zrefs/heads/another
+
+'
+
+test_expect_success 'shallow clone locally' '
+ git clone --depth=1 --no-local src ssrrcc &&
+ git clone ssrrcc ddsstt &&
+ test_cmp ssrrcc/.git/shallow ddsstt/.git/shallow &&
+ ( cd ddsstt && git fsck )
+'
+
+test_expect_success 'GIT_TRACE_PACKFILE produces a usable pack' '
+ rm -rf dst.git &&
+ GIT_TRACE_PACKFILE=$PWD/tmp.pack git clone --no-local --bare src dst.git &&
+ git init --bare replay.git &&
+ git -C replay.git index-pack -v --stdin <tmp.pack
+'
+
test_done
diff --git a/t/t5602-clone-remote-exec.sh b/t/t5602-clone-remote-exec.sh
index deffdae..cbcceab 100755
--- a/t/t5602-clone-remote-exec.sh
+++ b/t/t5602-clone-remote-exec.sh
@@ -5,21 +5,22 @@ 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
+ 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_must_fail env 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_must_fail env 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
'
diff --git a/t/t5603-clone-dirname.sh b/t/t5603-clone-dirname.sh
new file mode 100755
index 0000000..d5af758
--- /dev/null
+++ b/t/t5603-clone-dirname.sh
@@ -0,0 +1,106 @@
+#!/bin/sh
+
+test_description='check output directory names used by git-clone'
+. ./test-lib.sh
+
+# we use a fake ssh wrapper that ignores the arguments
+# entirely; we really only care that we get _some_ repo,
+# as the real test is what clone does on the local side
+test_expect_success 'setup ssh wrapper' '
+ write_script "$TRASH_DIRECTORY/ssh-wrapper" <<-\EOF &&
+ git upload-pack "$TRASH_DIRECTORY"
+ EOF
+ GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper" &&
+ export GIT_SSH &&
+ export TRASH_DIRECTORY
+'
+
+# make sure that cloning $1 results in local directory $2
+test_clone_dir () {
+ url=$1; shift
+ dir=$1; shift
+ expect=success
+ bare=non-bare
+ clone_opts=
+ for i in "$@"
+ do
+ case "$i" in
+ fail)
+ expect=failure
+ ;;
+ bare)
+ bare=bare
+ clone_opts=--bare
+ ;;
+ esac
+ done
+ test_expect_$expect "clone of $url goes to $dir ($bare)" "
+ rm -rf $dir &&
+ git clone $clone_opts $url &&
+ test_path_is_dir $dir
+ "
+}
+
+# basic syntax with bare and non-bare variants
+test_clone_dir host:foo foo
+test_clone_dir host:foo foo.git bare
+test_clone_dir host:foo.git foo
+test_clone_dir host:foo.git foo.git bare
+test_clone_dir host:foo/.git foo
+test_clone_dir host:foo/.git foo.git bare
+
+# similar, but using ssh URL rather than host:path syntax
+test_clone_dir ssh://host/foo foo
+test_clone_dir ssh://host/foo foo.git bare
+test_clone_dir ssh://host/foo.git foo
+test_clone_dir ssh://host/foo.git foo.git bare
+test_clone_dir ssh://host/foo/.git foo
+test_clone_dir ssh://host/foo/.git foo.git bare
+
+# we should remove trailing slashes and .git suffixes
+test_clone_dir ssh://host/foo/ foo
+test_clone_dir ssh://host/foo/// foo
+test_clone_dir ssh://host/foo/.git/ foo
+test_clone_dir ssh://host/foo.git/ foo
+test_clone_dir ssh://host/foo.git/// foo
+test_clone_dir ssh://host/foo///.git/ foo
+test_clone_dir ssh://host/foo/.git/// foo
+
+test_clone_dir host:foo/ foo
+test_clone_dir host:foo/// foo
+test_clone_dir host:foo.git/ foo
+test_clone_dir host:foo/.git/ foo
+test_clone_dir host:foo.git/// foo
+test_clone_dir host:foo///.git/ foo
+test_clone_dir host:foo/.git/// foo
+
+# omitting the path should default to the hostname
+test_clone_dir ssh://host/ host
+test_clone_dir ssh://host:1234/ host
+test_clone_dir ssh://user@host/ host
+test_clone_dir host:/ host
+
+# auth materials should be redacted
+test_clone_dir ssh://user:password@host/ host
+test_clone_dir ssh://user:password@host:1234/ host
+test_clone_dir ssh://user:passw@rd@host:1234/ host
+test_clone_dir user@host:/ host
+test_clone_dir user:password@host:/ host
+test_clone_dir user:passw@rd@host:/ host
+
+# auth-like material should not be dropped
+test_clone_dir ssh://host/foo@bar foo@bar
+test_clone_dir ssh://host/foo@bar.git foo@bar
+test_clone_dir ssh://user:password@host/foo@bar foo@bar
+test_clone_dir ssh://user:passw@rd@host/foo@bar.git foo@bar
+
+test_clone_dir host:/foo@bar foo@bar
+test_clone_dir host:/foo@bar.git foo@bar
+test_clone_dir user:password@host:/foo@bar foo@bar
+test_clone_dir user:passw@rd@host:/foo@bar.git foo@bar
+
+# trailing port-like numbers should not be stripped for paths
+test_clone_dir ssh://user:password@host/test:1234 1234
+test_clone_dir ssh://user:password@host/test:1234.git 1234
+
+test_done
diff --git a/t/t5604-clone-reference.sh b/t/t5604-clone-reference.sh
new file mode 100755
index 0000000..4320082
--- /dev/null
+++ b/t/t5604-clone-reference.sh
@@ -0,0 +1,224 @@
+#!/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
+
+# create a commit in repo $1 with name $2
+commit_in () {
+ (
+ cd "$1" &&
+ echo "$2" >"$2" &&
+ git add "$2" &&
+ git commit -m "$2"
+ )
+}
+
+# check that there are $2 loose objects in repo $1
+test_objcount () {
+ echo "$2" >expect &&
+ git -C "$1" count-objects >actual.raw &&
+ cut -d' ' -f1 <actual.raw >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'preparing first repository' '
+ test_create_repo A &&
+ commit_in A file1
+'
+
+test_expect_success 'preparing second repository' '
+ git clone A B &&
+ commit_in B file2 &&
+ git -C B repack -ad &&
+ git -C B prune
+'
+
+test_expect_success 'cloning with reference (-l -s)' '
+ git clone -l -s --reference B A C
+'
+
+test_expect_success 'existence of info/alternates' '
+ test_line_count = 2 C/.git/objects/info/alternates
+'
+
+test_expect_success 'pulling from reference' '
+ git -C C pull ../B master
+'
+
+test_expect_success 'that reference gets used' '
+ test_objcount C 0
+'
+
+test_expect_success 'cloning with reference (no -l -s)' '
+ GIT_TRACE_PACKET=$U.D git clone --reference B "file://$(pwd)/A" D
+'
+
+test_expect_success 'fetched no objects' '
+ test -s "$U.D" &&
+ ! grep " want" "$U.D"
+'
+
+test_expect_success 'existence of info/alternates' '
+ test_line_count = 1 D/.git/objects/info/alternates
+'
+
+test_expect_success 'pulling from reference' '
+ git -C D pull ../B master
+'
+
+test_expect_success 'that reference gets used' '
+ test_objcount D 0
+'
+
+test_expect_success 'updating origin' '
+ commit_in A file3 &&
+ git -C A repack -ad &&
+ git -C A prune
+'
+
+test_expect_success 'pulling changes from origin' '
+ git -C C pull origin
+'
+
+# the 2 local objects are commit and tree from the merge
+test_expect_success 'that alternate to origin gets used' '
+ test_objcount C 2
+'
+
+test_expect_success 'pulling changes from origin' '
+ git -C D pull origin
+'
+
+# 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' '
+ test_objcount D 5
+'
+
+test_expect_success 'preparing alternate repository #1' '
+ test_create_repo F &&
+ commit_in F file1
+'
+
+test_expect_success 'cloning alternate repo #2 and adding changes to repo #1' '
+ git clone F G &&
+ commit_in F file2
+'
+
+test_expect_success 'cloning alternate repo #1, using #2 as reference' '
+ git clone --reference G F H
+'
+
+test_expect_success 'cloning with reference being subset of source (-l -s)' '
+ git clone -l -s --reference A B E
+'
+
+test_expect_success 'cloning with multiple references drops duplicates' '
+ git clone -s --reference B --reference A --reference B A dups &&
+ test_line_count = 2 dups/.git/objects/info/alternates
+'
+
+test_expect_success 'clone with reference from a tagged repository' '
+ (
+ cd A && git tag -a -m tagged HEAD
+ ) &&
+ git clone --reference=A A I
+'
+
+test_expect_success 'prepare branched repository' '
+ git clone A J &&
+ (
+ cd J &&
+ git checkout -b other master^ &&
+ echo other >otherfile &&
+ git add otherfile &&
+ git commit -m other &&
+ git checkout master
+ )
+'
+
+test_expect_success 'fetch with incomplete alternates' '
+ git init K &&
+ echo "$base_dir/A/.git/objects" >K/.git/objects/info/alternates &&
+ (
+ cd K &&
+ git remote add J "file://$base_dir/J" &&
+ GIT_TRACE_PACKET=$U.K git fetch J
+ ) &&
+ master_object=$(cd A && git for-each-ref --format="%(objectname)" refs/heads/master) &&
+ test -s "$U.K" &&
+ ! grep " want $master_object" "$U.K" &&
+ tag_object=$(cd A && git for-each-ref --format="%(objectname)" refs/tags/HEAD) &&
+ ! grep " want $tag_object" "$U.K"
+'
+
+test_expect_success 'clone using repo with gitfile as a reference' '
+ git clone --separate-git-dir=L A M &&
+ git clone --reference=M A N &&
+ echo "$base_dir/L/objects" >expected &&
+ test_cmp expected "$base_dir/N/.git/objects/info/alternates"
+'
+
+test_expect_success 'clone using repo pointed at by gitfile as reference' '
+ git clone --reference=M/.git A O &&
+ echo "$base_dir/L/objects" >expected &&
+ test_cmp expected "$base_dir/O/.git/objects/info/alternates"
+'
+
+test_expect_success 'clone and dissociate from reference' '
+ git init P &&
+ (
+ cd P && test_commit one
+ ) &&
+ git clone P Q &&
+ (
+ cd Q && test_commit two
+ ) &&
+ git clone --no-local --reference=P Q R &&
+ git clone --no-local --reference=P --dissociate Q S &&
+ # removing the reference P would corrupt R but not S
+ rm -fr P &&
+ test_must_fail git -C R fsck &&
+ git -C S fsck
+'
+test_expect_success 'clone, dissociate from partial reference and repack' '
+ rm -fr P Q R &&
+ git init P &&
+ (
+ cd P &&
+ test_commit one &&
+ git repack &&
+ test_commit two &&
+ git repack
+ ) &&
+ git clone --bare P Q &&
+ (
+ cd P &&
+ git checkout -b second &&
+ test_commit three &&
+ git repack
+ ) &&
+ git clone --bare --dissociate --reference=P Q R &&
+ ls R/objects/pack/*.pack >packs.txt &&
+ test_line_count = 1 packs.txt
+'
+
+test_expect_success 'clone, dissociate from alternates' '
+ rm -fr A B C &&
+ test_create_repo A &&
+ commit_in A file1 &&
+ git clone --reference=A A B &&
+ test_line_count = 1 B/.git/objects/info/alternates &&
+ git clone --local --dissociate B C &&
+ ! test -f C/.git/objects/info/alternates &&
+ ( cd C && git fsck )
+'
+
+test_done
diff --git a/t/t5701-clone-local.sh b/t/t5605-clone-local.sh
index 8b4c356..3c087e9 100755
--- a/t/t5701-clone-local.sh
+++ b/t/t5605-clone-local.sh
@@ -3,121 +3,91 @@
test_description='test local clone'
. ./test-lib.sh
-D=`pwd`
+repo_is_hardlinked() {
+ find "$1/objects" -type f -links 1 >output &&
+ test_line_count = 0 output
+}
test_expect_success 'preparing origin repository' '
: >file && git add . && git commit -m1 &&
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
+ test "$(cd a.git && git config --bool core.bare)" = true &&
+ test "$(cd x && 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 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
+ (cd b &&
+ test "$(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
+ (cd c && git fetch)
'
test_expect_success 'local clone from x' '
- cd "$D" &&
git clone -l -s x y &&
- cd y &&
- git fetch
+ (cd y && git fetch)
'
test_expect_success 'local clone from x.git that does not exist' '
- cd "$D" &&
- if git clone -l -s x.git z
- then
- echo "Oops, should have failed"
- false
- else
- echo happy
- fi
+ test_must_fail git clone -l -s x.git z
'
test_expect_success 'With -no-hardlinks, local will make a copy' '
- cd "$D" &&
git clone --bare --no-hardlinks x w &&
- cd w &&
- linked=$(find objects -type f ! -links 1 | wc -l) &&
- test 0 = $linked
+ ! repo_is_hardlinked w
'
test_expect_success 'Even without -l, local will make a hardlink' '
- cd "$D" &&
rm -fr w &&
git clone -l --bare x w &&
- cd w &&
- copied=$(find objects -type f -links 1 | wc -l) &&
- test 0 = $copied
+ repo_is_hardlinked w
'
test_expect_success 'local clone of repo with nonexistent ref in HEAD' '
- cd "$D" &&
echo "ref: refs/heads/nonexistent" > a.git/HEAD &&
git clone a d &&
- cd d &&
+ (cd d &&
git fetch &&
- test ! -e .git/refs/remotes/origin/HEAD'
+ test ! -e .git/refs/remotes/origin/HEAD)
+'
test_expect_success 'bundle clone without .bundle suffix' '
- cd "$D" &&
git clone dir/b3 &&
- cd b3 &&
- git fetch
+ (cd b3 && git fetch)
'
test_expect_success 'bundle clone with .bundle suffix' '
- cd "$D" &&
git clone b1.bundle &&
- cd b1 &&
- git fetch
+ (cd b1 && git fetch)
'
test_expect_success 'bundle clone from b4' '
- cd "$D" &&
git clone b4 bdl &&
- cd bdl &&
- git fetch
+ (cd bdl && git fetch)
'
test_expect_success 'bundle clone from b4.bundle that does not exist' '
- cd "$D" &&
- if git clone b4.bundle bb
- then
- echo "Oops, should have failed"
- false
- else
- echo happy
- fi
+ test_must_fail git clone b4.bundle bb
'
test_expect_success 'bundle clone with nonexistent HEAD' '
- cd "$D" &&
git clone b2.bundle b2 &&
- cd b2 &&
- git fetch
- test ! -e .git/refs/heads/master
+ (cd b2 &&
+ git fetch &&
+ test_must_fail git rev-parse --verify refs/heads/master)
'
test_expect_success 'clone empty repository' '
- cd "$D" &&
mkdir empty &&
(cd empty &&
git init &&
@@ -135,7 +105,6 @@ test_expect_success 'clone empty repository' '
'
test_expect_success 'clone empty repository, and then push should not segfault.' '
- cd "$D" &&
rm -fr empty/ empty-clone/ &&
mkdir empty &&
(cd empty && git init) &&
@@ -144,4 +113,29 @@ test_expect_success 'clone empty repository, and then push should not segfault.'
test_must_fail git push)
'
+test_expect_success 'cloning non-existent directory fails' '
+ rm -rf does-not-exist &&
+ test_must_fail git clone does-not-exist
+'
+
+test_expect_success 'cloning non-git directory fails' '
+ rm -rf not-a-git-repo not-a-git-repo-clone &&
+ mkdir not-a-git-repo &&
+ test_must_fail git clone not-a-git-repo not-a-git-repo-clone
+'
+
+test_expect_success 'cloning file:// does not hardlink' '
+ git clone --bare file://"$(pwd)"/a non-local &&
+ ! repo_is_hardlinked non-local
+'
+
+test_expect_success 'cloning a local path with --no-local does not hardlink' '
+ git clone --bare --no-local a force-nonlocal &&
+ ! repo_is_hardlinked force-nonlocal
+'
+
+test_expect_success 'cloning locally respects "-u" for fetching refs' '
+ test_must_fail git clone --bare -u false a should_not_work.git
+'
+
test_done
diff --git a/t/t5702-clone-options.sh b/t/t5606-clone-options.sh
index 02cb024..9e24ec8 100755
--- a/t/t5702-clone-options.sh
+++ b/t/t5606-clone-options.sh
@@ -19,17 +19,19 @@ test_expect_success 'clone -o' '
'
-test_expect_success 'redirected clone' '
+test_expect_success 'redirected clone does not show progress' '
git clone "file://$(pwd)/parent" clone-redirected >out 2>err &&
- test ! -s err
+ ! grep % err &&
+ test_i18ngrep ! "Checking connectivity" err
'
-test_expect_success 'redirected clone -v' '
+
+test_expect_success 'redirected clone -v does show progress' '
git clone --progress "file://$(pwd)/parent" clone-redirected-progress \
>out 2>err &&
- test -s err
+ grep % err
'
diff --git a/t/t5607-clone-bundle.sh b/t/t5607-clone-bundle.sh
new file mode 100755
index 0000000..348d9b3
--- /dev/null
+++ b/t/t5607-clone-bundle.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+test_description='some bundle related tests'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit initial &&
+ test_tick &&
+ git tag -m tag tag &&
+ test_commit second &&
+ test_commit third &&
+ git tag -d initial &&
+ git tag -d second &&
+ git tag -d third
+'
+
+test_expect_success 'annotated tags can be excluded by rev-list options' '
+ git bundle create bundle --all --since=7.Apr.2005.15:14:00.-0700 &&
+ git ls-remote bundle > output &&
+ grep tag output &&
+ git bundle create bundle --all --since=7.Apr.2005.15:16:00.-0700 &&
+ git ls-remote bundle > output &&
+ ! grep tag output
+'
+
+test_expect_success 'die if bundle file cannot be created' '
+ mkdir adir &&
+ test_must_fail git bundle create adir --all
+'
+
+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_expect_success 'empty bundle file is rejected' '
+ : >empty-bundle &&
+ test_must_fail git fetch empty-bundle
+'
+
+# This triggers a bug in older versions where the resulting line (with
+# --pretty=oneline) was longer than a 1024-char buffer.
+test_expect_success 'ridiculously long subject in boundary' '
+ : >file4 &&
+ test_tick &&
+ git add file4 &&
+ printf "%01200d\n" 0 | git commit -F - &&
+ test_commit fifth &&
+ git bundle create long-subject-bundle.bdl HEAD^..HEAD &&
+ git bundle list-heads long-subject-bundle.bdl >heads &&
+ test -s heads &&
+ git fetch long-subject-bundle.bdl &&
+ sed -n "/^-/{p;q;}" long-subject-bundle.bdl >boundary &&
+ grep "^-[0-9a-f]\\{40\\} " boundary
+'
+
+test_expect_success 'prerequisites with an empty commit message' '
+ : >file1 &&
+ git add file1 &&
+ test_tick &&
+ git commit --allow-empty-message -m "" &&
+ test_commit file2 &&
+ git bundle create bundle HEAD^.. &&
+ git bundle verify bundle
+'
+
+test_done
diff --git a/t/t5705-clone-2gb.sh b/t/t5608-clone-2gb.sh
index e4d1b6a..191d6d3 100755
--- a/t/t5705-clone-2gb.sh
+++ b/t/t5608-clone-2gb.sh
@@ -3,12 +3,14 @@
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
+if test -z "$GIT_TEST_CLONE_2GB"
+then
+ say 'Skipping expensive 2GB clone test; enable it with GIT_TEST_CLONE_2GB=t'
+else
+ test_set_prereq CLONE_2GB
+fi
-test_expect_success 'setup' '
+test_expect_success CLONE_2GB 'setup' '
git config pack.compression 0 &&
git config pack.depth 0 &&
@@ -36,15 +38,15 @@ test_expect_success 'setup' '
'
-test_expect_success 'clone - bare' '
+test_expect_success CLONE_2GB 'clone - bare' '
git clone --bare --no-hardlinks . clone-bare
'
-test_expect_success 'clone - with worktree, file:// protocol' '
+test_expect_success CLONE_2GB 'clone - with worktree, file:// protocol' '
- git clone file://. clone-wt
+ git clone "file://$(pwd)" clone-wt
'
diff --git a/t/t5706-clone-branch.sh b/t/t5609-clone-branch.sh
index f3f9a76..6e7a7be 100755
--- a/t/t5706-clone-branch.sh
+++ b/t/t5609-clone-branch.sh
@@ -20,7 +20,9 @@ test_expect_success 'setup' '
echo one >file && git add file && git commit -m one &&
git checkout -b two &&
echo two >file && git add file && git commit -m two &&
- git checkout master)
+ git checkout master) &&
+ mkdir empty &&
+ (cd empty && git init)
'
test_expect_success 'vanilla clone chooses HEAD' '
@@ -57,12 +59,12 @@ test_expect_success 'clone -b does not munge remotes/origin/HEAD' '
)
'
-test_expect_success 'clone -b with bogus branch chooses HEAD' '
- git clone -b bogus parent clone-bogus &&
- (cd clone-bogus &&
- check_HEAD master &&
- check_file one
- )
+test_expect_success 'clone -b with bogus branch' '
+ test_must_fail git clone -b bogus parent clone-bogus
+'
+
+test_expect_success 'clone -b not allowed with empty repos' '
+ test_must_fail git clone -b branch empty clone-branch-empty
'
test_done
diff --git a/t/t5610-clone-detached.sh b/t/t5610-clone-detached.sh
new file mode 100755
index 0000000..8b0d607
--- /dev/null
+++ b/t/t5610-clone-detached.sh
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+test_description='test cloning a repository with detached HEAD'
+. ./test-lib.sh
+
+head_is_detached() {
+ git --git-dir=$1/.git rev-parse --verify HEAD &&
+ test_must_fail git --git-dir=$1/.git symbolic-ref HEAD
+}
+
+test_expect_success 'setup' '
+ echo one >file &&
+ git add file &&
+ git commit -m one &&
+ echo two >file &&
+ git commit -a -m two &&
+ git tag two &&
+ echo three >file &&
+ git commit -a -m three
+'
+
+test_expect_success 'clone repo (detached HEAD points to branch)' '
+ git checkout master^0 &&
+ git clone "file://$PWD" detached-branch
+'
+test_expect_success 'cloned HEAD matches' '
+ echo three >expect &&
+ git --git-dir=detached-branch/.git log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+test_expect_failure 'cloned HEAD is detached' '
+ head_is_detached detached-branch
+'
+
+test_expect_success 'clone repo (detached HEAD points to tag)' '
+ git checkout two^0 &&
+ git clone "file://$PWD" detached-tag
+'
+test_expect_success 'cloned HEAD matches' '
+ echo two >expect &&
+ git --git-dir=detached-tag/.git log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+test_expect_success 'cloned HEAD is detached' '
+ head_is_detached detached-tag
+'
+
+test_expect_success 'clone repo (detached HEAD points to history)' '
+ git checkout two^ &&
+ git clone "file://$PWD" detached-history
+'
+test_expect_success 'cloned HEAD matches' '
+ echo one >expect &&
+ git --git-dir=detached-history/.git log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+test_expect_success 'cloned HEAD is detached' '
+ head_is_detached detached-history
+'
+
+test_expect_success 'clone repo (orphan detached HEAD)' '
+ git checkout master^0 &&
+ echo four >file &&
+ git commit -a -m four &&
+ git clone "file://$PWD" detached-orphan
+'
+test_expect_success 'cloned HEAD matches' '
+ echo four >expect &&
+ git --git-dir=detached-orphan/.git log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+test_expect_success 'cloned HEAD is detached' '
+ head_is_detached detached-orphan
+'
+
+test_done
diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh
new file mode 100755
index 0000000..e4850b7
--- /dev/null
+++ b/t/t5611-clone-config.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='tests for git clone -c key=value'
+. ./test-lib.sh
+
+test_expect_success 'clone -c sets config in cloned repo' '
+ rm -rf child &&
+ git clone -c core.foo=bar . child &&
+ echo bar >expect &&
+ git --git-dir=child/.git config core.foo >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone -c can set multi-keys' '
+ rm -rf child &&
+ git clone -c core.foo=bar -c core.foo=baz . child &&
+ { echo bar; echo baz; } >expect &&
+ git --git-dir=child/.git config --get-all core.foo >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone -c without a value is boolean true' '
+ rm -rf child &&
+ git clone -c core.foo . child &&
+ echo true >expect &&
+ git --git-dir=child/.git config --bool core.foo >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone -c config is available during clone' '
+ echo content >file &&
+ git add file &&
+ git commit -m one &&
+ rm -rf child &&
+ git clone -c core.autocrlf . child &&
+ printf "content\\r\\n" >expect &&
+ test_cmp expect child/file
+'
+
+# Tests for the hidden file attribute on windows
+is_hidden () {
+ # Use the output of `attrib`, ignore the absolute path
+ case "$(attrib "$1")" in *H*?:*) return 0;; esac
+ return 1
+}
+
+test_expect_success MINGW 'clone -c core.hideDotFiles' '
+ test_commit attributes .gitattributes "" &&
+ rm -rf child &&
+ git clone -c core.hideDotFiles=false . child &&
+ ! is_hidden child/.gitattributes &&
+ rm -rf child &&
+ git clone -c core.hideDotFiles=dotGitOnly . child &&
+ ! is_hidden child/.gitattributes &&
+ rm -rf child &&
+ git clone -c core.hideDotFiles=true . child &&
+ is_hidden child/.gitattributes
+'
+
+test_done
diff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh
new file mode 100755
index 0000000..7ace253
--- /dev/null
+++ b/t/t5612-clone-refspec.sh
@@ -0,0 +1,156 @@
+#!/bin/sh
+
+test_description='test refspec written by clone-command'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ # Make two branches, "master" and "side"
+ echo one >file &&
+ git add file &&
+ git commit -m one &&
+ echo two >file &&
+ git commit -a -m two &&
+ git tag two &&
+ echo three >file &&
+ git commit -a -m three &&
+ git checkout -b side &&
+ echo four >file &&
+ git commit -a -m four &&
+ git checkout master &&
+
+ # default clone
+ git clone . dir_all &&
+
+ # default --single that follows HEAD=master
+ git clone --single-branch . dir_master &&
+
+ # default --single that follows HEAD=side
+ git checkout side &&
+ git clone --single-branch . dir_side &&
+
+ # explicit --single that follows side
+ git checkout master &&
+ git clone --single-branch --branch side . dir_side2 &&
+
+ # default --single with --mirror
+ git clone --single-branch --mirror . dir_mirror &&
+
+ # default --single with --branch and --mirror
+ git clone --single-branch --mirror --branch side . dir_mirror_side &&
+
+ # --single that does not know what branch to follow
+ git checkout two^ &&
+ git clone --single-branch . dir_detached &&
+
+ # explicit --single with tag
+ git clone --single-branch --branch two . dir_tag &&
+
+ # advance both "master" and "side" branches
+ git checkout side &&
+ echo five >file &&
+ git commit -a -m five &&
+ git checkout master &&
+ echo six >file &&
+ git commit -a -m six &&
+
+ # update tag
+ git tag -d two && git tag two
+'
+
+test_expect_success 'by default all branches will be kept updated' '
+ (
+ cd dir_all && git fetch &&
+ git for-each-ref refs/remotes/origin |
+ sed -e "/HEAD$/d" \
+ -e "s|/remotes/origin/|/heads/|" >../actual
+ ) &&
+ # follow both master and side
+ git for-each-ref refs/heads >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'by default no tags will be kept updated' '
+ (
+ cd dir_all && git fetch &&
+ git for-each-ref refs/tags >../actual
+ ) &&
+ git for-each-ref refs/tags >expect &&
+ test_must_fail test_cmp expect actual
+'
+
+test_expect_success '--single-branch while HEAD pointing at master' '
+ (
+ cd dir_master && git fetch &&
+ git for-each-ref refs/remotes/origin |
+ sed -e "/HEAD$/d" \
+ -e "s|/remotes/origin/|/heads/|" >../actual
+ ) &&
+ # only follow master
+ git for-each-ref refs/heads/master >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '--single-branch while HEAD pointing at side' '
+ (
+ cd dir_side && git fetch &&
+ git for-each-ref refs/remotes/origin |
+ sed -e "/HEAD$/d" \
+ -e "s|/remotes/origin/|/heads/|" >../actual
+ ) &&
+ # only follow side
+ git for-each-ref refs/heads/side >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '--single-branch with explicit --branch side' '
+ (
+ cd dir_side2 && git fetch &&
+ git for-each-ref refs/remotes/origin |
+ sed -e "/HEAD$/d" \
+ -e "s|/remotes/origin/|/heads/|" >../actual
+ ) &&
+ # only follow side
+ git for-each-ref refs/heads/side >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '--single-branch with explicit --branch with tag fetches updated tag' '
+ (
+ cd dir_tag && git fetch &&
+ git for-each-ref refs/tags >../actual
+ ) &&
+ git for-each-ref refs/tags >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '--single-branch with --mirror' '
+ (
+ cd dir_mirror && git fetch &&
+ git for-each-ref refs > ../actual
+ ) &&
+ git for-each-ref refs >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '--single-branch with explicit --branch and --mirror' '
+ (
+ cd dir_mirror_side && git fetch &&
+ git for-each-ref refs > ../actual
+ ) &&
+ git for-each-ref refs >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '--single-branch with detached' '
+ (
+ cd dir_detached && git fetch &&
+ git for-each-ref refs/remotes/origin |
+ sed -e "/HEAD$/d" \
+ -e "s|/remotes/origin/|/heads/|" >../actual
+ ) &&
+ # nothing
+ >expect &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t5710-info-alternate.sh b/t/t5613-info-alternate.sh
index ef7127c..9cd2626 100755
--- a/t/t5710-info-alternate.sh
+++ b/t/t5613-info-alternate.sh
@@ -18,10 +18,10 @@ reachable_via() {
test_valid_repo() {
git fsck --full > fsck.log &&
- test `wc -l < fsck.log` = 0
+ test_line_count = 0 fsck.log
}
-base_dir=`pwd`
+base_dir=$(pwd)
test_expect_success 'preparing first repository' \
'test_create_repo A && cd A &&
@@ -58,7 +58,7 @@ test_expect_success 'creating too deep nesting' \
git clone -l -s D E &&
git clone -l -s E F &&
git clone -l -s F G &&
-git clone -l -s G H'
+git clone --bare -l -s G H'
test_expect_success 'invalidity of deepest repository' \
'cd H && {
diff --git a/t/t5614-clone-submodules.sh b/t/t5614-clone-submodules.sh
new file mode 100755
index 0000000..da2a67f
--- /dev/null
+++ b/t/t5614-clone-submodules.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='Test shallow cloning of repos with submodules'
+
+. ./test-lib.sh
+
+pwd=$(pwd)
+
+test_expect_success 'setup' '
+ git checkout -b master &&
+ test_commit commit1 &&
+ test_commit commit2 &&
+ mkdir sub &&
+ (
+ cd sub &&
+ git init &&
+ test_commit subcommit1 &&
+ test_commit subcommit2 &&
+ test_commit subcommit3
+ ) &&
+ git submodule add "file://$pwd/sub" sub &&
+ git commit -m "add submodule"
+'
+
+test_expect_success 'nonshallow clone implies nonshallow submodule' '
+ test_when_finished "rm -rf super_clone" &&
+ git clone --recurse-submodules "file://$pwd/." super_clone &&
+ git -C super_clone log --oneline >lines &&
+ test_line_count = 3 lines &&
+ git -C super_clone/sub log --oneline >lines &&
+ test_line_count = 3 lines
+'
+
+test_expect_success 'shallow clone with shallow submodule' '
+ test_when_finished "rm -rf super_clone" &&
+ git clone --recurse-submodules --depth 2 --shallow-submodules "file://$pwd/." super_clone &&
+ git -C super_clone log --oneline >lines &&
+ test_line_count = 2 lines &&
+ git -C super_clone/sub log --oneline >lines &&
+ test_line_count = 1 lines
+'
+
+test_expect_success 'shallow clone does not imply shallow submodule' '
+ test_when_finished "rm -rf super_clone" &&
+ git clone --recurse-submodules --depth 2 "file://$pwd/." super_clone &&
+ git -C super_clone log --oneline >lines &&
+ test_line_count = 2 lines &&
+ git -C super_clone/sub log --oneline >lines &&
+ test_line_count = 3 lines
+'
+
+test_expect_success 'shallow clone with non shallow submodule' '
+ test_when_finished "rm -rf super_clone" &&
+ git clone --recurse-submodules --depth 2 --no-shallow-submodules "file://$pwd/." super_clone &&
+ git -C super_clone log --oneline >lines &&
+ test_line_count = 2 lines &&
+ git -C super_clone/sub log --oneline >lines &&
+ test_line_count = 3 lines
+'
+
+test_expect_success 'non shallow clone with shallow submodule' '
+ test_when_finished "rm -rf super_clone" &&
+ git clone --recurse-submodules --no-local --shallow-submodules "file://$pwd/." super_clone &&
+ git -C super_clone log --oneline >lines &&
+ test_line_count = 3 lines &&
+ git -C super_clone/sub log --oneline >lines &&
+ test_line_count = 1 lines
+'
+
+test_done
diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh
deleted file mode 100755
index 895f559..0000000
--- a/t/t5700-clone-reference.sh
+++ /dev/null
@@ -1,149 +0,0 @@
-#!/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/t5704-bundle.sh b/t/t5704-bundle.sh
deleted file mode 100755
index ddc3dc5..0000000
--- a/t/t5704-bundle.sh
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/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/t5800-remote-helpers.sh b/t/t5800-remote-helpers.sh
deleted file mode 100755
index 637d8e9..0000000
--- a/t/t5800-remote-helpers.sh
+++ /dev/null
@@ -1,82 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2010 Sverre Rabbelier
-#
-
-test_description='Test remote-helper import and export commands'
-
-. ./test-lib.sh
-
-if test_have_prereq PYTHON && "$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/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh
new file mode 100755
index 0000000..362b158
--- /dev/null
+++ b/t/t5801-remote-helpers.sh
@@ -0,0 +1,304 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Sverre Rabbelier
+#
+
+test_description='Test remote-helper import and export commands'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
+
+compare_refs() {
+ git --git-dir="$1/.git" rev-parse --verify $2 >expect &&
+ git --git-dir="$3/.git" rev-parse --verify $4 >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'setup repository' '
+ git init server &&
+ (cd server &&
+ echo content >file &&
+ git add file &&
+ git commit -m one)
+'
+
+test_expect_success 'cloning from local repo' '
+ git clone "testgit::${PWD}/server" local &&
+ test_cmp server/file local/file
+'
+
+test_expect_success 'create new commit on remote' '
+ (cd server &&
+ echo content >>file &&
+ git commit -a -m two)
+'
+
+test_expect_success 'pulling from local repo' '
+ (cd local && git pull) &&
+ test_cmp server/file local/file
+'
+
+test_expect_success 'pushing to local repo' '
+ (cd local &&
+ echo content >>file &&
+ git commit -a -m three &&
+ git push) &&
+ compare_refs local HEAD server HEAD
+'
+
+test_expect_success 'fetch new branch' '
+ (cd server &&
+ git reset --hard &&
+ git checkout -b new &&
+ echo content >>file &&
+ git commit -a -m five
+ ) &&
+ (cd local &&
+ git fetch origin new
+ ) &&
+ compare_refs server HEAD local FETCH_HEAD
+'
+
+test_expect_success 'fetch multiple branches' '
+ (cd local &&
+ git fetch
+ ) &&
+ compare_refs server master local refs/remotes/origin/master &&
+ compare_refs server new local refs/remotes/origin/new
+'
+
+test_expect_success 'push when remote has extra refs' '
+ (cd local &&
+ git reset --hard origin/master &&
+ echo content >>file &&
+ git commit -a -m six &&
+ git push
+ ) &&
+ compare_refs local master server master
+'
+
+test_expect_success 'push new branch by name' '
+ (cd local &&
+ git checkout -b new-name &&
+ echo content >>file &&
+ git commit -a -m seven &&
+ git push origin new-name
+ ) &&
+ compare_refs local HEAD server refs/heads/new-name
+'
+
+test_expect_success 'push new branch with old:new refspec' '
+ (cd local &&
+ git push origin new-name:new-refspec
+ ) &&
+ compare_refs local HEAD server refs/heads/new-refspec
+'
+
+test_expect_success 'push new branch with HEAD:new refspec' '
+ (cd local &&
+ git checkout new-name
+ git push origin HEAD:new-refspec-2
+ ) &&
+ compare_refs local HEAD server refs/heads/new-refspec-2
+'
+
+test_expect_success 'push delete branch' '
+ (cd local &&
+ git push origin :new-name
+ ) &&
+ test_must_fail git --git-dir="server/.git" \
+ rev-parse --verify refs/heads/new-name
+'
+
+test_expect_success 'forced push' '
+ (cd local &&
+ git checkout -b force-test &&
+ echo content >> file &&
+ git commit -a -m eight &&
+ git push origin force-test &&
+ echo content >> file &&
+ git commit -a --amend -m eight-modified &&
+ git push --force origin force-test
+ ) &&
+ compare_refs local refs/heads/force-test server refs/heads/force-test
+'
+
+test_expect_success 'cloning without refspec' '
+ GIT_REMOTE_TESTGIT_REFSPEC="" \
+ git clone "testgit::${PWD}/server" local2 2>error &&
+ grep "This remote helper should implement refspec capability" error &&
+ compare_refs local2 HEAD server HEAD
+'
+
+test_expect_success 'pulling without refspecs' '
+ (cd local2 &&
+ git reset --hard &&
+ GIT_REMOTE_TESTGIT_REFSPEC="" git pull 2>../error) &&
+ grep "This remote helper should implement refspec capability" error &&
+ compare_refs local2 HEAD server HEAD
+'
+
+test_expect_success 'pushing without refspecs' '
+ test_when_finished "(cd local2 && git reset --hard origin)" &&
+ (cd local2 &&
+ echo content >>file &&
+ git commit -a -m ten &&
+ GIT_REMOTE_TESTGIT_REFSPEC="" &&
+ export GIT_REMOTE_TESTGIT_REFSPEC &&
+ test_must_fail git push 2>../error) &&
+ grep "remote-helper doesn.t support push; refspec needed" error
+'
+
+test_expect_success 'pulling without marks' '
+ (cd local2 &&
+ GIT_REMOTE_TESTGIT_NO_MARKS=1 git pull) &&
+ compare_refs local2 HEAD server HEAD
+'
+
+test_expect_failure 'pushing without marks' '
+ test_when_finished "(cd local2 && git reset --hard origin)" &&
+ (cd local2 &&
+ echo content >>file &&
+ git commit -a -m twelve &&
+ GIT_REMOTE_TESTGIT_NO_MARKS=1 git push) &&
+ compare_refs local2 HEAD server HEAD
+'
+
+test_expect_success 'push all with existing object' '
+ (cd local &&
+ git branch dup2 master &&
+ git push origin --all
+ ) &&
+ compare_refs local dup2 server dup2
+'
+
+test_expect_success 'push ref with existing object' '
+ (cd local &&
+ git branch dup master &&
+ git push origin dup
+ ) &&
+ compare_refs local dup server dup
+'
+
+test_expect_success GPG 'push signed tag' '
+ (cd local &&
+ git checkout master &&
+ git tag -s -m signed-tag signed-tag &&
+ git push origin signed-tag
+ ) &&
+ compare_refs local signed-tag^{} server signed-tag^{} &&
+ test_must_fail compare_refs local signed-tag server signed-tag
+'
+
+test_expect_success GPG 'push signed tag with signed-tags capability' '
+ (cd local &&
+ git checkout master &&
+ git tag -s -m signed-tag signed-tag-2 &&
+ GIT_REMOTE_TESTGIT_SIGNED_TAGS=1 git push origin signed-tag-2
+ ) &&
+ compare_refs local signed-tag-2 server signed-tag-2
+'
+
+test_expect_success 'push update refs' '
+ (cd local &&
+ git checkout -b update master &&
+ echo update >>file &&
+ git commit -a -m update &&
+ git push origin update &&
+ git rev-parse --verify remotes/origin/update >expect &&
+ git rev-parse --verify testgit/origin/heads/update >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'push update refs disabled by no-private-update' '
+ (cd local &&
+ echo more-update >>file &&
+ git commit -a -m more-update &&
+ git rev-parse --verify testgit/origin/heads/update >expect &&
+ GIT_REMOTE_TESTGIT_NO_PRIVATE_UPDATE=t git push origin update &&
+ git rev-parse --verify testgit/origin/heads/update >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'push update refs failure' '
+ (cd local &&
+ git checkout update &&
+ echo "update fail" >>file &&
+ git commit -a -m "update fail" &&
+ git rev-parse --verify testgit/origin/heads/update >expect &&
+ test_expect_code 1 env GIT_REMOTE_TESTGIT_FAILURE="non-fast forward" \
+ git push origin update &&
+ git rev-parse --verify testgit/origin/heads/update >actual &&
+ test_cmp expect actual
+ )
+'
+
+clean_mark () {
+ cut -f 2 -d ' ' "$1" |
+ git cat-file --batch-check |
+ grep commit |
+ sort >$(basename "$1")
+}
+
+test_expect_success 'proper failure checks for fetching' '
+ (cd local &&
+ test_must_fail env GIT_REMOTE_TESTGIT_FAILURE=1 git fetch 2>error &&
+ cat error &&
+ grep -q "Error while running fast-import" error
+ )
+'
+
+test_expect_success 'proper failure checks for pushing' '
+ test_when_finished "rm -rf local/git.marks local/testgit.marks" &&
+ (cd local &&
+ git checkout -b crash master &&
+ echo crash >>file &&
+ git commit -a -m crash &&
+ test_must_fail env GIT_REMOTE_TESTGIT_FAILURE=1 git push --all &&
+ clean_mark ".git/testgit/origin/git.marks" &&
+ clean_mark ".git/testgit/origin/testgit.marks" &&
+ test_cmp git.marks testgit.marks
+ )
+'
+
+test_expect_success 'push messages' '
+ (cd local &&
+ git checkout -b new_branch master &&
+ echo new >>file &&
+ git commit -a -m new &&
+ git push origin new_branch &&
+ git fetch origin &&
+ echo new >>file &&
+ git commit -a -m new &&
+ git push origin new_branch 2> msg &&
+ ! grep "\[new branch\]" msg
+ )
+'
+
+test_expect_success 'fetch HEAD' '
+ (cd server &&
+ git checkout master &&
+ echo more >>file &&
+ git commit -a -m more
+ ) &&
+ (cd local &&
+ git fetch origin HEAD
+ ) &&
+ compare_refs server HEAD local FETCH_HEAD
+'
+
+test_expect_success 'fetch url' '
+ (cd server &&
+ git checkout master &&
+ echo more >>file &&
+ git commit -a -m more
+ ) &&
+ (cd local &&
+ git fetch "testgit::${PWD}/../server"
+ ) &&
+ compare_refs server HEAD local FETCH_HEAD
+'
+
+test_done
diff --git a/t/t5802-connect-helper.sh b/t/t5802-connect-helper.sh
new file mode 100755
index 0000000..b7a7f9d
--- /dev/null
+++ b/t/t5802-connect-helper.sh
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+test_description='ext::cmd remote "connect" helper'
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_tick &&
+ git commit --allow-empty -m initial &&
+ test_tick &&
+ git commit --allow-empty -m second &&
+ test_tick &&
+ git commit --allow-empty -m third &&
+ test_tick &&
+ git tag -a -m "tip three" three &&
+
+ test_tick &&
+ git commit --allow-empty -m fourth
+'
+
+test_expect_success clone '
+ cmd=$(echo "echo >&2 ext::sh invoked && %S .." | sed -e "s/ /% /g") &&
+ git clone "ext::sh -c %S% ." dst &&
+ git for-each-ref refs/heads/ refs/tags/ >expect &&
+ (
+ cd dst &&
+ git config remote.origin.url "ext::sh -c $cmd" &&
+ git for-each-ref refs/heads/ refs/tags/
+ ) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'update following tag' '
+ test_tick &&
+ git commit --allow-empty -m fifth &&
+ test_tick &&
+ git tag -a -m "tip five" five &&
+ git for-each-ref refs/heads/ refs/tags/ >expect &&
+ (
+ cd dst &&
+ git pull &&
+ git for-each-ref refs/heads/ refs/tags/ >../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'update backfilled tag' '
+ test_tick &&
+ git commit --allow-empty -m sixth &&
+ test_tick &&
+ git tag -a -m "tip two" two three^1 &&
+ git for-each-ref refs/heads/ refs/tags/ >expect &&
+ (
+ cd dst &&
+ git pull &&
+ git for-each-ref refs/heads/ refs/tags/ >../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'update backfilled tag without primary transfer' '
+ test_tick &&
+ git tag -a -m "tip one " one two^1 &&
+ git for-each-ref refs/heads/ refs/tags/ >expect &&
+ (
+ cd dst &&
+ git pull &&
+ git for-each-ref refs/heads/ refs/tags/ >../actual
+ ) &&
+ test_cmp expect actual
+'
+
+
+test_expect_success 'set up fake git-daemon' '
+ mkdir remote &&
+ git init --bare remote/one.git &&
+ mkdir remote/host &&
+ git init --bare remote/host/two.git &&
+ write_script fake-daemon <<-\EOF &&
+ git daemon --inetd \
+ --informative-errors \
+ --export-all \
+ --base-path="$TRASH_DIRECTORY/remote" \
+ --interpolated-path="$TRASH_DIRECTORY/remote/%H%D" \
+ "$TRASH_DIRECTORY/remote"
+ EOF
+ export TRASH_DIRECTORY &&
+ PATH=$TRASH_DIRECTORY:$PATH
+'
+
+test_expect_success 'ext command can connect to git daemon (no vhost)' '
+ rm -rf dst &&
+ git clone "ext::fake-daemon %G/one.git" dst
+'
+
+test_expect_success 'ext command can connect to git daemon (vhost)' '
+ rm -rf dst &&
+ git clone "ext::fake-daemon %G/two.git %Vhost" dst
+'
+
+test_done
diff --git a/t/t5810-proto-disable-local.sh b/t/t5810-proto-disable-local.sh
new file mode 100755
index 0000000..563592d
--- /dev/null
+++ b/t/t5810-proto-disable-local.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+test_description='test disabling of local paths in clone/fetch'
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-proto-disable.sh"
+
+test_expect_success 'setup repository to clone' '
+ test_commit one
+'
+
+test_proto "file://" file "file://$PWD"
+test_proto "path" file .
+
+test_done
diff --git a/t/t5811-proto-disable-git.sh b/t/t5811-proto-disable-git.sh
new file mode 100755
index 0000000..8ac6b2a
--- /dev/null
+++ b/t/t5811-proto-disable-git.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+test_description='test disabling of git-over-tcp in clone/fetch'
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-proto-disable.sh"
+. "$TEST_DIRECTORY/lib-git-daemon.sh"
+start_git_daemon
+
+test_expect_success 'create git-accessible repo' '
+ bare="$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" &&
+ test_commit one &&
+ git --bare init "$bare" &&
+ git push "$bare" HEAD &&
+ >"$bare/git-daemon-export-ok" &&
+ git -C "$bare" config daemon.receivepack true
+'
+
+test_proto "git://" git "$GIT_DAEMON_URL/repo.git"
+
+test_done
diff --git a/t/t5812-proto-disable-http.sh b/t/t5812-proto-disable-http.sh
new file mode 100755
index 0000000..0d105d5
--- /dev/null
+++ b/t/t5812-proto-disable-http.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+test_description='test disabling of git-over-http in clone/fetch'
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-proto-disable.sh"
+. "$TEST_DIRECTORY/lib-httpd.sh"
+start_httpd
+
+test_expect_success 'create git-accessible repo' '
+ bare="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ test_commit one &&
+ git --bare init "$bare" &&
+ git push "$bare" HEAD &&
+ git -C "$bare" config http.receivepack true
+'
+
+test_proto "smart http" http "$HTTPD_URL/smart/repo.git"
+
+test_expect_success 'curl redirects respect whitelist' '
+ test_must_fail env GIT_ALLOW_PROTOCOL=http:https \
+ git clone "$HTTPD_URL/ftp-redir/repo.git" 2>stderr &&
+ {
+ test_i18ngrep "ftp.*disabled" stderr ||
+ test_i18ngrep "your curl version is too old"
+ }
+'
+
+test_expect_success 'curl limits redirects' '
+ test_must_fail git clone "$HTTPD_URL/loop-redir/smart/repo.git"
+'
+
+stop_httpd
+test_done
diff --git a/t/t5813-proto-disable-ssh.sh b/t/t5813-proto-disable-ssh.sh
new file mode 100755
index 0000000..a954ead
--- /dev/null
+++ b/t/t5813-proto-disable-ssh.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+test_description='test disabling of git-over-ssh in clone/fetch'
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-proto-disable.sh"
+
+setup_ssh_wrapper
+
+test_expect_success 'setup repository to clone' '
+ test_commit one &&
+ mkdir remote &&
+ git init --bare remote/repo.git &&
+ git push remote/repo.git HEAD
+'
+
+test_proto "host:path" ssh "remote:repo.git"
+test_proto "ssh://" ssh "ssh://remote$PWD/remote/repo.git"
+test_proto "git+ssh://" ssh "git+ssh://remote$PWD/remote/repo.git"
+
+test_done
diff --git a/t/t5814-proto-disable-ext.sh b/t/t5814-proto-disable-ext.sh
new file mode 100755
index 0000000..9d6f7df
--- /dev/null
+++ b/t/t5814-proto-disable-ext.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+test_description='test disabling of remote-helper paths in clone/fetch'
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-proto-disable.sh"
+
+setup_ext_wrapper
+
+test_expect_success 'setup repository to clone' '
+ test_commit one &&
+ mkdir remote &&
+ git init --bare remote/repo.git &&
+ git push remote/repo.git HEAD
+'
+
+test_proto "remote-helper" ext "ext::fake-remote %S repo.git"
+
+test_done
diff --git a/t/t5815-submodule-protos.sh b/t/t5815-submodule-protos.sh
new file mode 100755
index 0000000..06f55a1
--- /dev/null
+++ b/t/t5815-submodule-protos.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+test_description='test protocol whitelisting with submodules'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-proto-disable.sh
+
+setup_ext_wrapper
+setup_ssh_wrapper
+
+test_expect_success 'setup repository with submodules' '
+ mkdir remote &&
+ git init remote/repo.git &&
+ (cd remote/repo.git && test_commit one) &&
+ # submodule-add should probably trust what we feed it on the cmdline,
+ # but its implementation is overly conservative.
+ GIT_ALLOW_PROTOCOL=ssh git submodule add remote:repo.git ssh-module &&
+ GIT_ALLOW_PROTOCOL=ext git submodule add "ext::fake-remote %S repo.git" ext-module &&
+ git commit -m "add submodules"
+'
+
+test_expect_success 'clone with recurse-submodules fails' '
+ test_must_fail git clone --recurse-submodules . dst
+'
+
+test_expect_success 'setup individual updates' '
+ rm -rf dst &&
+ git clone . dst &&
+ git -C dst submodule init
+'
+
+test_expect_success 'update of ssh allowed' '
+ git -C dst submodule update ssh-module
+'
+
+test_expect_success 'update of ext not allowed' '
+ test_must_fail git -C dst submodule update ext-module
+'
+
+test_expect_success 'user can override whitelist' '
+ GIT_ALLOW_PROTOCOL=ext git -C dst submodule update ext-module
+'
+
+test_done
diff --git a/t/t5900-repo-selection.sh b/t/t5900-repo-selection.sh
new file mode 100755
index 0000000..14e59c5
--- /dev/null
+++ b/t/t5900-repo-selection.sh
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+test_description='selecting remote repo in ambiguous cases'
+. ./test-lib.sh
+
+reset() {
+ rm -rf foo foo.git fetch clone
+}
+
+make_tree() {
+ git init "$1" &&
+ (cd "$1" && test_commit "$1")
+}
+
+make_bare() {
+ git init --bare "$1" &&
+ (cd "$1" &&
+ tree=$(git hash-object -w -t tree /dev/null) &&
+ commit=$(echo "$1" | git commit-tree $tree) &&
+ git update-ref HEAD $commit
+ )
+}
+
+get() {
+ git init --bare fetch &&
+ (cd fetch && git fetch "../$1") &&
+ git clone "$1" clone
+}
+
+check() {
+ echo "$1" >expect &&
+ (cd fetch && git log -1 --format=%s FETCH_HEAD) >actual.fetch &&
+ (cd clone && git log -1 --format=%s HEAD) >actual.clone &&
+ test_cmp expect actual.fetch &&
+ test_cmp expect actual.clone
+}
+
+test_expect_success 'find .git dir in worktree' '
+ reset &&
+ make_tree foo &&
+ get foo &&
+ check foo
+'
+
+test_expect_success 'automagically add .git suffix' '
+ reset &&
+ make_bare foo.git &&
+ get foo &&
+ check foo.git
+'
+
+test_expect_success 'automagically add .git suffix to worktree' '
+ reset &&
+ make_tree foo.git &&
+ get foo &&
+ check foo.git
+'
+
+test_expect_success 'prefer worktree foo over bare foo.git' '
+ reset &&
+ make_tree foo &&
+ make_bare foo.git &&
+ get foo &&
+ check foo
+'
+
+test_expect_success 'prefer bare foo over bare foo.git' '
+ reset &&
+ make_bare foo &&
+ make_bare foo.git &&
+ get foo &&
+ check foo
+'
+
+test_expect_success 'disambiguate with full foo.git' '
+ reset &&
+ make_bare foo &&
+ make_bare foo.git &&
+ get foo.git &&
+ check foo.git
+'
+
+test_expect_success 'we are not fooled by non-git foo directory' '
+ reset &&
+ make_bare foo.git &&
+ mkdir foo &&
+ get foo &&
+ check foo.git
+'
+
+test_expect_success 'prefer inner .git over outer bare' '
+ reset &&
+ make_tree foo &&
+ make_bare foo.git &&
+ mv foo/.git foo.git &&
+ get foo.git &&
+ check foo
+'
+
+test_done
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
new file mode 100755
index 0000000..3e752ce
--- /dev/null
+++ b/t/t6000-rev-list-misc.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+test_description='miscellaneous rev-list tests'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo content1 >wanted_file &&
+ echo content2 >unwanted_file &&
+ git add wanted_file unwanted_file &&
+ git commit -m one
+'
+
+test_expect_success 'rev-list --objects heeds pathspecs' '
+ git rev-list --objects HEAD -- wanted_file >output &&
+ grep wanted_file output &&
+ ! grep unwanted_file output
+'
+
+test_expect_success 'rev-list --objects with pathspecs and deeper paths' '
+ mkdir foo &&
+ >foo/file &&
+ git add foo/file &&
+ git commit -m two &&
+
+ git rev-list --objects HEAD -- foo >output &&
+ grep foo/file output &&
+
+ git rev-list --objects HEAD -- foo/file >output &&
+ grep foo/file output &&
+ ! grep unwanted_file output
+'
+
+test_expect_success 'rev-list --objects with pathspecs and copied files' '
+ git checkout --orphan junio-testcase &&
+ git rm -rf . &&
+
+ mkdir two &&
+ echo frotz >one &&
+ cp one two/three &&
+ git add one two/three &&
+ test_tick &&
+ git commit -m that &&
+
+ ONE=$(git rev-parse HEAD:one) &&
+ git rev-list --objects HEAD two >output &&
+ grep "$ONE two/three" output &&
+ ! grep one output
+'
+
+test_expect_success 'rev-list A..B and rev-list ^A B are the same' '
+ git commit --allow-empty -m another &&
+ git tag -a -m "annotated" v1.0 &&
+ git rev-list --objects ^v1.0^ v1.0 >expect &&
+ git rev-list --objects v1.0^..v1.0 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'propagate uninteresting flag down correctly' '
+ git rev-list --objects ^HEAD^{tree} HEAD^{tree} >actual &&
+ >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'symleft flag bit is propagated down from tag' '
+ git log --format="%m %s" --left-right v1.0...master >actual &&
+ cat >expect <<-\EOF &&
+ > two
+ > one
+ < another
+ < that
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'rev-list can show index objects' '
+ # Of the blobs and trees in the index, note:
+ #
+ # - we do not show two/three, because it is the
+ # same blob as "one", and we show objects only once
+ #
+ # - we do show the tree "two", because it has a valid cache tree
+ # from the last commit
+ #
+ # - we do not show the root tree; since we updated the index, it
+ # does not have a valid cache tree
+ #
+ cat >expect <<-\EOF &&
+ 8e4020bb5a8d8c873b25de15933e75cc0fc275df one
+ d9d3a7417b9605cfd88ee6306b28dadc29e6ab08 only-in-index
+ 9200b628cf9dc883a85a7abc8d6e6730baee589c two
+ EOF
+ echo only-in-index >only-in-index &&
+ git add only-in-index &&
+ git rev-list --objects --indexed-objects >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--bisect and --first-parent can not be combined' '
+ test_must_fail git rev-list --bisect --first-parent HEAD
+'
+
+test_done
diff --git a/t/t6001-rev-list-graft.sh b/t/t6001-rev-list-graft.sh
index fc57e7d..05ddc69 100755
--- a/t/t6001-rev-list-graft.sh
+++ b/t/t6001-rev-list-graft.sh
@@ -10,15 +10,15 @@ test_expect_success setup '
echo >subdir/fileB fileB &&
git add fileA subdir/fileB &&
git commit -a -m "Initial in one history." &&
- A0=`git rev-parse --verify HEAD` &&
+ A0=$(git rev-parse --verify HEAD) &&
echo >fileA fileA modified &&
git commit -a -m "Second in one history." &&
- A1=`git rev-parse --verify HEAD` &&
+ 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` &&
+ A2=$(git rev-parse --verify HEAD) &&
rm -f .git/refs/heads/master .git/index &&
@@ -26,15 +26,15 @@ test_expect_success setup '
echo >subdir/fileB fileB again &&
git add fileA subdir/fileB &&
git commit -a -m "Initial in alternate history." &&
- B0=`git rev-parse --verify HEAD` &&
+ 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` &&
+ 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` &&
+ B2=$(git rev-parse --verify HEAD) &&
: done
'
@@ -90,22 +90,22 @@ check () {
for type in basic parents parents-raw
do
test_expect_success 'without grafts' "
- rm -f .git/info/grafts
+ rm -f .git/info/grafts &&
check $type $B2 -- $B2 $B1 $B0
"
test_expect_success 'with grafts' "
- echo '$B0 $A2' >.git/info/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
+ rm -f .git/info/grafts &&
check $type $B2 subdir -- $B2 $B0
"
test_expect_success 'with grafts, with pathlimit' "
- echo '$B0 $A2' >.git/info/grafts
+ echo '$B0 $A2' >.git/info/grafts &&
check $type $B2 subdir -- $B2 $B0 $A2 $A0
"
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index fb07536..3bf2759 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -27,9 +27,9 @@ test_bisection_diff()
# 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
+ _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" \
@@ -39,25 +39,25 @@ test_bisection_diff()
date >path0
git update-index --add path0
save_tag tree git write-tree
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
-on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
-on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
-on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1
-on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2
-on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0
-on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0
-on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1
-on_committer_date "1971-08-16 00:00:08" save_tag b2 unique_commit b2 tree -p b1
-on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b2 tree -p b2
-on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
-on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2
-on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1
-on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2
-on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
-on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
-on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
-on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
-on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
+on_committer_date "00:00" hide_error save_tag root unique_commit root tree
+on_committer_date "00:01" save_tag l0 unique_commit l0 tree -p root
+on_committer_date "00:02" save_tag l1 unique_commit l1 tree -p l0
+on_committer_date "00:03" save_tag l2 unique_commit l2 tree -p l1
+on_committer_date "00:04" save_tag a0 unique_commit a0 tree -p l2
+on_committer_date "00:05" save_tag a1 unique_commit a1 tree -p a0
+on_committer_date "00:06" save_tag b1 unique_commit b1 tree -p a0
+on_committer_date "00:07" save_tag c1 unique_commit c1 tree -p b1
+on_committer_date "00:08" save_tag b2 unique_commit b2 tree -p b1
+on_committer_date "00:09" save_tag b3 unique_commit b2 tree -p b2
+on_committer_date "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
+on_committer_date "00:11" save_tag c3 unique_commit c3 tree -p c2
+on_committer_date "00:12" save_tag a2 unique_commit a2 tree -p a1
+on_committer_date "00:13" save_tag a3 unique_commit a3 tree -p a2
+on_committer_date "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
+on_committer_date "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
+on_committer_date "00:16" save_tag l3 unique_commit l3 tree -p a4
+on_committer_date "00:17" save_tag l4 unique_commit l4 tree -p l3
+on_committer_date "00:18" save_tag l5 unique_commit l5 tree -p l4
git update-ref HEAD $(tag l5)
@@ -90,29 +90,29 @@ git update-ref HEAD $(tag l5)
# F
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag F unique_commit F tree
-on_committer_date "1971-08-16 00:00:01" save_tag e8 unique_commit e8 tree -p F
-on_committer_date "1971-08-16 00:00:02" save_tag e7 unique_commit e7 tree -p e8
-on_committer_date "1971-08-16 00:00:03" save_tag e6 unique_commit e6 tree -p e7
-on_committer_date "1971-08-16 00:00:04" save_tag e5 unique_commit e5 tree -p e6
-on_committer_date "1971-08-16 00:00:05" save_tag f4 unique_commit f4 tree -p F
-on_committer_date "1971-08-16 00:00:06" save_tag f3 unique_commit f3 tree -p f4
-on_committer_date "1971-08-16 00:00:07" save_tag f2 unique_commit f2 tree -p f3
-on_committer_date "1971-08-16 00:00:08" save_tag f1 unique_commit f1 tree -p f2
-on_committer_date "1971-08-16 00:00:09" save_tag e4 unique_commit e4 tree -p e5
-on_committer_date "1971-08-16 00:00:10" save_tag e3 unique_commit e3 tree -p e4
-on_committer_date "1971-08-16 00:00:11" save_tag e2 unique_commit e2 tree -p e3
-on_committer_date "1971-08-16 00:00:12" save_tag e1 unique_commit e1 tree -p e2
-on_committer_date "1971-08-16 00:00:13" save_tag E unique_commit E tree -p e1 -p f1
-
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag U unique_commit U tree
-on_committer_date "1971-08-16 00:00:01" save_tag u0 unique_commit u0 tree -p U
-on_committer_date "1971-08-16 00:00:01" save_tag u1 unique_commit u1 tree -p u0
-on_committer_date "1971-08-16 00:00:02" save_tag u2 unique_commit u2 tree -p u0
-on_committer_date "1971-08-16 00:00:03" save_tag u3 unique_commit u3 tree -p u0
-on_committer_date "1971-08-16 00:00:04" save_tag u4 unique_commit u4 tree -p u0
-on_committer_date "1971-08-16 00:00:05" save_tag u5 unique_commit u5 tree -p u0
-on_committer_date "1971-08-16 00:00:06" save_tag V unique_commit V tree -p u1 -p u2 -p u3 -p u4 -p u5
+on_committer_date "00:00" hide_error save_tag F unique_commit F tree
+on_committer_date "00:01" save_tag e8 unique_commit e8 tree -p F
+on_committer_date "00:02" save_tag e7 unique_commit e7 tree -p e8
+on_committer_date "00:03" save_tag e6 unique_commit e6 tree -p e7
+on_committer_date "00:04" save_tag e5 unique_commit e5 tree -p e6
+on_committer_date "00:05" save_tag f4 unique_commit f4 tree -p F
+on_committer_date "00:06" save_tag f3 unique_commit f3 tree -p f4
+on_committer_date "00:07" save_tag f2 unique_commit f2 tree -p f3
+on_committer_date "00:08" save_tag f1 unique_commit f1 tree -p f2
+on_committer_date "00:09" save_tag e4 unique_commit e4 tree -p e5
+on_committer_date "00:10" save_tag e3 unique_commit e3 tree -p e4
+on_committer_date "00:11" save_tag e2 unique_commit e2 tree -p e3
+on_committer_date "00:12" save_tag e1 unique_commit e1 tree -p e2
+on_committer_date "00:13" save_tag E unique_commit E tree -p e1 -p f1
+
+on_committer_date "00:00" hide_error save_tag U unique_commit U tree
+on_committer_date "00:01" save_tag u0 unique_commit u0 tree -p U
+on_committer_date "00:01" save_tag u1 unique_commit u1 tree -p u0
+on_committer_date "00:02" save_tag u2 unique_commit u2 tree -p u0
+on_committer_date "00:03" save_tag u3 unique_commit u3 tree -p u0
+on_committer_date "00:04" save_tag u4 unique_commit u4 tree -p u0
+on_committer_date "00:05" save_tag u5 unique_commit u5 tree -p u0
+on_committer_date "00:06" save_tag V unique_commit V tree -p u1 -p u2 -p u3 -p u4 -p u5
test_sequence()
{
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index e4c52b0..24d1836 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -16,39 +16,34 @@ list_duplicates()
date >path0
git update-index --add path0
save_tag tree git write-tree
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
-on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
-on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
-on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1
-on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2
-on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0
-on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0
-on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1
-on_committer_date "1971-08-16 00:00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1
-on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b3 tree -p b2
-on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
-on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2
-on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1
-on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2
-on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
-on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
-on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
-on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
-on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
-on_committer_date "1971-08-16 00:00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3
-on_committer_date "1971-08-16 00:00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4
-on_committer_date "1971-08-16 00:00:21" hide_error save_tag alt_root unique_commit alt_root tree
-on_committer_date "1971-08-16 00:00:22" save_tag r0 unique_commit r0 tree -p alt_root
-on_committer_date "1971-08-16 00:00:23" save_tag r1 unique_commit r1 tree -p r0
-on_committer_date "1971-08-16 00:00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1
-on_committer_date "1971-08-16 00:00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5
+on_dates "00:00" "00:00" hide_error save_tag root unique_commit root tree
+on_dates "00:01" "00:01" save_tag l0 unique_commit l0 tree -p root
+on_dates "00:02" "00:02" save_tag l1 unique_commit l1 tree -p l0
+on_dates "00:03" "00:03" save_tag l2 unique_commit l2 tree -p l1
+on_dates "00:04" "00:04" save_tag a0 unique_commit a0 tree -p l2
+on_dates "00:05" "00:05" save_tag a1 unique_commit a1 tree -p a0
+on_dates "00:06" "00:06" save_tag b1 unique_commit b1 tree -p a0
+on_dates "00:07" "00:07" save_tag c1 unique_commit c1 tree -p b1
+on_dates "00:08" "00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1
+on_dates "00:09" "00:09" save_tag b3 unique_commit b3 tree -p b2
+on_dates "00:10" "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
+on_dates "00:11" "00:11" save_tag c3 unique_commit c3 tree -p c2
+on_dates "00:12" "00:00" save_tag a2 unique_commit a2 tree -p a1
+on_dates "00:13" "00:01" save_tag a3 unique_commit a3 tree -p a2
+on_dates "00:14" "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
+on_dates "00:15" "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
+on_dates "00:16" "00:16" save_tag l3 unique_commit l3 tree -p a4
+on_dates "00:17" "00:17" save_tag l4 unique_commit l4 tree -p l3
+on_dates "00:18" "00:18" save_tag l5 unique_commit l5 tree -p l4
+on_dates "00:19" "00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3
+on_dates "00:20" "00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4
+on_dates "00:21" "00:21" hide_error save_tag alt_root unique_commit alt_root tree
+on_dates "00:22" "00:22" save_tag r0 unique_commit r0 tree -p alt_root
+on_dates "00:23" "00:23" save_tag r1 unique_commit r1 tree -p r0
+on_dates "00:24" "00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1
+on_dates "00:25" "00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5
-#
-# note: as of 20/6, it isn't possible to create duplicate parents, so this
-# can't be tested.
-#
-#on_committer_date "1971-08-16 00:00:20" save_tag m3 unique_commit m3 tree -p c3 -p a4 -p c3
hide_error save_tag e1 as_author e@example.com unique_commit e1 tree
save_tag e2 as_author e@example.com unique_commit e2 tree -p e1
save_tag f1 as_author f@example.com unique_commit f1 tree -p e1
@@ -105,6 +100,50 @@ l0
root
EOF
+test_output_expect_success 'simple date order' 'git rev-list --date-order HEAD' <<EOF
+l5
+l4
+l3
+a4
+b4
+a3
+a2
+c3
+c2
+b3
+b2
+c1
+b1
+a1
+a0
+l2
+l1
+l0
+root
+EOF
+
+test_output_expect_success 'simple author-date order' 'git rev-list --author-date-order HEAD' <<EOF
+l5
+l4
+l3
+a4
+b4
+c3
+c2
+b3
+b2
+c1
+b1
+a3
+a2
+a1
+a0
+l2
+l1
+l0
+root
+EOF
+
test_output_expect_success 'two diamonds topo order (g6)' 'git rev-list --topo-order g4' <<EOF
g4
h2
diff --git a/t/t6004-rev-list-path-optim.sh b/t/t6004-rev-list-path-optim.sh
index 5dabf1c..3e8c42e 100755
--- a/t/t6004-rev-list-path-optim.sh
+++ b/t/t6004-rev-list-path-optim.sh
@@ -1,51 +1,96 @@
#!/bin/sh
-test_description='git rev-list trivial path optimization test'
+test_description='git rev-list trivial path optimization test
+
+ d/z1
+ b0 b1
+ o------------------------*----o master
+ / /
+ o---------o----o----o----o side
+ a0 c0 c1 a1 c2
+ d/f0 d/f1
+ d/z0
+
+'
. ./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)
+ echo Hello >a &&
+ mkdir d &&
+ echo World >d/f &&
+ echo World >d/z &&
+ git add a d &&
+ test_tick &&
+ git commit -m "Initial commit" &&
+ git rev-parse --verify HEAD &&
+ git tag initial
'
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_tick &&
+ 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 &&
+ echo Irrelevant >d/f &&
+ git add c d/f &&
+ test_tick &&
git commit -m "Side makes an irrelevant commit" &&
+ git tag side_c0 &&
echo "More Irrelevancy" >c &&
git add c &&
+ test_tick &&
git commit -m "Side makes another irrelevant commit" &&
echo Bye >a &&
git add a &&
+ test_tick &&
git commit -m "Side touches a" &&
- side=$(git rev-parse --verify HEAD) &&
+ git tag side_a1 &&
echo "Yet more Irrelevancy" >c &&
git add c &&
+ test_tick &&
git commit -m "Side makes yet another irrelevant commit" &&
git checkout master &&
echo Another >b &&
- git add b &&
+ echo Munged >d/z &&
+ git add b d/z &&
+ test_tick &&
git commit -m "Master touches b" &&
+ git tag master_b0 &&
git merge side &&
echo Touched >b &&
git add b &&
+ test_tick &&
git commit -m "Master touches b again"
'
test_expect_success 'path optimization 2' '
- ( echo "$side"; echo "$initial" ) >expected &&
+ git rev-parse side_a1 initial >expected &&
git rev-list HEAD -- a >actual &&
test_cmp expected actual
'
+test_expect_success 'pathspec with leading path' '
+ git rev-parse master^ master_b0 side_c0 initial >expected &&
+ git rev-list HEAD -- d >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'pathspec with glob (1)' '
+ git rev-parse master^ master_b0 side_c0 initial >expected &&
+ git rev-list HEAD -- "d/*" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'pathspec with glob (2)' '
+ git rev-parse side_c0 initial >expected &&
+ git rev-list HEAD -- "d/[a-m]*" >actual &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index cccacd4..a1dcdb8 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -1,67 +1,118 @@
#!/bin/sh
+# Copyright (c) 2009 Jens Lehmann
+# Copyright (c) 2011 Alexey Shumkin (+ non-UTF-8 commit encoding tests)
+
test_description='git rev-list --pretty=format test'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
test_tick
+# Tested non-UTF-8 encoding
+test_encoding="ISO8859-1"
+
+# String "added" in German
+# (translated with Google Translate),
+# encoded in UTF-8, used as a commit log message below.
+added_utf8_part=$(printf "\303\274")
+added_utf8_part_iso88591=$(echo "$added_utf8_part" | iconv -f utf-8 -t $test_encoding)
+added=$(printf "added (hinzugef${added_utf8_part}gt) foo")
+added_iso88591=$(echo "$added" | iconv -f utf-8 -t $test_encoding)
+# same but "changed"
+changed_utf8_part=$(printf "\303\244")
+changed_utf8_part_iso88591=$(echo "$changed_utf8_part" | iconv -f utf-8 -t $test_encoding)
+changed=$(printf "changed (ge${changed_utf8_part}ndert) foo")
+changed_iso88591=$(echo "$changed" | iconv -f utf-8 -t $test_encoding)
+
+# Count of char to truncate
+# Number is chosen so, that non-ACSII characters
+# (see $added_utf8_part and $changed_utf8_part)
+# fall into truncated parts of appropriate words both from left and right
+truncate_count=20
+
test_expect_success 'setup' '
-touch foo && git add foo && git commit -m "added foo" &&
- echo changed >foo && git commit -a -m "changed foo"
+ : >foo &&
+ git add foo &&
+ git config i18n.commitEncoding $test_encoding &&
+ echo "$added_iso88591" | git commit -F - &&
+ head1=$(git rev-parse --verify HEAD) &&
+ head1_short=$(git rev-parse --verify --short $head1) &&
+ tree1=$(git rev-parse --verify HEAD:) &&
+ tree1_short=$(git rev-parse --verify --short $tree1) &&
+ echo "$changed" > foo &&
+ echo "$changed_iso88591" | git commit -a -F - &&
+ head2=$(git rev-parse --verify HEAD) &&
+ head2_short=$(git rev-parse --verify --short $head2) &&
+ tree2=$(git rev-parse --verify HEAD:) &&
+ tree2_short=$(git rev-parse --verify --short $tree2) &&
+ git config --unset i18n.commitEncoding
'
-# usage: test_format name format_string <expected_output
-test_format() {
+# usage: test_format name format_string [failure] <expected_output
+test_format () {
cat >expect.$1
- test_expect_success "format $1" "
-git rev-list --pretty=format:'$2' master >output.$1 &&
-test_cmp expect.$1 output.$1
-"
+ test_expect_${3:-success} "format $1" "
+ git rev-list --pretty=format:'$2' master >output.$1 &&
+ test_cmp expect.$1 output.$1
+ "
+}
+
+# Feed to --format to provide predictable colored sequences.
+AUTO_COLOR='%C(auto,red)foo%C(auto,reset)'
+has_color () {
+ printf '\033[31mfoo\033[m\n' >expect &&
+ test_cmp expect "$1"
}
-test_format percent %%h <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+has_no_color () {
+ echo foo >expect &&
+ test_cmp expect "$1"
+}
+
+test_format percent %%h <<EOF
+commit $head2
%h
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
%h
EOF
-test_format hash %H%n%h <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-131a310eb913d107dd3c09a65d1651175898735d
-131a310
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-86c75cf
+test_format hash %H%n%h <<EOF
+commit $head2
+$head2
+$head2_short
+commit $head1
+$head1
+$head1_short
EOF
-test_format tree %T%n%t <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-fe722612f26da5064c32ca3843aa154bdb0b08a0
-fe72261
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-4d5fcadc293a348e88f777dc0920f11e7d71441c
-4d5fcad
+test_format tree %T%n%t <<EOF
+commit $head2
+$tree2
+$tree2_short
+commit $head1
+$tree1
+$tree1_short
EOF
-test_format parents %P%n%p <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-86c75cf
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+test_format parents %P%n%p <<EOF
+commit $head2
+$head1
+$head1_short
+commit $head1
EOF
# we don't test relative here
-test_format author %an%n%ae%n%ad%n%aD%n%at <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format author %an%n%ae%n%ad%n%aD%n%at <<EOF
+commit $head2
A U Thor
author@example.com
Thu Apr 7 15:13:13 2005 -0700
Thu, 7 Apr 2005 15:13:13 -0700
1112911993
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
A U Thor
author@example.com
Thu Apr 7 15:13:13 2005 -0700
@@ -69,14 +120,14 @@ Thu, 7 Apr 2005 15:13:13 -0700
1112911993
EOF
-test_format committer %cn%n%ce%n%cd%n%cD%n%ct <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format committer %cn%n%ce%n%cd%n%cD%n%ct <<EOF
+commit $head2
C O Mitter
committer@example.com
Thu Apr 7 15:13:13 2005 -0700
Thu, 7 Apr 2005 15:13:13 -0700
1112911993
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
C O Mitter
committer@example.com
Thu Apr 7 15:13:13 2005 -0700
@@ -84,84 +135,231 @@ Thu, 7 Apr 2005 15:13:13 -0700
1112911993
EOF
-test_format encoding %e <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+test_format encoding %e <<EOF
+commit $head2
+$test_encoding
+commit $head1
+$test_encoding
+EOF
+
+test_format subject %s <<EOF
+commit $head2
+$changed
+commit $head1
+$added
EOF
-test_format subject %s <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-changed foo
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-added foo
+test_format subject-truncated "%<($truncate_count,trunc)%s" <<EOF
+commit $head2
+changed (ge${changed_utf8_part}ndert)..
+commit $head1
+added (hinzugef${added_utf8_part}gt..
EOF
-test_format body %b <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+test_format body %b <<EOF
+commit $head2
+commit $head1
EOF
-test_format raw-body %B <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-changed foo
+test_format raw-body %B <<EOF
+commit $head2
+$changed
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-added foo
+commit $head1
+$added
EOF
-test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<EOF
+commit $head2
foobarbazxyzzy
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
foobarbazxyzzy
EOF
-test_format advanced-colors '%C(red yellow bold)foo%C(reset)' <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format advanced-colors '%C(red yellow bold)foo%C(reset)' <<EOF
+commit $head2
foo
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
foo
EOF
-cat >commit-msg <<'EOF'
+test_expect_success '%C(auto,...) does not enable color by default' '
+ git log --format=$AUTO_COLOR -1 >actual &&
+ has_no_color actual
+'
+
+test_expect_success '%C(auto,...) enables colors for color.diff' '
+ git -c color.diff=always log --format=$AUTO_COLOR -1 >actual &&
+ has_color actual
+'
+
+test_expect_success '%C(auto,...) enables colors for color.ui' '
+ git -c color.ui=always log --format=$AUTO_COLOR -1 >actual &&
+ has_color actual
+'
+
+test_expect_success '%C(auto,...) respects --color' '
+ git log --format=$AUTO_COLOR -1 --color >actual &&
+ has_color actual
+'
+
+test_expect_success '%C(auto,...) respects --no-color' '
+ git -c color.ui=always log --format=$AUTO_COLOR -1 --no-color >actual &&
+ has_no_color actual
+'
+
+test_expect_success TTY '%C(auto,...) respects --color=auto (stdout is tty)' '
+ test_terminal env TERM=vt100 \
+ git log --format=$AUTO_COLOR -1 --color=auto >actual &&
+ has_color actual
+'
+
+test_expect_success '%C(auto,...) respects --color=auto (stdout not tty)' '
+ (
+ TERM=vt100 && export TERM &&
+ git log --format=$AUTO_COLOR -1 --color=auto >actual &&
+ has_no_color actual
+ )
+'
+
+test_expect_success '%C(auto) respects --color' '
+ git log --color --format="%C(auto)%H" -1 >actual &&
+ printf "\\033[33m%s\\033[m\\n" $(git rev-parse HEAD) >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '%C(auto) respects --no-color' '
+ git log --no-color --format="%C(auto)%H" -1 >actual &&
+ git rev-parse HEAD >expect &&
+ test_cmp expect actual
+'
+
+iconv -f utf-8 -t $test_encoding > 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!
+and it will be encoded in $test_encoding. We should therefore
+include an ISO8859 character: ¡bueno!
EOF
+
test_expect_success 'setup complex body' '
-git config i18n.commitencoding iso8859-1 &&
- echo change2 >foo && git commit -a -F commit-msg
+ git config i18n.commitencoding $test_encoding &&
+ echo change2 >foo && git commit -a -F commit-msg &&
+ head3=$(git rev-parse --verify HEAD) &&
+ head3_short=$(git rev-parse --short $head3)
'
-test_format complex-encoding %e <<'EOF'
-commit f58db70b055c5718631e5c61528b28b12090cdea
-iso8859-1
-commit 131a310eb913d107dd3c09a65d1651175898735d
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+test_format complex-encoding %e <<EOF
+commit $head3
+$test_encoding
+commit $head2
+$test_encoding
+commit $head1
+$test_encoding
EOF
-test_format complex-subject %s <<'EOF'
-commit f58db70b055c5718631e5c61528b28b12090cdea
+test_format complex-subject %s <<EOF
+commit $head3
Test printing of complex bodies
-commit 131a310eb913d107dd3c09a65d1651175898735d
-changed foo
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-added foo
+commit $head2
+$changed_iso88591
+commit $head1
+$added_iso88591
EOF
-test_format complex-body %b <<'EOF'
-commit f58db70b055c5718631e5c61528b28b12090cdea
-This commit message is much longer than the others,
-and it will be encoded in iso8859-1. We should therefore
-include an iso8859 character: ¡bueno!
+test_format complex-subject-trunc "%<($truncate_count,trunc)%s" <<EOF
+commit $head3
+Test printing of c..
+commit $head2
+changed (ge${changed_utf8_part_iso88591}ndert)..
+commit $head1
+added (hinzugef${added_utf8_part_iso88591}gt..
+EOF
+
+test_format complex-subject-mtrunc "%<($truncate_count,mtrunc)%s" <<EOF
+commit $head3
+Test prin..ex bodies
+commit $head2
+changed (..dert) foo
+commit $head1
+added (hi..f${added_utf8_part_iso88591}gt) foo
+EOF
-commit 131a310eb913d107dd3c09a65d1651175898735d
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+test_format complex-subject-ltrunc "%<($truncate_count,ltrunc)%s" <<EOF
+commit $head3
+.. of complex bodies
+commit $head2
+..ged (ge${changed_utf8_part_iso88591}ndert) foo
+commit $head1
+.. (hinzugef${added_utf8_part_iso88591}gt) foo
EOF
+test_expect_success 'prepare expected messages (for test %b)' '
+ cat <<-EOF >expected.utf-8 &&
+ commit $head3
+ This commit message is much longer than the others,
+ and it will be encoded in $test_encoding. We should therefore
+ include an ISO8859 character: ¡bueno!
+
+ commit $head2
+ commit $head1
+ EOF
+ iconv -f utf-8 -t $test_encoding expected.utf-8 >expected.ISO8859-1
+'
+
+test_format complex-body %b <expected.ISO8859-1
+
+# Git uses i18n.commitEncoding if no i18n.logOutputEncoding set
+# so unset i18n.commitEncoding to test encoding conversion
+git config --unset i18n.commitEncoding
+
+test_format complex-subject-commitencoding-unset %s <<EOF
+commit $head3
+Test printing of complex bodies
+commit $head2
+$changed
+commit $head1
+$added
+EOF
+
+test_format complex-subject-commitencoding-unset-trunc "%<($truncate_count,trunc)%s" <<EOF
+commit $head3
+Test printing of c..
+commit $head2
+changed (ge${changed_utf8_part}ndert)..
+commit $head1
+added (hinzugef${added_utf8_part}gt..
+EOF
+
+test_format complex-subject-commitencoding-unset-mtrunc "%<($truncate_count,mtrunc)%s" <<EOF
+commit $head3
+Test prin..ex bodies
+commit $head2
+changed (..dert) foo
+commit $head1
+added (hi..f${added_utf8_part}gt) foo
+EOF
+
+test_format complex-subject-commitencoding-unset-ltrunc "%<($truncate_count,ltrunc)%s" <<EOF
+commit $head3
+.. of complex bodies
+commit $head2
+..ged (ge${changed_utf8_part}ndert) foo
+commit $head1
+.. (hinzugef${added_utf8_part}gt) foo
+EOF
+
+test_format complex-body-commitencoding-unset %b <expected.utf-8
+
+test_expect_success '%x00 shows NUL' '
+ echo >expect commit $head3 &&
+ echo >>expect fooQbar &&
+ git rev-list -1 --format=foo%x00bar HEAD >actual.nul &&
+ nul_to_q <actual.nul >actual &&
+ test_cmp expect actual
+'
+
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 &&
@@ -172,42 +370,39 @@ 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
- }
+ verbose test "$A" = "A U Thor,,Thu Apr 7 15:14:13 2005 -0700"
'
test_expect_success 'del LF before empty (1)' '
git show -s --pretty=format:"%s%n%-b%nThanks%n" HEAD^^ >actual &&
- test $(wc -l <actual) = 2
+ test_line_count = 2 actual
'
test_expect_success 'del LF before empty (2)' '
git show -s --pretty=format:"%s%n%-b%nThanks%n" HEAD >actual &&
- test $(wc -l <actual) = 6 &&
+ test_line_count = 6 actual &&
grep "^$" actual
'
test_expect_success 'add LF before non-empty (1)' '
git show -s --pretty=format:"%s%+b%nThanks%n" HEAD^^ >actual &&
- test $(wc -l <actual) = 2
+ test_line_count = 2 actual
'
test_expect_success 'add LF before non-empty (2)' '
git show -s --pretty=format:"%s%+b%nThanks%n" HEAD >actual &&
- test $(wc -l <actual) = 6 &&
+ test_line_count = 6 actual &&
grep "^$" actual
'
test_expect_success 'add SP before non-empty (1)' '
git show -s --pretty=format:"%s% bThanks" HEAD^^ >actual &&
- test $(wc -w <actual) = 2
+ test $(wc -w <actual) = 3
'
test_expect_success 'add SP before non-empty (2)' '
git show -s --pretty=format:"%s% sThanks" HEAD^^ >actual &&
- test $(wc -w <actual) = 4
+ test $(wc -w <actual) = 6
'
test_expect_success '--abbrev' '
@@ -259,13 +454,33 @@ test_expect_success '%gd shortens ref name' '
test_cmp expect.gd-short actual.gd-short
'
+test_expect_success 'reflog identity' '
+ echo "C O Mitter:committer@example.com" >expect &&
+ git log -g -1 --format="%gn:%ge" >actual &&
+ test_cmp expect actual
+'
+
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_line_count = 5 test.txt &&
+ git rev-list --oneline --graph HEAD >testg.txt &&
+ test_line_count = 5 testg.txt
+'
+
+test_expect_success 'single-character name is parsed correctly' '
+ git commit --author="a <a@example.com>" --allow-empty -m foo &&
+ echo "a <a@example.com>" >expect &&
+ git log -1 --format="%an <%ae>" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'unused %G placeholders are passed through' '
+ echo "%GX %G" >expect &&
+ git log -1 --format="%GX %G" >actual &&
+ test_cmp expect actual
'
test_done
diff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh
index b565638..28d4f6b 100755
--- a/t/t6007-rev-list-cherry-pick-file.sh
+++ b/t/t6007-rev-list-cherry-pick-file.sh
@@ -4,13 +4,14 @@ test_description='test git rev-list --cherry-pick -- file'
. ./test-lib.sh
-# A---B
+# A---B---D---F
# \
# \
-# C
+# C---E
#
# 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.
+# D and C change bar in the same way, E and F differently.
test_expect_success setup '
echo Hallo > foo &&
@@ -25,11 +26,26 @@ test_expect_success setup '
test_tick &&
git commit -m "C" &&
git tag C &&
+ echo Dello > bar &&
+ git add bar &&
+ test_tick &&
+ git commit -m "E" &&
+ git tag E &&
git checkout master &&
git checkout branch foo &&
test_tick &&
git commit -m "B" &&
- git tag B
+ git tag B &&
+ echo Cello > bar &&
+ git add bar &&
+ test_tick &&
+ git commit -m "D" &&
+ git tag D &&
+ echo Nello > bar &&
+ git add bar &&
+ test_tick &&
+ git commit -m "F" &&
+ git tag F
'
cat >expect <<EOF
@@ -53,8 +69,119 @@ test_expect_success '--cherry-pick foo comes up empty' '
test -z "$(git rev-list --left-right --cherry-pick B...C -- foo)"
'
+cat >expect <<EOF
+>tags/C
+EOF
+
test_expect_success '--cherry-pick bar does not come up empty' '
- ! test -z "$(git rev-list --left-right --cherry-pick B...C -- bar)"
+ git rev-list --left-right --cherry-pick B...C -- bar > actual &&
+ git name-rev --stdin --name-only --refs="*tags/*" \
+ < actual > actual.named &&
+ test_cmp actual.named expect
+'
+
+test_expect_success 'bar does not come up empty' '
+ git rev-list --left-right B...C -- bar > actual &&
+ git name-rev --stdin --name-only --refs="*tags/*" \
+ < actual > actual.named &&
+ test_cmp actual.named expect
+'
+
+cat >expect <<EOF
+<tags/F
+>tags/E
+EOF
+
+test_expect_success '--cherry-pick bar does not come up empty (II)' '
+ git rev-list --left-right --cherry-pick F...E -- bar > actual &&
+ git name-rev --stdin --name-only --refs="*tags/*" \
+ < actual > actual.named &&
+ test_cmp actual.named expect
+'
+
+cat >expect <<EOF
++tags/F
+=tags/D
++tags/E
+=tags/C
+EOF
+
+test_expect_success '--cherry-mark' '
+ git rev-list --cherry-mark F...E -- bar > actual &&
+ git name-rev --stdin --name-only --refs="*tags/*" \
+ < actual > actual.named &&
+ test_cmp actual.named expect
+'
+
+cat >expect <<EOF
+<tags/F
+=tags/D
+>tags/E
+=tags/C
+EOF
+
+test_expect_success '--cherry-mark --left-right' '
+ git rev-list --cherry-mark --left-right F...E -- bar > actual &&
+ git name-rev --stdin --name-only --refs="*tags/*" \
+ < actual > actual.named &&
+ test_cmp actual.named expect
+'
+
+cat >expect <<EOF
+tags/E
+EOF
+
+test_expect_success '--cherry-pick --right-only' '
+ git rev-list --cherry-pick --right-only F...E -- bar > actual &&
+ git name-rev --stdin --name-only --refs="*tags/*" \
+ < actual > actual.named &&
+ test_cmp actual.named expect
+'
+
+test_expect_success '--cherry-pick --left-only' '
+ git rev-list --cherry-pick --left-only E...F -- bar > actual &&
+ git name-rev --stdin --name-only --refs="*tags/*" \
+ < actual > actual.named &&
+ test_cmp actual.named expect
+'
+
+cat >expect <<EOF
++tags/E
+=tags/C
+EOF
+
+test_expect_success '--cherry' '
+ git rev-list --cherry F...E -- bar > actual &&
+ git name-rev --stdin --name-only --refs="*tags/*" \
+ < actual > actual.named &&
+ test_cmp actual.named expect
+'
+
+cat >expect <<EOF
+1 1
+EOF
+
+test_expect_success '--cherry --count' '
+ git rev-list --cherry --count F...E -- bar > actual &&
+ test_cmp actual expect
+'
+
+cat >expect <<EOF
+2 2
+EOF
+
+test_expect_success '--cherry-mark --count' '
+ git rev-list --cherry-mark --count F...E -- bar > actual &&
+ test_cmp actual expect
+'
+
+cat >expect <<EOF
+1 1 2
+EOF
+
+test_expect_success '--cherry-mark --left-right --count' '
+ git rev-list --cherry-mark --left-right --count F...E -- bar > actual &&
+ test_cmp actual expect
'
test_expect_success '--cherry-pick with independent, but identical branches' '
@@ -75,11 +202,8 @@ 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 &&
+ git rev-list --count --left-right C...D > actual &&
test_cmp expect actual
'
diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh
index c8a96a9..20e3e25 100755
--- a/t/t6009-rev-list-parent.sh
+++ b/t/t6009-rev-list-parent.sh
@@ -1,14 +1,15 @@
#!/bin/sh
-test_description='properly cull all ancestors'
+test_description='ancestor culling and limiting by parent number'
. ./test-lib.sh
-commit () {
- test_tick &&
- echo $1 >file &&
- git commit -a -m $1 &&
- git tag $1
+check_revlist () {
+ rev_list_args="$1" &&
+ shift &&
+ git rev-parse "$@" >expect &&
+ git rev-list $rev_list_args --all >actual &&
+ test_cmp expect actual
}
test_expect_success setup '
@@ -16,13 +17,13 @@ test_expect_success setup '
touch file &&
git add file &&
- commit one &&
+ test_commit one &&
- test_tick=$(($test_tick - 2400))
+ test_tick=$(($test_tick - 2400)) &&
- commit two &&
- commit three &&
- commit four &&
+ test_commit two &&
+ test_commit three &&
+ test_commit four &&
git log --pretty=oneline --abbrev-commit
'
@@ -35,4 +36,116 @@ test_expect_success 'one is ancestor of others and should not be shown' '
'
+test_expect_success 'setup roots, merges and octopuses' '
+
+ git checkout --orphan newroot &&
+ test_commit five &&
+ git checkout -b sidebranch two &&
+ test_commit six &&
+ git checkout -b anotherbranch three &&
+ test_commit seven &&
+ git checkout -b yetanotherbranch four &&
+ test_commit eight &&
+ git checkout master &&
+ test_tick &&
+ git merge --allow-unrelated-histories -m normalmerge newroot &&
+ git tag normalmerge &&
+ test_tick &&
+ git merge -m tripus sidebranch anotherbranch &&
+ git tag tripus &&
+ git checkout -b tetrabranch normalmerge &&
+ test_tick &&
+ git merge -m tetrapus sidebranch anotherbranch yetanotherbranch &&
+ git tag tetrapus &&
+ git checkout master
+'
+
+test_expect_success 'rev-list roots' '
+
+ check_revlist "--max-parents=0" one five
+'
+
+test_expect_success 'rev-list no merges' '
+
+ check_revlist "--max-parents=1" one eight seven six five four three two &&
+ check_revlist "--no-merges" one eight seven six five four three two
+'
+
+test_expect_success 'rev-list no octopuses' '
+
+ check_revlist "--max-parents=2" one normalmerge eight seven six five four three two
+'
+
+test_expect_success 'rev-list no roots' '
+
+ check_revlist "--min-parents=1" tetrapus tripus normalmerge eight seven six four three two
+'
+
+test_expect_success 'rev-list merges' '
+
+ check_revlist "--min-parents=2" tetrapus tripus normalmerge &&
+ check_revlist "--merges" tetrapus tripus normalmerge
+'
+
+test_expect_success 'rev-list octopus' '
+
+ check_revlist "--min-parents=3" tetrapus tripus
+'
+
+test_expect_success 'rev-list ordinary commits' '
+
+ check_revlist "--min-parents=1 --max-parents=1" eight seven six four three two
+'
+
+test_expect_success 'rev-list --merges --no-merges yields empty set' '
+
+ check_revlist "--min-parents=2 --no-merges" &&
+ check_revlist "--merges --no-merges" &&
+ check_revlist "--no-merges --merges"
+'
+
+test_expect_success 'rev-list override and infinities' '
+
+ check_revlist "--min-parents=2 --max-parents=1 --max-parents=3" tripus normalmerge &&
+ check_revlist "--min-parents=1 --min-parents=2 --max-parents=7" tetrapus tripus normalmerge &&
+ check_revlist "--min-parents=2 --max-parents=8" tetrapus tripus normalmerge &&
+ check_revlist "--min-parents=2 --max-parents=-1" tetrapus tripus normalmerge &&
+ check_revlist "--min-parents=2 --no-max-parents" tetrapus tripus normalmerge &&
+ check_revlist "--max-parents=0 --min-parents=1 --no-min-parents" one five
+'
+
+test_expect_success 'dodecapus' '
+
+ roots= &&
+ for i in 1 2 3 4 5 6 7 8 9 10 11
+ do
+ git checkout -b root$i five &&
+ test_commit $i &&
+ roots="$roots root$i" ||
+ return
+ done &&
+ git checkout master &&
+ test_tick &&
+ git merge -m dodecapus $roots &&
+ git tag dodecapus &&
+
+ check_revlist "--min-parents=4" dodecapus tetrapus &&
+ check_revlist "--min-parents=8" dodecapus &&
+ check_revlist "--min-parents=12" dodecapus &&
+ check_revlist "--min-parents=13" &&
+ check_revlist "--min-parents=4 --max-parents=11" tetrapus
+'
+
+test_expect_success 'ancestors with the same commit time' '
+
+ test_tick_keep=$test_tick &&
+ for i in 1 2 3 4 5 6 7 8; do
+ test_tick=$test_tick_keep
+ test_commit t$i
+ done &&
+ git rev-list t1^! --not t$i >result &&
+ >expect &&
+ test_cmp expect result
+'
+
test_done
diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh
index 0144d9e..e0c5f44 100755
--- a/t/t6010-merge-base.sh
+++ b/t/t6010-merge-base.sh
@@ -3,13 +3,11 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='Merge base computation.
+test_description='Merge base and parent list computation.
'
. ./test-lib.sh
-T=$(git write-tree)
-
M=1130000000
Z=+0000
@@ -19,159 +17,286 @@ 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=
+doit () {
+ OFFSET=$1 &&
+ NAME=$2 &&
+ shift 2 &&
+
+ 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
+ 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.
+test_expect_success 'setup' '
+ T=$(git mktree </dev/null)
+'
-S=$(doit 0 S)
+test_expect_success 'set up G and H' '
+ # E---D---C---B---A
+ # \"-_ \ \
+ # \ `---------G \
+ # \ \
+ # F----------------H
+ 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)
+'
-C0=$(doit -3 C0 $S)
-C1=$(doit -2 C1 $C0)
-C2=$(doit -1 C2 $C1)
+test_expect_success 'merge-base G H' '
+ git name-rev $B >expected &&
-L0=$(doit 1 L0 $S)
-L1=$(doit 2 L1 $L0)
-L2=$(doit 3 L2 $L1)
+ MB=$(git merge-base G H) &&
+ git name-rev "$MB" >actual.single &&
-R0=$(doit 1 R0 $S)
-R1=$(doit 2 R1 $R0)
-R2=$(doit 3 R2 $R1)
+ MB=$(git merge-base --all G H) &&
+ git name-rev "$MB" >actual.all &&
-PL=$(doit 4 PL $L2 $C2)
-PR=$(doit 4 PR $C2 $R2)
+ MB=$(git show-branch --merge-base G H) &&
+ git name-rev "$MB" >actual.sb &&
-test_expect_success 'compute merge-base (single)' \
- 'MB=$(git merge-base PL PR) &&
- expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"'
+ test_cmp expected actual.single &&
+ test_cmp expected actual.all &&
+ test_cmp expected actual.sb
+'
-test_expect_success 'compute merge-base (all)' \
- 'MB=$(git merge-base --all PL PR) &&
- expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"'
+test_expect_success 'merge-base/show-branch --independent' '
+ git name-rev "$H" >expected1 &&
+ git name-rev "$H" "$G" >expected2 &&
-# 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)
+ parents=$(git merge-base --independent H) &&
+ git name-rev $parents >actual1.mb &&
+ parents=$(git merge-base --independent A H G) &&
+ git name-rev $parents >actual2.mb &&
+
+ parents=$(git show-branch --independent H) &&
+ git name-rev $parents >actual1.sb &&
+ parents=$(git show-branch --independent A H G) &&
+ git name-rev $parents >actual2.sb &&
+
+ test_cmp expected1 actual1.mb &&
+ test_cmp expected2 actual2.mb &&
+ test_cmp expected1 actual1.sb &&
+ test_cmp expected2 actual2.sb
+'
+
+test_expect_success 'unsynchronized clocks' '
+ # This test is 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) &&
+
+ git name-rev $C2 >expected &&
+
+ MB=$(git merge-base PL PR) &&
+ git name-rev "$MB" >actual.single &&
+
+ MB=$(git merge-base --all PL PR) &&
+ git name-rev "$MB" >actual.all &&
+
+ test_cmp expected actual.single &&
+ test_cmp expected actual.all
+'
+
+test_expect_success '--independent with unsynchronized clocks' '
+ IB=$(doit 0 IB) &&
+ I1=$(doit -10 I1 $IB) &&
+ I2=$(doit -9 I2 $I1) &&
+ I3=$(doit -8 I3 $I2) &&
+ I4=$(doit -7 I4 $I3) &&
+ I5=$(doit -6 I5 $I4) &&
+ I6=$(doit -5 I6 $I5) &&
+ I7=$(doit -4 I7 $I6) &&
+ I8=$(doit -3 I8 $I7) &&
+ IH=$(doit -2 IH $I8) &&
+
+ echo $IH >expected &&
+ git merge-base --independent IB IH >actual &&
+ test_cmp expected actual
+'
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 &&
+ # 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_commit MMR &&
+ test_commit MM1 &&
+ test_commit MM-o &&
+ test_commit MM-p &&
+ test_commit MM-q &&
+ test_commit 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 &&
+ test_commit MM-r &&
+ test_commit MM-s &&
+ test_commit MM-t &&
+ test_commit 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_commit MM-u &&
+ test_commit MM-v &&
+ test_commit MM-w &&
+ test_commit MM-x &&
+ test_commit 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"
-'
+ git rev-parse --verify MM1 >expected &&
+ git rev-parse --verify MMR >expected.sb &&
+
+ git merge-base --all MMA MMB MMC >actual &&
+ git merge-base --all --octopus MMA MMB MMC >actual.common &&
+ git show-branch --merge-base MMA MMB MMC >actual.sb &&
-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_cmp expected actual &&
+ test_cmp expected.sb actual.common &&
+ test_cmp expected.sb actual.sb
'
-test_expect_success 'criss-cross merge-base for octopus-step (setup)' '
+test_expect_success 'criss-cross merge-base for octopus-step' '
git reset --hard MMR &&
- test_tick && git commit --allow-empty -m 1 && git tag CC1 &&
+ test_commit 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 &&
+ test_commit CC2 &&
+ test_tick &&
+ # E is a root commit unrelated to MMR root on which CC1 is based
+ git merge -s ours --allow-unrelated-histories CC1 &&
+ test_commit CC-o &&
+ test_commit CCB &&
git reset --hard CC1 &&
- test_tick && git merge -s ours CC2 &&
- test_tick && git commit --allow-empty -m A && git tag CCA
+ # E is a root commit unrelated to MMR root on which CC1 is based
+ git merge -s ours --allow-unrelated-histories CC2 &&
+ test_commit CCA &&
+
+ git rev-parse CC1 CC2 >expected &&
+ git merge-base --all CCB CCA^^ CCA^^2 >actual &&
+
+ sort expected >expected.sorted &&
+ sort actual >actual.sorted &&
+ test_cmp expected.sorted actual.sorted
+'
+
+test_expect_success 'using reflog to find the fork point' '
+ git reset --hard &&
+ git checkout -b base $E &&
+
+ (
+ for count in 1 2 3
+ do
+ git commit --allow-empty -m "Base commit #$count" &&
+ git rev-parse HEAD >expect$count &&
+ git checkout -B derived &&
+ git commit --allow-empty -m "Derived #$count" &&
+ git rev-parse HEAD >derived$count &&
+ git checkout -B base $E || exit 1
+ done
+
+ for count in 1 2 3
+ do
+ git merge-base --fork-point base $(cat derived$count) >actual &&
+ test_cmp expect$count actual || exit 1
+ done
+
+ ) &&
+ # check that we correctly default to HEAD
+ git checkout derived &&
+ git merge-base --fork-point base >actual &&
+ test_cmp expect3 actual
'
-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_expect_success 'merge-base --octopus --all for complex tree' '
+ # Best common ancestor for JE, JAA and JDD is JC
+ # JE
+ # / |
+ # / |
+ # / |
+ # JAA / |
+ # |\ / |
+ # | \ | JDD |
+ # | \ |/ | |
+ # | JC JD |
+ # | | /| |
+ # | |/ | |
+ # JA | | |
+ # |\ /| | |
+ # X JB | X X
+ # \ \ | / /
+ # \__\|/___/
+ # J
+ test_commit J &&
+ test_commit JB &&
+ git reset --hard J &&
+ test_commit JC &&
+ git reset --hard J &&
+ test_commit JTEMP1 &&
+ test_merge JA JB &&
+ test_merge JAA JC &&
+ git reset --hard J &&
+ test_commit JTEMP2 &&
+ test_merge JD JB &&
+ test_merge JDD JC &&
+ git reset --hard J &&
+ test_commit JTEMP3 &&
+ test_merge JE JC &&
+ git rev-parse JC >expected &&
+ git merge-base --all --octopus JAA JDD JE >actual &&
+ test_cmp expected actual
'
test_done
diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh
index af34a1e..2a0fbb8 100755
--- a/t/t6012-rev-list-simplify.sh
+++ b/t/t6012-rev-list-simplify.sh
@@ -14,21 +14,24 @@ unnote () {
test_expect_success setup '
echo "Hi there" >file &&
- git add file &&
- test_tick && git commit -m "Initial file" &&
+ echo "initial" >lost &&
+ git add file lost &&
+ test_tick && git commit -m "Initial file and lost" &&
note A &&
git branch other-branch &&
echo "Hello" >file &&
- git add file &&
- test_tick && git commit -m "Modified file" &&
+ echo "second" >lost &&
+ git add file lost &&
+ test_tick && git commit -m "Modified file and lost" &&
note B &&
git checkout other-branch &&
echo "Hello" >file &&
- git add file &&
+ >lost &&
+ git add file lost &&
test_tick && git commit -m "Modified the file identically" &&
note C &&
@@ -37,7 +40,9 @@ test_expect_success setup '
test_tick && git commit -m "Add another file" &&
note D &&
- test_tick && git merge -m "merge" master &&
+ test_tick &&
+ test_must_fail git merge -m "merge" master &&
+ >lost && git commit -a -m "merge" &&
note E &&
echo "Yet another" >elif &&
@@ -56,35 +61,70 @@ test_expect_success setup '
echo "Final change" >file &&
test_tick && git commit -a -m "Final change" &&
- note I
+ note I &&
+
+ git symbolic-ref HEAD refs/heads/unrelated &&
+ git rm -f "*" &&
+ echo "Unrelated branch" >side &&
+ git add side &&
+ test_tick && git commit -m "Side root" &&
+ note J &&
+
+ git checkout master &&
+ test_tick && git merge --allow-unrelated-histories -m "Coolest" unrelated &&
+ note K &&
+
+ echo "Immaterial" >elif &&
+ git add elif &&
+ test_tick && git commit -m "Last" &&
+ note L
'
FMT='tformat:%P %H | %s'
-check_result () {
+check_outcome () {
+ outcome=$1
+ shift
for c in $1
do
echo "$c"
done >expect &&
shift &&
param="$*" &&
- test_expect_success "log $param" '
+ test_expect_$outcome "log $param" '
git log --pretty="$FMT" --parents $param |
unnote >actual &&
sed -e "s/^.* \([^ ]*\) .*/\1/" >check <actual &&
- test_cmp expect check || {
- cat actual
- false
- }
+ test_cmp expect check
'
}
-check_result 'I H G F E D C B A' --full-history
-check_result 'I H E C B A' --full-history -- file
-check_result 'I H E C B A' --full-history --topo-order -- file
-check_result 'I H E C B A' --full-history --date-order -- file
+check_result () {
+ check_outcome success "$@"
+}
+
+check_result 'L K J I H G F E D C B A' --full-history
+check_result 'K I H E C B A' --full-history -- file
+check_result 'K I H E C B A' --full-history --topo-order -- file
+check_result 'K I H E C B A' --full-history --date-order -- file
check_result 'I E C B A' --simplify-merges -- file
check_result 'I B A' -- file
check_result 'I B A' --topo-order -- file
+check_result 'H' --first-parent -- another-file
+
+check_result 'E C B A' --full-history E -- lost
+test_expect_success 'full history simplification without parent' '
+ printf "%s\n" E C B A >expect &&
+ git log --pretty="$FMT" --full-history E -- lost |
+ unnote >actual &&
+ sed -e "s/^.* \([^ ]*\) .*/\1/" >check <actual &&
+ test_cmp expect check
+'
+
+test_expect_success '--full-diff is not affected by --parents' '
+ git log -p --pretty="%H" --full-diff -- file >expected &&
+ git log -p --pretty="%H" --full-diff --parents -- file >actual &&
+ test_cmp expected actual
+'
test_done
diff --git a/t/t6014-rev-list-all.sh b/t/t6014-rev-list-all.sh
index 991ab4a..c9bedd2 100755
--- a/t/t6014-rev-list-all.sh
+++ b/t/t6014-rev-list-all.sh
@@ -35,4 +35,8 @@ test_expect_success 'repack does not lose detached HEAD' '
'
+test_expect_success 'rev-list --graph --no-walk is forbidden' '
+ test_must_fail git rev-list --graph --no-walk HEAD
+'
+
test_done
diff --git a/t/t6015-rev-list-show-all-parents.sh b/t/t6015-rev-list-show-all-parents.sh
index 8b146fb..3c73c93 100755
--- a/t/t6015-rev-list-show-all-parents.sh
+++ b/t/t6015-rev-list-show-all-parents.sh
@@ -6,11 +6,11 @@ test_description='--show-all --parents does not rewrite TREESAME commits'
test_expect_success 'set up --show-all --parents test' '
test_commit one foo.txt &&
- commit1=`git rev-list -1 HEAD` &&
+ commit1=$(git rev-list -1 HEAD) &&
test_commit two bar.txt &&
- commit2=`git rev-list -1 HEAD` &&
+ commit2=$(git rev-list -1 HEAD) &&
test_commit three foo.txt &&
- commit3=`git rev-list -1 HEAD`
+ commit3=$(git rev-list -1 HEAD)
'
test_expect_success '--parents rewrites TREESAME parents correctly' '
diff --git a/t/t6016-rev-list-graph-simplify-history.sh b/t/t6016-rev-list-graph-simplify-history.sh
index 27fd52b..f7181d1 100755
--- a/t/t6016-rev-list-graph-simplify-history.sh
+++ b/t/t6016-rev-list-graph-simplify-history.sh
@@ -29,7 +29,7 @@ test_expect_success 'set up rev-list --graph test' '
# Octopus merge B and C into branch A
git checkout A &&
git merge B C &&
- git tag A4
+ git tag A4 &&
test_commit A5 bar.txt &&
@@ -39,7 +39,7 @@ test_expect_success 'set up rev-list --graph test' '
test_commit C4 bar.txt &&
git checkout A &&
git merge -s ours C &&
- git tag A6
+ git tag A6 &&
test_commit A7 bar.txt &&
@@ -90,7 +90,7 @@ test_expect_success '--graph --all' '
# that undecorated merges are interesting, even with --simplify-by-decoration
test_expect_success '--graph --simplify-by-decoration' '
rm -f expected &&
- git tag -d A4
+ git tag -d A4 &&
echo "* $A7" >> expected &&
echo "* $A6" >> expected &&
echo "|\\ " >> expected &&
@@ -116,12 +116,15 @@ test_expect_success '--graph --simplify-by-decoration' '
test_cmp expected actual
'
-# Get rid of all decorations on branch B, and graph with it simplified away
+test_expect_success 'setup: get rid of decorations on B' '
+ git tag -d B2 &&
+ git tag -d B1 &&
+ git branch -d B
+'
+
+# Graph with branch B 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 &&
@@ -143,9 +146,6 @@ test_expect_success '--graph --simplify-by-decoration prune branch B' '
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 &&
@@ -163,9 +163,6 @@ test_expect_success '--graph --full-history -- bar.txt' '
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 &&
@@ -181,9 +178,6 @@ test_expect_success '--graph --full-history --simplify-merges -- bar.txt' '
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 &&
@@ -196,9 +190,6 @@ test_expect_success '--graph -- bar.txt' '
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 &&
diff --git a/t/t6017-rev-list-stdin.sh b/t/t6017-rev-list-stdin.sh
index f1c32db..667b375 100755
--- a/t/t6017-rev-list-stdin.sh
+++ b/t/t6017-rev-list-stdin.sh
@@ -58,4 +58,21 @@ check side-3 ^side-4 -- file-3
check side-3 ^side-2
check side-3 ^side-2 -- file-1
+test_expect_success 'not only --stdin' '
+ cat >expect <<-EOF &&
+ 7
+
+ file-1
+ file-2
+ EOF
+ cat >input <<-EOF &&
+ ^master^
+ --
+ file-2
+ EOF
+ git log --pretty=tformat:%s --name-only --stdin master -- file-1 \
+ <input >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index 58428d9..d00f7db 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -69,6 +69,18 @@ test_expect_success 'rev-parse --glob=heads/subspace' '
'
+test_expect_failure 'rev-parse accepts --glob as detached option' '
+
+ compare rev-parse "subspace/one subspace/two" "--glob heads/subspace"
+
+'
+
+test_expect_failure 'rev-parse is not confused by option-like glob' '
+
+ compare rev-parse "master" "--glob --symbolic master"
+
+'
+
test_expect_success 'rev-parse --branches=subspace/*' '
compare rev-parse "subspace/one subspace/two" "--branches=subspace/*"
@@ -117,12 +129,36 @@ test_expect_success 'rev-parse --remotes=foo' '
'
+test_expect_success 'rev-parse --exclude with --branches' '
+ compare rev-parse "--exclude=*/* --branches" "master someref subspace-x"
+'
+
+test_expect_success 'rev-parse --exclude with --all' '
+ compare rev-parse "--exclude=refs/remotes/* --all" "--branches --tags"
+'
+
+test_expect_success 'rev-parse accumulates multiple --exclude' '
+ compare rev-parse "--exclude=refs/remotes/* --exclude=refs/tags/* --all" --branches
+'
+
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 refs/heads/subspace/*' '
+
+ compare rev-list "subspace/one subspace/two" "--glob refs/heads/subspace/*"
+
+'
+
+test_expect_success 'rev-list not confused by option-like --glob arg' '
+
+ compare rev-list "master" "--glob -0 master"
+
+'
+
test_expect_success 'rev-list --glob=heads/subspace/*' '
compare rev-list "subspace/one subspace/two" "--glob=heads/subspace/*"
@@ -207,4 +243,78 @@ test_expect_success 'rev-list --remotes=foo' '
'
+test_expect_success 'rev-list --exclude with --branches' '
+ compare rev-list "--exclude=*/* --branches" "master someref subspace-x"
+'
+
+test_expect_success 'rev-list --exclude with --all' '
+ compare rev-list "--exclude=refs/remotes/* --all" "--branches --tags"
+'
+
+test_expect_success 'rev-list accumulates multiple --exclude' '
+ compare rev-list "--exclude=refs/remotes/* --exclude=refs/tags/* --all" --branches
+'
+
+
+# "git rev-list<ENTER>" is likely to be a bug in the calling script and may
+# deserve an error message, but do cases where set of refs programatically
+# given using globbing and/or --stdin need to fail with the same error, or
+# are we better off reporting a success with no output? The following few
+# tests document the current behaviour to remind us that we might want to
+# think about this issue.
+
+test_expect_failure 'rev-list may want to succeed with empty output on no input (1)' '
+ >expect &&
+ git rev-list --stdin <expect >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'rev-list may want to succeed with empty output on no input (2)' '
+ >expect &&
+ git rev-list --exclude=* --all >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'rev-list may want to succeed with empty output on no input (3)' '
+ (
+ test_create_repo empty &&
+ cd empty &&
+ >expect &&
+ git rev-list --all >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'shortlog accepts --glob/--tags/--remotes' '
+
+ compare shortlog "subspace/one subspace/two" --branches=subspace &&
+ compare shortlog \
+ "master subspace-x someref other/three subspace/one subspace/two" \
+ --branches &&
+ compare shortlog master "--glob=heads/someref/* master" &&
+ compare shortlog "subspace/one subspace/two other/three" \
+ "--glob=heads/subspace/* --glob=heads/other/*" &&
+ compare shortlog \
+ "master other/three someref subspace-x subspace/one subspace/two" \
+ "--glob=heads/*" &&
+ compare shortlog foo/bar --tags=foo &&
+ compare shortlog foo/bar --tags &&
+ compare shortlog foo/baz --remotes=foo
+
+'
+
+test_expect_failure 'shortlog accepts --glob as detached option' '
+
+ compare shortlog \
+ "master other/three someref subspace-x subspace/one subspace/two" \
+ "--glob heads/*"
+
+'
+
+test_expect_failure 'shortlog --glob is not confused by option-like argument' '
+
+ compare shortlog master "--glob -e master"
+
+'
+
test_done
diff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh
index 7641029..dabebae 100755
--- a/t/t6019-rev-list-ancestry-path.sh
+++ b/t/t6019-rev-list-ancestry-path.sh
@@ -13,6 +13,13 @@ test_description='--ancestry-path'
#
# D..M -- M.t == M
# --ancestry-path D..M -- M.t == M
+#
+# F...I == F G H I
+# --ancestry-path F...I == F H I
+#
+# G..M -- G.t == [nothing - was dropped in "-s ours" merge L]
+# --ancestry-path G..M -- G.t == L
+# --ancestry-path --simplify-merges G^..M -- G.t == G L
. ./test-lib.sh
@@ -63,11 +70,88 @@ test_expect_success 'rev-list D..M -- M.t' '
test_cmp expect actual
'
-test_expect_success 'rev-list --ancestry-patch D..M -- M.t' '
+test_expect_success 'rev-list --ancestry-path D..M -- M.t' '
echo M >expect &&
git rev-list --ancestry-path --format=%s D..M -- M.t |
sed -e "/^commit /d" >actual &&
test_cmp expect actual
'
+test_expect_success 'rev-list F...I' '
+ for c in F G H I; do echo $c; done >expect &&
+ git rev-list --format=%s F...I |
+ sed -e "/^commit /d" |
+ sort >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rev-list --ancestry-path F...I' '
+ for c in F H I; do echo $c; done >expect &&
+ git rev-list --ancestry-path --format=%s F...I |
+ sed -e "/^commit /d" |
+ sort >actual &&
+ test_cmp expect actual
+'
+
+# G.t is dropped in an "-s ours" merge
+test_expect_success 'rev-list G..M -- G.t' '
+ >expect &&
+ git rev-list --format=%s G..M -- G.t |
+ sed -e "/^commit /d" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rev-list --ancestry-path G..M -- G.t' '
+ echo L >expect &&
+ git rev-list --ancestry-path --format=%s G..M -- G.t |
+ sed -e "/^commit /d" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rev-list --ancestry-path --simplify-merges G^..M -- G.t' '
+ for c in G L; do echo $c; done >expect &&
+ git rev-list --ancestry-path --simplify-merges --format=%s G^..M -- G.t |
+ sed -e "/^commit /d" |
+ sort >actual &&
+ test_cmp expect actual
+'
+
+# b---bc
+# / \ /
+# a X
+# \ / \
+# c---cb
+#
+# All refnames prefixed with 'x' to avoid confusion with the tags
+# generated by test_commit on case-insensitive systems.
+test_expect_success 'setup criss-cross' '
+ mkdir criss-cross &&
+ (cd criss-cross &&
+ git init &&
+ test_commit A &&
+ git checkout -b xb master &&
+ test_commit B &&
+ git checkout -b xc master &&
+ test_commit C &&
+ git checkout -b xbc xb -- &&
+ git merge xc &&
+ git checkout -b xcb xc -- &&
+ git merge xb &&
+ git checkout master)
+'
+
+# no commits in bc descend from cb
+test_expect_success 'criss-cross: rev-list --ancestry-path cb..bc' '
+ (cd criss-cross &&
+ git rev-list --ancestry-path xcb..xbc > actual &&
+ test -z "$(cat actual)")
+'
+
+# no commits in repository descend from cb
+test_expect_success 'criss-cross: rev-list --ancestry-path --all ^cb' '
+ (cd criss-cross &&
+ git rev-list --ancestry-path --all ^xcb > actual &&
+ test -z "$(cat actual)")
+'
+
test_done
diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh
index e71c687..2af1bee 100755
--- a/t/t6020-merge-df.sh
+++ b/t/t6020-merge-df.sh
@@ -6,23 +6,28 @@
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' '
+test_expect_success 'prepare repository' '
+ echo Hello >init &&
+ git add init &&
+ git commit -m initial &&
+
+ 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_success 'Merge with d/f conflicts' '
+ test_expect_code 1 git merge -m "merge msg" master
+'
+
+test_expect_success 'F/D conflict' '
git reset --hard &&
git checkout master &&
rm .git/index &&
@@ -45,4 +50,61 @@ test_expect_failure 'F/D conflict' '
git merge master
'
+test_expect_success 'setup modify/delete + directory/file conflict' '
+ git checkout --orphan modify &&
+ git rm -rf . &&
+ git clean -fdqx &&
+
+ printf "a\nb\nc\nd\ne\nf\ng\nh\n" >letters &&
+ git add letters &&
+ git commit -m initial &&
+
+ # Throw in letters.txt for sorting order fun
+ # ("letters.txt" sorts between "letters" and "letters/file")
+ echo i >>letters &&
+ echo "version 2" >letters.txt &&
+ git add letters letters.txt &&
+ git commit -m modified &&
+
+ git checkout -b delete HEAD^ &&
+ git rm letters &&
+ mkdir letters &&
+ >letters/file &&
+ echo "version 1" >letters.txt &&
+ git add letters letters.txt &&
+ git commit -m deleted
+'
+
+test_expect_success 'modify/delete + directory/file conflict' '
+ git checkout delete^0 &&
+ test_must_fail git merge modify &&
+
+ test 5 -eq $(git ls-files -s | wc -l) &&
+ test 4 -eq $(git ls-files -u | wc -l) &&
+ test 1 -eq $(git ls-files -o | wc -l) &&
+
+ test -f letters/file &&
+ test -f letters.txt &&
+ test -f letters~modify
+'
+
+test_expect_success 'modify/delete + directory/file conflict; other way' '
+ # Yes, we really need the double reset since "letters" appears as
+ # both a file and a directory.
+ git reset --hard &&
+ git reset --hard &&
+ git clean -f &&
+ git checkout modify^0 &&
+
+ test_must_fail git merge delete &&
+
+ test 5 -eq $(git ls-files -s | wc -l) &&
+ test 4 -eq $(git ls-files -u | wc -l) &&
+ test 1 -eq $(git ls-files -o | wc -l) &&
+
+ test -f letters/file &&
+ test -f letters.txt &&
+ test -f letters~HEAD
+'
+
test_done
diff --git a/t/t6021-merge-criss-cross.sh b/t/t6021-merge-criss-cross.sh
index 331b9b0..213deec 100755
--- a/t/t6021-merge-criss-cross.sh
+++ b/t/t6021-merge-criss-cross.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Fredrik Kuivinen
#
-# See http://marc.theaimsgroup.com/?l=git&m=111463358500362&w=2 for a
+# See http://marc.info/?l=git&m=111463358500362&w=2 for a
# nice description of what this is about.
@@ -48,7 +48,7 @@ echo "1
" > file &&
git commit -m "C3" file &&
git branch C3 &&
-git merge "pre E3 merge" B A &&
+git merge -m "pre E3 merge" A &&
echo "1
2
3 changed in E3, branch B. New file size
@@ -61,7 +61,7 @@ echo "1
" > file &&
git commit -m "E3" file &&
git checkout A &&
-git merge "pre D8 merge" A C3 &&
+git merge -m "pre D8 merge" C3 &&
echo "1
2
3 changed in C3, branch B
@@ -73,7 +73,7 @@ echo "1
9" > file &&
git commit -m D8 file'
-test_expect_success 'Criss-cross merge' 'git merge "final merge" A B'
+test_expect_success 'Criss-cross merge' 'git merge -m "final merge" B'
cat > file-expect <<EOF
1
diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
index b66544b..05ebba7 100755
--- a/t/t6022-merge-rename.sh
+++ b/t/t6022-merge-rename.sh
@@ -3,6 +3,11 @@
test_description='Merge-recursive merging renames'
. ./test-lib.sh
+modify () {
+ sed -e "$1" <"$2" >"$2.x" &&
+ mv "$2.x" "$2"
+}
+
test_expect_success setup \
'
cat >A <<\EOF &&
@@ -94,251 +99,805 @@ 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
- }
+ git show-branch &&
+ test_expect_code 1 git pull . white &&
+ git ls-files -s &&
+ git ls-files -u B >b.stages &&
+ test_line_count = 3 b.stages &&
+ git ls-files -s N >n.stages &&
+ test_line_count = 1 n.stages &&
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
- }
+ }" B | grep master &&
+ git diff --exit-code white N
'
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
- }
+ rm -f B &&
+ git reset --hard &&
+ git checkout red &&
+ test_expect_code 1 git pull . white &&
+ git ls-files -u B >b.stages &&
+ test_line_count = 3 b.stages &&
+ git ls-files -s N >n.stages &&
+ test_line_count = 1 n.stages &&
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
- }
+ }" B | grep red &&
+ git diff --exit-code white N
'
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
- }
+ git reset --hard &&
+ git show-branch &&
+ test_expect_code 1 git pull . master &&
+ git ls-files -u B >b.stages &&
+ test_line_count = 3 b.stages &&
+ git ls-files -s N >n.stages &&
+ test_line_count = 1 n.stages &&
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
- }
+ }" B | grep red &&
+ git diff --exit-code white N
'
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
- }
+ git reset --hard &&
+ git show-branch &&
+ test_expect_code 1 git pull . blue &&
+ git ls-files -u A >a.stages &&
+ test_line_count = 1 a.stages &&
+ git ls-files -u B >b.stages &&
+ test_line_count = 1 b.stages &&
+ git ls-files -u C >c.stages &&
+ test_line_count = 1 c.stages &&
+ git ls-files -s N >n.stages &&
+ test_line_count = 1 n.stages &&
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
- }
+ }" B | grep red &&
+ git diff --exit-code white N
'
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
- }
+ git reset --hard &&
+ git show-branch &&
+ echo >A this file should not matter &&
+ test_expect_code 1 git pull . white &&
+ test_path_is_file A
'
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
- }
+ git reset --hard &&
+ git checkout white &&
+ git show-branch &&
+ rm -f A &&
+ echo >A this file should not matter &&
+ test_expect_code 1 git pull . red &&
+ test_path_is_file A
'
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 &&
+ rm -f A M &&
+ git checkout -f master &&
+ git tag -f anchor &&
+ git show-branch &&
+ git pull . yellow &&
+ test_path_is_missing M &&
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
- }
+ 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 &&
+ test_expect_code 128 git pull . yellow &&
+ test_cmp M M.saved &&
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
- }
+ 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 &&
+ test_expect_code 128 git pull . yellow &&
+ test_cmp M M.saved &&
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 &&
+ 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 &&
+ test_path_is_file M &&
+ ! {
+ git ls-files -s |
+ grep M
+ } &&
git reset --hard anchor
'
test_expect_success 'merge of identical changes in a renamed file' '
- rm -f A M N
+ rm -f A M N &&
git reset --hard &&
git checkout change+rename &&
- GIT_MERGE_VERBOSITY=3 git merge change | grep "^Skipped B" &&
+ GIT_MERGE_VERBOSITY=3 git merge change | test_i18ngrep "^Skipped B" &&
git reset --hard HEAD^ &&
git checkout change &&
- GIT_MERGE_VERBOSITY=3 git merge change+rename | grep "^Skipped B"
+ GIT_MERGE_VERBOSITY=3 git merge change+rename | test_i18ngrep "^Skipped B"
+'
+
+test_expect_success 'setup for rename + d/f conflicts' '
+ git reset --hard &&
+ git checkout --orphan dir-in-way &&
+ git rm -rf . &&
+ git clean -fdqx &&
+
+ mkdir sub &&
+ mkdir dir &&
+ printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >sub/file &&
+ echo foo >dir/file-in-the-way &&
+ git add -A &&
+ git commit -m "Common commit" &&
+
+ echo 11 >>sub/file &&
+ echo more >>dir/file-in-the-way &&
+ git add -u &&
+ git commit -m "Commit to merge, with dir in the way" &&
+
+ git checkout -b dir-not-in-way &&
+ git reset --soft HEAD^ &&
+ git rm -rf dir &&
+ git commit -m "Commit to merge, with dir removed" -- dir sub/file &&
+
+ git checkout -b renamed-file-has-no-conflicts dir-in-way~1 &&
+ git rm -rf dir &&
+ git rm sub/file &&
+ printf "1\n2\n3\n4\n5555\n6\n7\n8\n9\n10\n" >dir &&
+ git add dir &&
+ git commit -m "Independent change" &&
+
+ git checkout -b renamed-file-has-conflicts dir-in-way~1 &&
+ git rm -rf dir &&
+ git mv sub/file dir &&
+ echo 12 >>dir &&
+ git add dir &&
+ git commit -m "Conflicting change"
+'
+
+printf "1\n2\n3\n4\n5555\n6\n7\n8\n9\n10\n11\n" >expected
+
+test_expect_success 'Rename+D/F conflict; renamed file merges + dir not in way' '
+ git reset --hard &&
+ git checkout -q renamed-file-has-no-conflicts^0 &&
+ git merge --strategy=recursive dir-not-in-way &&
+ git diff --quiet &&
+ test -f dir &&
+ test_cmp expected dir
+'
+
+test_expect_success 'Rename+D/F conflict; renamed file merges but dir in way' '
+ git reset --hard &&
+ rm -rf dir~* &&
+ git checkout -q renamed-file-has-no-conflicts^0 &&
+ test_must_fail git merge --strategy=recursive dir-in-way >output &&
+
+ test_i18ngrep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
+ test_i18ngrep "Auto-merging dir" output &&
+ test_i18ngrep "Adding as dir~HEAD instead" output &&
+
+ test 3 -eq "$(git ls-files -u | wc -l)" &&
+ test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
+
+ test_must_fail git diff --quiet &&
+ test_must_fail git diff --cached --quiet &&
+
+ test -f dir/file-in-the-way &&
+ test -f dir~HEAD &&
+ test_cmp expected dir~HEAD
+'
+
+test_expect_success 'Same as previous, but merged other way' '
+ git reset --hard &&
+ rm -rf dir~* &&
+ git checkout -q dir-in-way^0 &&
+ test_must_fail git merge --strategy=recursive renamed-file-has-no-conflicts >output 2>errors &&
+
+ ! grep "error: refusing to lose untracked file at" errors &&
+ test_i18ngrep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
+ test_i18ngrep "Auto-merging dir" output &&
+ test_i18ngrep "Adding as dir~renamed-file-has-no-conflicts instead" output &&
+
+ test 3 -eq "$(git ls-files -u | wc -l)" &&
+ test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
+
+ test_must_fail git diff --quiet &&
+ test_must_fail git diff --cached --quiet &&
+
+ test -f dir/file-in-the-way &&
+ test -f dir~renamed-file-has-no-conflicts &&
+ test_cmp expected dir~renamed-file-has-no-conflicts
+'
+
+cat >expected <<\EOF &&
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+<<<<<<< HEAD:dir
+12
+=======
+11
+>>>>>>> dir-not-in-way:sub/file
+EOF
+
+test_expect_success 'Rename+D/F conflict; renamed file cannot merge, dir not in way' '
+ git reset --hard &&
+ rm -rf dir~* &&
+ git checkout -q renamed-file-has-conflicts^0 &&
+ test_must_fail git merge --strategy=recursive dir-not-in-way &&
+
+ test 3 -eq "$(git ls-files -u | wc -l)" &&
+ test 3 -eq "$(git ls-files -u dir | wc -l)" &&
+
+ test_must_fail git diff --quiet &&
+ test_must_fail git diff --cached --quiet &&
+
+ test -f dir &&
+ test_cmp expected dir
+'
+
+test_expect_success 'Rename+D/F conflict; renamed file cannot merge and dir in the way' '
+ modify s/dir-not-in-way/dir-in-way/ expected &&
+
+ git reset --hard &&
+ rm -rf dir~* &&
+ git checkout -q renamed-file-has-conflicts^0 &&
+ test_must_fail git merge --strategy=recursive dir-in-way &&
+
+ test 5 -eq "$(git ls-files -u | wc -l)" &&
+ test 3 -eq "$(git ls-files -u dir | grep -v file-in-the-way | wc -l)" &&
+ test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
+
+ test_must_fail git diff --quiet &&
+ test_must_fail git diff --cached --quiet &&
+
+ test -f dir/file-in-the-way &&
+ test -f dir~HEAD &&
+ test_cmp expected dir~HEAD
+'
+
+cat >expected <<\EOF &&
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+<<<<<<< HEAD:sub/file
+11
+=======
+12
+>>>>>>> renamed-file-has-conflicts:dir
+EOF
+
+test_expect_success 'Same as previous, but merged other way' '
+ git reset --hard &&
+ rm -rf dir~* &&
+ git checkout -q dir-in-way^0 &&
+ test_must_fail git merge --strategy=recursive renamed-file-has-conflicts &&
+
+ test 5 -eq "$(git ls-files -u | wc -l)" &&
+ test 3 -eq "$(git ls-files -u dir | grep -v file-in-the-way | wc -l)" &&
+ test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
+
+ test_must_fail git diff --quiet &&
+ test_must_fail git diff --cached --quiet &&
+
+ test -f dir/file-in-the-way &&
+ test -f dir~renamed-file-has-conflicts &&
+ test_cmp expected dir~renamed-file-has-conflicts
+'
+
+test_expect_success 'setup both rename source and destination involved in D/F conflict' '
+ git reset --hard &&
+ git checkout --orphan rename-dest &&
+ git rm -rf . &&
+ git clean -fdqx &&
+
+ mkdir one &&
+ echo stuff >one/file &&
+ git add -A &&
+ git commit -m "Common commit" &&
+
+ git mv one/file destdir &&
+ git commit -m "Renamed to destdir" &&
+
+ git checkout -b source-conflict HEAD~1 &&
+ git rm -rf one &&
+ mkdir destdir &&
+ touch one destdir/foo &&
+ git add -A &&
+ git commit -m "Conflicts in the way"
+'
+
+test_expect_success 'both rename source and destination involved in D/F conflict' '
+ git reset --hard &&
+ rm -rf dir~* &&
+ git checkout -q rename-dest^0 &&
+ test_must_fail git merge --strategy=recursive source-conflict &&
+
+ test 1 -eq "$(git ls-files -u | wc -l)" &&
+
+ test_must_fail git diff --quiet &&
+
+ test -f destdir/foo &&
+ test -f one &&
+ test -f destdir~HEAD &&
+ test "stuff" = "$(cat destdir~HEAD)"
+'
+
+test_expect_success 'setup pair rename to parent of other (D/F conflicts)' '
+ git reset --hard &&
+ git checkout --orphan rename-two &&
+ git rm -rf . &&
+ git clean -fdqx &&
+
+ mkdir one &&
+ mkdir two &&
+ echo stuff >one/file &&
+ echo other >two/file &&
+ git add -A &&
+ git commit -m "Common commit" &&
+
+ git rm -rf one &&
+ git mv two/file one &&
+ git commit -m "Rename two/file -> one" &&
+
+ git checkout -b rename-one HEAD~1 &&
+ git rm -rf two &&
+ git mv one/file two &&
+ rm -r one &&
+ git commit -m "Rename one/file -> two"
+'
+
+test_expect_success 'pair rename to parent of other (D/F conflicts) w/ untracked dir' '
+ git checkout -q rename-one^0 &&
+ mkdir one &&
+ test_must_fail git merge --strategy=recursive rename-two &&
+
+ test 2 -eq "$(git ls-files -u | wc -l)" &&
+ test 1 -eq "$(git ls-files -u one | wc -l)" &&
+ test 1 -eq "$(git ls-files -u two | wc -l)" &&
+
+ test_must_fail git diff --quiet &&
+
+ test 4 -eq $(find . | grep -v .git | wc -l) &&
+
+ test -d one &&
+ test -f one~rename-two &&
+ test -f two &&
+ test "other" = $(cat one~rename-two) &&
+ test "stuff" = $(cat two)
+'
+
+test_expect_success 'pair rename to parent of other (D/F conflicts) w/ clean start' '
+ git reset --hard &&
+ git clean -fdqx &&
+ test_must_fail git merge --strategy=recursive rename-two &&
+
+ test 2 -eq "$(git ls-files -u | wc -l)" &&
+ test 1 -eq "$(git ls-files -u one | wc -l)" &&
+ test 1 -eq "$(git ls-files -u two | wc -l)" &&
+
+ test_must_fail git diff --quiet &&
+
+ test 3 -eq $(find . | grep -v .git | wc -l) &&
+
+ test -f one &&
+ test -f two &&
+ test "other" = $(cat one) &&
+ test "stuff" = $(cat two)
+'
+
+test_expect_success 'setup rename of one file to two, with directories in the way' '
+ git reset --hard &&
+ git checkout --orphan first-rename &&
+ git rm -rf . &&
+ git clean -fdqx &&
+
+ echo stuff >original &&
+ git add -A &&
+ git commit -m "Common commit" &&
+
+ mkdir two &&
+ >two/file &&
+ git add two/file &&
+ git mv original one &&
+ git commit -m "Put two/file in the way, rename to one" &&
+
+ git checkout -b second-rename HEAD~1 &&
+ mkdir one &&
+ >one/file &&
+ git add one/file &&
+ git mv original two &&
+ git commit -m "Put one/file in the way, rename to two"
+'
+
+test_expect_success 'check handling of differently renamed file with D/F conflicts' '
+ git checkout -q first-rename^0 &&
+ test_must_fail git merge --strategy=recursive second-rename &&
+
+ test 5 -eq "$(git ls-files -s | wc -l)" &&
+ test 3 -eq "$(git ls-files -u | wc -l)" &&
+ test 1 -eq "$(git ls-files -u one | wc -l)" &&
+ test 1 -eq "$(git ls-files -u two | wc -l)" &&
+ test 1 -eq "$(git ls-files -u original | wc -l)" &&
+ test 2 -eq "$(git ls-files -o | wc -l)" &&
+
+ test -f one/file &&
+ test -f two/file &&
+ test -f one~HEAD &&
+ test -f two~second-rename &&
+ ! test -f original
+'
+
+test_expect_success 'setup rename one file to two; directories moving out of the way' '
+ git reset --hard &&
+ git checkout --orphan first-rename-redo &&
+ git rm -rf . &&
+ git clean -fdqx &&
+
+ echo stuff >original &&
+ mkdir one two &&
+ touch one/file two/file &&
+ git add -A &&
+ git commit -m "Common commit" &&
+
+ git rm -rf one &&
+ git mv original one &&
+ git commit -m "Rename to one" &&
+
+ git checkout -b second-rename-redo HEAD~1 &&
+ git rm -rf two &&
+ git mv original two &&
+ git commit -m "Rename to two"
+'
+
+test_expect_success 'check handling of differently renamed file with D/F conflicts' '
+ git checkout -q first-rename-redo^0 &&
+ test_must_fail git merge --strategy=recursive second-rename-redo &&
+
+ test 3 -eq "$(git ls-files -u | wc -l)" &&
+ test 1 -eq "$(git ls-files -u one | wc -l)" &&
+ test 1 -eq "$(git ls-files -u two | wc -l)" &&
+ test 1 -eq "$(git ls-files -u original | wc -l)" &&
+ test 0 -eq "$(git ls-files -o | wc -l)" &&
+
+ test -f one &&
+ test -f two &&
+ ! test -f original
+'
+
+test_expect_success 'setup avoid unnecessary update, normal rename' '
+ git reset --hard &&
+ git checkout --orphan avoid-unnecessary-update-1 &&
+ git rm -rf . &&
+ git clean -fdqx &&
+
+ printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >original &&
+ git add -A &&
+ git commit -m "Common commit" &&
+
+ git mv original rename &&
+ echo 11 >>rename &&
+ git add -u &&
+ git commit -m "Renamed and modified" &&
+
+ git checkout -b merge-branch-1 HEAD~1 &&
+ echo "random content" >random-file &&
+ git add -A &&
+ git commit -m "Random, unrelated changes"
+'
+
+test_expect_success 'avoid unnecessary update, normal rename' '
+ git checkout -q avoid-unnecessary-update-1^0 &&
+ test-chmtime =1000000000 rename &&
+ test-chmtime -v +0 rename >expect &&
+ git merge merge-branch-1 &&
+ test-chmtime -v +0 rename >actual &&
+ test_cmp expect actual # "rename" should have stayed intact
+'
+
+test_expect_success 'setup to test avoiding unnecessary update, with D/F conflict' '
+ git reset --hard &&
+ git checkout --orphan avoid-unnecessary-update-2 &&
+ git rm -rf . &&
+ git clean -fdqx &&
+
+ mkdir df &&
+ printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >df/file &&
+ git add -A &&
+ git commit -m "Common commit" &&
+
+ git mv df/file temp &&
+ rm -rf df &&
+ git mv temp df &&
+ echo 11 >>df &&
+ git add -u &&
+ git commit -m "Renamed and modified" &&
+
+ git checkout -b merge-branch-2 HEAD~1 &&
+ >unrelated-change &&
+ git add unrelated-change &&
+ git commit -m "Only unrelated changes"
+'
+
+test_expect_success 'avoid unnecessary update, with D/F conflict' '
+ git checkout -q avoid-unnecessary-update-2^0 &&
+ test-chmtime =1000000000 df &&
+ test-chmtime -v +0 df >expect &&
+ git merge merge-branch-2 &&
+ test-chmtime -v +0 df >actual &&
+ test_cmp expect actual # "df" should have stayed intact
+'
+
+test_expect_success 'setup avoid unnecessary update, dir->(file,nothing)' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ >irrelevant &&
+ mkdir df &&
+ >df/file &&
+ git add -A &&
+ git commit -mA &&
+
+ git checkout -b side &&
+ git rm -rf df &&
+ git commit -mB &&
+
+ git checkout master &&
+ git rm -rf df &&
+ echo bla >df &&
+ git add -A &&
+ git commit -m "Add a newfile"
+'
+
+test_expect_success 'avoid unnecessary update, dir->(file,nothing)' '
+ git checkout -q master^0 &&
+ test-chmtime =1000000000 df &&
+ test-chmtime -v +0 df >expect &&
+ git merge side &&
+ test-chmtime -v +0 df >actual &&
+ test_cmp expect actual # "df" should have stayed intact
+'
+
+test_expect_success 'setup avoid unnecessary update, modify/delete' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ >irrelevant &&
+ >file &&
+ git add -A &&
+ git commit -mA &&
+
+ git checkout -b side &&
+ git rm -f file &&
+ git commit -m "Delete file" &&
+
+ git checkout master &&
+ echo bla >file &&
+ git add -A &&
+ git commit -m "Modify file"
+'
+
+test_expect_success 'avoid unnecessary update, modify/delete' '
+ git checkout -q master^0 &&
+ test-chmtime =1000000000 file &&
+ test-chmtime -v +0 file >expect &&
+ test_must_fail git merge side &&
+ test-chmtime -v +0 file >actual &&
+ test_cmp expect actual # "file" should have stayed intact
+'
+
+test_expect_success 'setup avoid unnecessary update, rename/add-dest' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ printf "1\n2\n3\n4\n5\n6\n7\n8\n" >file &&
+ git add -A &&
+ git commit -mA &&
+
+ git checkout -b side &&
+ cp file newfile &&
+ git add -A &&
+ git commit -m "Add file copy" &&
+
+ git checkout master &&
+ git mv file newfile &&
+ git commit -m "Rename file"
+'
+
+test_expect_success 'avoid unnecessary update, rename/add-dest' '
+ git checkout -q master^0 &&
+ test-chmtime =1000000000 newfile &&
+ test-chmtime -v +0 newfile >expect &&
+ git merge side &&
+ test-chmtime -v +0 newfile >actual &&
+ test_cmp expect actual # "file" should have stayed intact
+'
+
+test_expect_success 'setup merge of rename + small change' '
+ git reset --hard &&
+ git checkout --orphan rename-plus-small-change &&
+ git rm -rf . &&
+ git clean -fdqx &&
+
+ echo ORIGINAL >file &&
+ git add file &&
+
+ test_tick &&
+ git commit -m Initial &&
+ git checkout -b rename_branch &&
+ git mv file renamed_file &&
+ git commit -m Rename &&
+ git checkout rename-plus-small-change &&
+ echo NEW-VERSION >file &&
+ git commit -a -m Reformat
+'
+
+test_expect_success 'merge rename + small change' '
+ git merge rename_branch &&
+
+ test 1 -eq $(git ls-files -s | wc -l) &&
+ test 0 -eq $(git ls-files -o | wc -l) &&
+ test $(git rev-parse HEAD:renamed_file) = $(git rev-parse HEAD~1:file)
+'
+
+test_expect_success 'setup for use of extended merge markers' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ printf "1\n2\n3\n4\n5\n6\n7\n8\n" >original_file &&
+ git add original_file &&
+ git commit -mA &&
+
+ git checkout -b rename &&
+ echo 9 >>original_file &&
+ git add original_file &&
+ git mv original_file renamed_file &&
+ git commit -mB &&
+
+ git checkout master &&
+ echo 8.5 >>original_file &&
+ git add original_file &&
+ git commit -mC
+'
+
+cat >expected <<\EOF &&
+1
+2
+3
+4
+5
+6
+7
+8
+<<<<<<< HEAD:renamed_file
+9
+=======
+8.5
+>>>>>>> master^0:original_file
+EOF
+
+test_expect_success 'merge master into rename has correct extended markers' '
+ git checkout rename^0 &&
+ test_must_fail git merge -s recursive master^0 &&
+ test_cmp expected renamed_file
+'
+
+cat >expected <<\EOF &&
+1
+2
+3
+4
+5
+6
+7
+8
+<<<<<<< HEAD:original_file
+8.5
+=======
+9
+>>>>>>> rename^0:renamed_file
+EOF
+
+test_expect_success 'merge rename into master has correct extended markers' '
+ git reset --hard &&
+ git checkout master^0 &&
+ test_must_fail git merge -s recursive rename^0 &&
+ test_cmp expected renamed_file
+'
+
+test_expect_success 'setup spurious "refusing to lose untracked" message' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ > irrelevant_file &&
+ printf "1\n2\n3\n4\n5\n6\n7\n8\n" >original_file &&
+ git add irrelevant_file original_file &&
+ git commit -mA &&
+
+ git checkout -b rename &&
+ git mv original_file renamed_file &&
+ git commit -mB &&
+
+ git checkout master &&
+ git rm original_file &&
+ git commit -mC
+'
+
+test_expect_success 'no spurious "refusing to lose untracked" message' '
+ git checkout master^0 &&
+ test_must_fail git merge rename^0 2>errors.txt &&
+ ! grep "refusing to lose untracked file" errors.txt
+'
+
+test_expect_success 'do not follow renames for empty files' '
+ git checkout -f -b empty-base &&
+ >empty1 &&
+ git add empty1 &&
+ git commit -m base &&
+ echo content >empty1 &&
+ git add empty1 &&
+ git commit -m fill &&
+ git checkout -b empty-topic HEAD^ &&
+ git mv empty1 empty2 &&
+ git commit -m rename &&
+ test_must_fail git merge empty-base &&
+ >expect &&
+ test_cmp expect empty2
'
test_done
diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh
index d486d73..20aee43 100755
--- a/t/t6023-merge-file.sh
+++ b/t/t6023-merge-file.sh
@@ -64,17 +64,43 @@ cp new1.txt test.txt
test_expect_success "merge without conflict" \
"git merge-file test.txt orig.txt new2.txt"
+test_expect_success 'works in subdirectory' '
+ mkdir dir &&
+ cp new1.txt dir/a.txt &&
+ cp orig.txt dir/o.txt &&
+ cp new2.txt dir/b.txt &&
+ ( cd dir && git merge-file a.txt o.txt b.txt ) &&
+ test_path_is_missing a.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_failure "merge without conflict (missing LF at EOF)" \
+ "git merge-file test2.txt orig.txt new4.txt"
-test_expect_success "merge result added missing LF" \
+test_expect_failure "merge result added missing LF" \
"test_cmp test.txt test2.txt"
+cp new4.txt test3.txt
+test_expect_success "merge without conflict (missing LF at EOF, away from change in the other file)" \
+ "git merge-file --quiet test3.txt new2.txt new3.txt"
+
+cat > expect.txt << EOF
+DOMINUS regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+EOF
+printf "propter nomen suum." >> expect.txt
+
+test_expect_success "merge does not add LF away of change" \
+ "test_cmp test3.txt expect.txt"
+
cp test.txt backup.txt
test_expect_success "merge with conflicts" \
"test_must_fail git merge-file test.txt orig.txt new3.txt"
@@ -99,6 +125,55 @@ EOF
test_expect_success "expected conflict markers" "test_cmp test.txt expect.txt"
cp backup.txt test.txt
+
+cat > expect.txt << EOF
+Dominus regit me, et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+test_expect_success "merge conflicting with --ours" \
+ "git merge-file --ours test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+cp backup.txt test.txt
+
+cat > expect.txt << EOF
+DOMINUS regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+test_expect_success "merge conflicting with --theirs" \
+ "git merge-file --theirs test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+cp backup.txt test.txt
+
+cat > expect.txt << EOF
+Dominus regit me, et nihil mihi deerit.
+DOMINUS regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+test_expect_success "merge conflicting with --union" \
+ "git merge-file --union test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+cp backup.txt test.txt
+
test_expect_success "merge with conflicts, using -L" \
"test_must_fail git merge-file -L 1 -L 2 test.txt orig.txt new3.txt"
@@ -146,7 +221,7 @@ 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 &&
+ orig.txt "$TEST_DIRECTORY"/test-binary-1.png new1.txt 2> merge.err &&
grep "Cannot merge binary files" merge.err
'
@@ -252,4 +327,36 @@ test_expect_success 'marker size' '
test_cmp expect actual
'
+printf "line1\nline2\nline3" >nolf-orig.txt
+printf "line1\nline2\nline3x" >nolf-diff1.txt
+printf "line1\nline2\nline3y" >nolf-diff2.txt
+
+test_expect_success 'conflict at EOF without LF resolved by --ours' \
+ 'git merge-file -p --ours nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+ printf "line1\nline2\nline3x" >expect.txt &&
+ test_cmp expect.txt output.txt'
+
+test_expect_success 'conflict at EOF without LF resolved by --theirs' \
+ 'git merge-file -p --theirs nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+ printf "line1\nline2\nline3y" >expect.txt &&
+ test_cmp expect.txt output.txt'
+
+test_expect_success 'conflict at EOF without LF resolved by --union' \
+ 'git merge-file -p --union nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+ printf "line1\nline2\nline3x\nline3y" >expect.txt &&
+ test_cmp expect.txt output.txt'
+
+test_expect_success 'conflict sections match existing line endings' '
+ printf "1\\r\\n2\\r\\n3" >crlf-orig.txt &&
+ printf "1\\r\\n2\\r\\n4" >crlf-diff1.txt &&
+ printf "1\\r\\n2\\r\\n5" >crlf-diff2.txt &&
+ test_must_fail git -c core.eol=crlf merge-file -p \
+ crlf-diff1.txt crlf-orig.txt crlf-diff2.txt >crlf.txt &&
+ test $(tr "\015" Q <crlf.txt | grep "^[<=>].*Q$" | wc -l) = 3 &&
+ test $(tr "\015" Q <crlf.txt | grep "[345]Q$" | wc -l) = 3 &&
+ test_must_fail git -c core.eol=crlf merge-file -p \
+ nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >nolf.txt &&
+ test $(tr "\015" Q <nolf.txt | grep "^[<=>].*Q$" | wc -l) = 0
+'
+
test_done
diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh
index b3fbf65..3f59e58 100755
--- a/t/t6024-recursive-merge.sh
+++ b/t/t6024-recursive-merge.sh
@@ -76,7 +76,7 @@ test_expect_success "result contains a conflict" "test_cmp expect a1"
git ls-files --stage > out
cat > expect << EOF
-100644 439cc46de773d8a83c77799b7cc9191c128bfcff 1 a1
+100644 ec3fe2a791706733f2d8fa7ad45d9a9672031f5e 1 a1
100644 cf84443e49e1b366fac938711ddf4be2d4d1d9e9 2 a1
100644 fd7923529855d0b274795ae3349c5e0438333979 3 a1
EOF
@@ -104,7 +104,7 @@ test_expect_success 'mark rename/delete as unmerged' '
test_tick &&
git commit -m delete &&
git checkout -b rename HEAD^ &&
- git mv a1 a2
+ git mv a1 a2 &&
test_tick &&
git commit -m rename &&
test_must_fail git merge delete &&
diff --git a/t/t6026-merge-attr.sh b/t/t6026-merge-attr.sh
index 5e43997..ef0cbce 100755
--- a/t/t6026-merge-attr.sh
+++ b/t/t6026-merge-attr.sh
@@ -11,7 +11,7 @@ test_expect_success setup '
for f in text binary union
do
- echo Initial >$f && git add $f || break
+ echo Initial >$f && git add $f || return 1
done &&
test_tick &&
git commit -m Initial &&
@@ -19,7 +19,7 @@ test_expect_success setup '
git branch side &&
for f in text binary union
do
- echo Master >>$f && git add $f || break
+ echo Master >>$f && git add $f || return 1
done &&
test_tick &&
git commit -m Master &&
@@ -27,7 +27,7 @@ test_expect_success setup '
git checkout side &&
for f in text binary union
do
- echo Side >>$f && git add $f || break
+ echo Side >>$f && git add $f || return 1
done &&
test_tick &&
git commit -m Side &&
@@ -85,11 +85,12 @@ test_expect_success 'retry the merge with longer context' '
cat >./custom-merge <<\EOF
#!/bin/sh
-orig="$1" ours="$2" theirs="$3" exit="$4"
+orig="$1" ours="$2" theirs="$3" exit="$4" path=$5
(
echo "orig is $orig"
echo "ours is $ours"
echo "theirs is $theirs"
+ echo "path is $path"
echo "=== orig ==="
cat "$orig"
echo "=== ours ==="
@@ -110,7 +111,7 @@ test_expect_success 'custom merge backend' '
git reset --hard anchor &&
git config --replace-all \
- merge.custom.driver "./custom-merge %O %A %B 0" &&
+ merge.custom.driver "./custom-merge %O %A %B 0 %P" &&
git config --replace-all \
merge.custom.name "custom merge driver for testing" &&
@@ -121,7 +122,7 @@ test_expect_success 'custom merge backend' '
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" &&
+ sh -c "./custom-merge $o $a $b 0 'text'" &&
sed -e 1,3d $a >check-2 &&
cmp check-1 check-2 &&
rm -f $o $a $b
@@ -131,7 +132,7 @@ test_expect_success 'custom merge backend' '
git reset --hard anchor &&
git config --replace-all \
- merge.custom.driver "./custom-merge %O %A %B 1" &&
+ merge.custom.driver "./custom-merge %O %A %B 1 %P" &&
git config --replace-all \
merge.custom.name "custom merge driver for testing" &&
@@ -148,9 +149,12 @@ test_expect_success 'custom merge backend' '
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" &&
+ sh -c "./custom-merge $o $a $b 0 'text'" &&
sed -e 1,3d $a >check-2 &&
cmp check-1 check-2 &&
+ sed -e 1,3d -e 4q $a >check-3 &&
+ echo "path is text" >expect &&
+ cmp expect check-3 &&
rm -f $o $a $b
'
@@ -172,7 +176,8 @@ test_expect_success 'up-to-date merge without common ancestor' '
test_tick &&
(
cd repo1 &&
- git pull ../repo2 master
+ git fetch ../repo2 master &&
+ git merge --allow-unrelated-histories FETCH_HEAD
)
'
diff --git a/t/t6027-merge-binary.sh b/t/t6027-merge-binary.sh
index b519626..0773541 100755
--- a/t/t6027-merge-binary.sh
+++ b/t/t6027-merge-binary.sh
@@ -6,7 +6,7 @@ test_description='ask merge-recursive to merge binary files'
test_expect_success setup '
- cat "$TEST_DIRECTORY"/test4012.png >m &&
+ cat "$TEST_DIRECTORY"/test-binary-1.png >m &&
git add m &&
git ls-files -s | sed -e "s/ 0 / 1 /" >E1 &&
test_tick &&
diff --git a/t/t6028-merge-up-to-date.sh b/t/t6028-merge-up-to-date.sh
index a91644e..7763c1b 100755
--- a/t/t6028-merge-up-to-date.sh
+++ b/t/t6028-merge-up-to-date.sh
@@ -16,7 +16,12 @@ test_expect_success setup '
test_tick &&
git commit -m second &&
git tag c1 &&
- git branch test
+ git branch test &&
+ echo third >file &&
+ git add file &&
+ test_tick &&
+ git commit -m third &&
+ git tag c2
'
test_expect_success 'merge -s recursive up-to-date' '
@@ -74,4 +79,14 @@ test_expect_success 'merge -s subtree up-to-date' '
'
+test_expect_success 'merge fast-forward octopus' '
+
+ git reset --hard c0 &&
+ test_tick &&
+ git merge c1 c2 &&
+ expect=$(git rev-parse c2) &&
+ current=$(git rev-parse HEAD) &&
+ test "$expect" = "$current"
+'
+
test_done
diff --git a/t/t6029-merge-subtree.sh b/t/t6029-merge-subtree.sh
index 3900d9f..3e69245 100755
--- a/t/t6029-merge-subtree.sh
+++ b/t/t6029-merge-subtree.sh
@@ -6,7 +6,7 @@ test_description='subtree merge strategy'
test_expect_success setup '
- s="1 2 3 4 5 6 7 8"
+ 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 &&
@@ -49,7 +49,7 @@ test_expect_success 'setup' '
test_expect_success 'initial merge' '
git remote add -f gui ../git-gui &&
- git merge -s ours --no-commit gui/master &&
+ git merge -s ours --no-commit --allow-unrelated-histories gui/master &&
git read-tree --prefix=git-gui/ -u gui/master &&
git commit -m "Merge git-gui as our subdirectory" &&
git checkout -b work &&
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 3b042aa..e74662b 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -52,15 +52,8 @@ test_expect_success 'bisect starts with only one bad' '
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
+ git bisect good $HASH1 &&
+ test_must_fail git bisect next
'
test_expect_success 'bisect start with one bad and good' '
@@ -126,6 +119,18 @@ test_expect_success 'bisect reset removes packed refs' '
test -z "$(git for-each-ref "refs/heads/bisect")"
'
+test_expect_success 'bisect reset removes bisect state after --no-checkout' '
+ git bisect reset &&
+ git bisect start --no-checkout &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH3 &&
+ git bisect next &&
+ git bisect reset &&
+ test -z "$(git for-each-ref "refs/bisect/*")" &&
+ test -z "$(git for-each-ref "refs/heads/bisect")" &&
+ test -z "$(git for-each-ref "BISECT_HEAD")"
+'
+
test_expect_success 'bisect start: back in good branch' '
git branch > branch.output &&
grep "* other" branch.output > /dev/null &&
@@ -138,15 +143,23 @@ test_expect_success 'bisect start: back in good branch' '
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_expect_success 'bisect start: no ".git/BISECT_START" created if junk rev' '
+ git bisect reset &&
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: existing ".git/BISECT_START" not modified if junk rev' '
+ git bisect start $HASH4 $HASH1 -- &&
+ git bisect good &&
+ cp .git/BISECT_START saved &&
+ test_must_fail git bisect start $HASH4 foo -- &&
+ git branch > branch.output &&
+ test_i18ngrep "* (no branch, bisect started on other)" branch.output > /dev/null &&
+ test_cmp saved .git/BISECT_START
+'
test_expect_success 'bisect start: no ".git/BISECT_START" if mistaken rev' '
git bisect start $HASH4 $HASH1 -- &&
git bisect good &&
@@ -170,35 +183,28 @@ test_expect_success 'bisect start: no ".git/BISECT_START" if checkout error' '
# $HASH1 is good, $HASH4 is bad, we skip $HASH3
# but $HASH2 is bad,
# so we should find $HASH2 as the first bad commit
-test_expect_success 'bisect skip: successfull result' '
+test_expect_success 'bisect skip: successful result' '
+ test_when_finished git bisect reset &&
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
+ grep "$HASH2 is the first bad commit" my_bisect_log.txt
'
# $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' '
+ test_when_finished git bisect reset &&
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
+ git bisect skip &&
+ test_expect_code 2 git bisect skip >my_bisect_log.txt &&
+ 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
'
# $HASH1 is good, $HASH4 is bad, we skip $HASH3
@@ -206,22 +212,15 @@ test_expect_success 'bisect skip: cannot tell between 3 commits' '
# 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' '
+ test_when_finished git bisect reset &&
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
+ git bisect skip &&
+ test_expect_code 2 git bisect good >my_bisect_log.txt &&
+ 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
'
# $HASH1 is good, $HASH4 is both skipped and bad, we skip $HASH3
@@ -229,24 +228,18 @@ test_expect_success 'bisect skip: cannot tell between 2 commits' '
# 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' '
+ test_when_finished git bisect reset &&
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
+ test_expect_code 2 git bisect good >my_bisect_log.txt &&
+ 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
'
# We want to automatically find the commit that
@@ -369,7 +362,7 @@ test_expect_success 'bisect starting with a detached 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 &&
+ grep "mistook good and bad" rev_list_error &&
git bisect reset
'
@@ -460,7 +453,7 @@ test_expect_success 'many merge bases creation' '
git merge -m "merge HASH7 and SIDE_HASH7" "$HASH7" &&
B_HASH=$(git rev-parse --verify HEAD) &&
git merge-base --all "$A_HASH" "$B_HASH" > merge_bases.txt &&
- test $(wc -l < merge_bases.txt) = "2" &&
+ test_line_count = 2 merge_bases.txt &&
grep "$HASH5" merge_bases.txt &&
grep "$SIDE_HASH5" merge_bases.txt
'
@@ -517,13 +510,13 @@ test_expect_success '"parallel" side branch creation' '
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)
+ PARA_HASH3=$(git rev-parse --verify HEAD) &&
git merge -m "merge HASH4 and PARA_HASH3" "$HASH4" &&
- PARA_HASH4=$(git rev-parse --verify HEAD)
+ 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)
+ 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)
+ PARA_HASH6=$(git rev-parse --verify HEAD) &&
git merge -m "merge HASH7 and PARA_HASH6" "$HASH7" &&
PARA_HASH7=$(git rev-parse --verify HEAD)
'
@@ -572,6 +565,333 @@ test_expect_success 'erroring out when using bad path parameters' '
grep "bad path parameters" error.txt
'
+test_expect_success 'test bisection on bare repo - --no-checkout specified' '
+ git clone --bare . bare.nocheckout &&
+ (
+ cd bare.nocheckout &&
+ git bisect start --no-checkout &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect run eval \
+ "test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \
+ >../nocheckout.log
+ ) &&
+ grep "$HASH3 is the first bad commit" nocheckout.log
+'
+
+
+test_expect_success 'test bisection on bare repo - --no-checkout defaulted' '
+ git clone --bare . bare.defaulted &&
+ (
+ cd bare.defaulted &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect run eval \
+ "test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \
+ >../defaulted.log
+ ) &&
+ grep "$HASH3 is the first bad commit" defaulted.log
+'
+
#
+# This creates a broken branch which cannot be checked out because
+# the tree created has been deleted.
#
+# H1-H2-H3-H4-H5-H6-H7 <--other
+# \
+# S5-S6'-S7'-S8'-S9 <--broken
+#
+# Commits marked with ' have a missing tree.
+#
+test_expect_success 'broken branch creation' '
+ git bisect reset &&
+ git checkout -b broken $HASH4 &&
+ git tag BROKEN_HASH4 $HASH4 &&
+ add_line_into_file "5(broken): first line on a broken branch" hello2 &&
+ git tag BROKEN_HASH5 &&
+ mkdir missing &&
+ :> missing/MISSING &&
+ git add missing/MISSING &&
+ git commit -m "6(broken): Added file that will be deleted" &&
+ git tag BROKEN_HASH6 &&
+ add_line_into_file "7(broken): second line on a broken branch" hello2 &&
+ git tag BROKEN_HASH7 &&
+ add_line_into_file "8(broken): third line on a broken branch" hello2 &&
+ git tag BROKEN_HASH8 &&
+ git rm missing/MISSING &&
+ git commit -m "9(broken): Remove missing file" &&
+ git tag BROKEN_HASH9 &&
+ rm .git/objects/39/f7e61a724187ab767d2e08442d9b6b9dab587d
+'
+
+echo "" > expected.ok
+cat > expected.missing-tree.default <<EOF
+fatal: unable to read tree 39f7e61a724187ab767d2e08442d9b6b9dab587d
+EOF
+
+test_expect_success 'bisect fails if tree is broken on start commit' '
+ git bisect reset &&
+ test_must_fail git bisect start BROKEN_HASH7 BROKEN_HASH4 2>error.txt &&
+ test_cmp expected.missing-tree.default error.txt
+'
+
+test_expect_success 'bisect fails if tree is broken on trial commit' '
+ git bisect reset &&
+ test_must_fail git bisect start BROKEN_HASH9 BROKEN_HASH4 2>error.txt &&
+ git reset --hard broken &&
+ git checkout broken &&
+ test_cmp expected.missing-tree.default error.txt
+'
+
+check_same()
+{
+ echo "Checking $1 is the same as $2" &&
+ test_cmp_rev "$1" "$2"
+}
+
+test_expect_success 'bisect: --no-checkout - start commit bad' '
+ git bisect reset &&
+ git bisect start BROKEN_HASH7 BROKEN_HASH4 --no-checkout &&
+ check_same BROKEN_HASH6 BISECT_HEAD &&
+ git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - trial commit bad' '
+ git bisect reset &&
+ git bisect start broken BROKEN_HASH4 --no-checkout &&
+ check_same BROKEN_HASH6 BISECT_HEAD &&
+ git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - target before breakage' '
+ git bisect reset &&
+ git bisect start broken BROKEN_HASH4 --no-checkout &&
+ check_same BROKEN_HASH6 BISECT_HEAD &&
+ git bisect bad BISECT_HEAD &&
+ check_same BROKEN_HASH5 BISECT_HEAD &&
+ git bisect bad BISECT_HEAD &&
+ check_same BROKEN_HASH5 bisect/bad &&
+ git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - target in breakage' '
+ git bisect reset &&
+ git bisect start broken BROKEN_HASH4 --no-checkout &&
+ check_same BROKEN_HASH6 BISECT_HEAD &&
+ git bisect bad BISECT_HEAD &&
+ check_same BROKEN_HASH5 BISECT_HEAD &&
+ git bisect good BISECT_HEAD &&
+ check_same BROKEN_HASH6 bisect/bad &&
+ git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - target after breakage' '
+ git bisect reset &&
+ git bisect start broken BROKEN_HASH4 --no-checkout &&
+ check_same BROKEN_HASH6 BISECT_HEAD &&
+ git bisect good BISECT_HEAD &&
+ check_same BROKEN_HASH8 BISECT_HEAD &&
+ git bisect good BISECT_HEAD &&
+ check_same BROKEN_HASH9 bisect/bad &&
+ git bisect reset
+'
+
+test_expect_success 'bisect: demonstrate identification of damage boundary' "
+ git bisect reset &&
+ git checkout broken &&
+ git bisect start broken master --no-checkout &&
+ git bisect run \"\$SHELL_PATH\" -c '
+ GOOD=\$(git for-each-ref \"--format=%(objectname)\" refs/bisect/good-*) &&
+ git rev-list --objects BISECT_HEAD --not \$GOOD >tmp.\$\$ &&
+ git pack-objects --stdout >/dev/null < tmp.\$\$
+ rc=\$?
+ rm -f tmp.\$\$
+ test \$rc = 0' &&
+ check_same BROKEN_HASH6 bisect/bad &&
+ git bisect reset
+"
+
+cat > expected.bisect-log <<EOF
+# bad: [32a594a3fdac2d57cf6d02987e30eec68511498c] Add <4: Ciao for now> into <hello>.
+# good: [7b7f204a749c3125d5224ed61ea2ae1187ad046f] Add <2: A new day for git> into <hello>.
+git bisect start '32a594a3fdac2d57cf6d02987e30eec68511498c' '7b7f204a749c3125d5224ed61ea2ae1187ad046f'
+# good: [3de952f2416b6084f557ec417709eac740c6818c] Add <3: Another new day for git> into <hello>.
+git bisect good 3de952f2416b6084f557ec417709eac740c6818c
+# first bad commit: [32a594a3fdac2d57cf6d02987e30eec68511498c] Add <4: Ciao for now> into <hello>.
+EOF
+
+test_expect_success 'bisect log: successfull result' '
+ git bisect reset &&
+ git bisect start $HASH4 $HASH2 &&
+ git bisect good &&
+ git bisect log >bisect-log.txt &&
+ test_cmp expected.bisect-log bisect-log.txt &&
+ git bisect reset
+'
+
+cat > expected.bisect-skip-log <<EOF
+# bad: [32a594a3fdac2d57cf6d02987e30eec68511498c] Add <4: Ciao for now> into <hello>.
+# good: [7b7f204a749c3125d5224ed61ea2ae1187ad046f] Add <2: A new day for git> into <hello>.
+git bisect start '32a594a3fdac2d57cf6d02987e30eec68511498c' '7b7f204a749c3125d5224ed61ea2ae1187ad046f'
+# skip: [3de952f2416b6084f557ec417709eac740c6818c] Add <3: Another new day for git> into <hello>.
+git bisect skip 3de952f2416b6084f557ec417709eac740c6818c
+# only skipped commits left to test
+# possible first bad commit: [32a594a3fdac2d57cf6d02987e30eec68511498c] Add <4: Ciao for now> into <hello>.
+# possible first bad commit: [3de952f2416b6084f557ec417709eac740c6818c] Add <3: Another new day for git> into <hello>.
+EOF
+
+test_expect_success 'bisect log: only skip commits left' '
+ git bisect reset &&
+ git bisect start $HASH4 $HASH2 &&
+ test_must_fail git bisect skip &&
+ git bisect log >bisect-skip-log.txt &&
+ test_cmp expected.bisect-skip-log bisect-skip-log.txt &&
+ git bisect reset
+'
+
+test_expect_success '"git bisect bad HEAD" behaves as "git bisect bad"' '
+ git checkout parallel &&
+ git bisect start HEAD $HASH1 &&
+ git bisect good HEAD &&
+ git bisect bad HEAD &&
+ test "$HASH6" = $(git rev-parse --verify HEAD) &&
+ git bisect reset
+'
+
+test_expect_success 'bisect starts with only one new' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect new $HASH4 &&
+ git bisect next
+'
+
+test_expect_success 'bisect does not start with only one old' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect old $HASH1 &&
+ test_must_fail git bisect next
+'
+
+test_expect_success 'bisect start with one new and old' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect old $HASH1 &&
+ git bisect new $HASH4 &&
+ git bisect new &&
+ git bisect new >bisect_result &&
+ grep "$HASH2 is the first new commit" bisect_result &&
+ git bisect log >log_to_replay.txt &&
+ git bisect reset
+'
+
+test_expect_success 'bisect replay with old and new' '
+ git bisect replay log_to_replay.txt >bisect_result &&
+ grep "$HASH2 is the first new commit" bisect_result &&
+ git bisect reset
+'
+
+test_expect_success 'bisect cannot mix old/new and good/bad' '
+ git bisect start &&
+ git bisect bad $HASH4 &&
+ test_must_fail git bisect old $HASH1
+'
+
+test_expect_success 'bisect terms needs 0 or 1 argument' '
+ git bisect reset &&
+ test_must_fail git bisect terms only-one &&
+ test_must_fail git bisect terms 1 2 &&
+ test_must_fail git bisect terms 2>actual &&
+ echo "no terms defined" >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'bisect terms shows good/bad after start' '
+ git bisect reset &&
+ git bisect start HEAD $HASH1 &&
+ git bisect terms --term-good >actual &&
+ echo good >expected &&
+ test_cmp expected actual &&
+ git bisect terms --term-bad >actual &&
+ echo bad >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'bisect start with one term1 and term2' '
+ git bisect reset &&
+ git bisect start --term-old term2 --term-new term1 &&
+ git bisect term2 $HASH1 &&
+ git bisect term1 $HASH4 &&
+ git bisect term1 &&
+ git bisect term1 >bisect_result &&
+ grep "$HASH2 is the first term1 commit" bisect_result &&
+ git bisect log >log_to_replay.txt &&
+ git bisect reset
+'
+
+test_expect_success 'bisect replay with term1 and term2' '
+ git bisect replay log_to_replay.txt >bisect_result &&
+ grep "$HASH2 is the first term1 commit" bisect_result &&
+ git bisect reset
+'
+
+test_expect_success 'bisect start term1 term2' '
+ git bisect reset &&
+ git bisect start --term-new term1 --term-old term2 $HASH4 $HASH1 &&
+ git bisect term1 &&
+ git bisect term1 >bisect_result &&
+ grep "$HASH2 is the first term1 commit" bisect_result &&
+ git bisect log >log_to_replay.txt &&
+ git bisect reset
+'
+
+test_expect_success 'bisect cannot mix terms' '
+ git bisect reset &&
+ git bisect start --term-good term1 --term-bad term2 $HASH4 $HASH1 &&
+ test_must_fail git bisect a &&
+ test_must_fail git bisect b &&
+ test_must_fail git bisect bad &&
+ test_must_fail git bisect good &&
+ test_must_fail git bisect new &&
+ test_must_fail git bisect old
+'
+
+test_expect_success 'bisect terms rejects invalid terms' '
+ git bisect reset &&
+ test_must_fail git bisect start --term-good invalid..term &&
+ test_must_fail git bisect terms --term-bad invalid..term &&
+ test_must_fail git bisect terms --term-good bad &&
+ test_must_fail git bisect terms --term-good old &&
+ test_must_fail git bisect terms --term-good skip &&
+ test_must_fail git bisect terms --term-good reset &&
+ test_path_is_missing .git/BISECT_TERMS
+'
+
+test_expect_success 'bisect start --term-* does store terms' '
+ git bisect reset &&
+ git bisect start --term-bad=one --term-good=two &&
+ git bisect terms >actual &&
+ cat <<-EOF >expected &&
+ Your current terms are two for the old state
+ and one for the new state.
+ EOF
+ test_cmp expected actual &&
+ git bisect terms --term-bad >actual &&
+ echo one >expected &&
+ test_cmp expected actual &&
+ git bisect terms --term-good >actual &&
+ echo two >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'bisect start takes options and revs in any order' '
+ git bisect reset &&
+ git bisect start --term-good one $HASH4 \
+ --term-good two --term-bad bad-term \
+ $HASH1 --term-good three -- &&
+ (git bisect terms --term-bad && git bisect terms --term-good) >actual &&
+ printf "%s\n%s\n" bad-term three >expected &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t6031-merge-filemode.sh b/t/t6031-merge-filemode.sh
new file mode 100755
index 0000000..7d06461
--- /dev/null
+++ b/t/t6031-merge-filemode.sh
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+test_description='merge: handle file mode'
+. ./test-lib.sh
+
+test_expect_success 'set up mode change in one branch' '
+ : >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 add file1 &&
+ git commit -m b1
+'
+
+do_one_mode () {
+ strategy=$1
+ us=$2
+ them=$3
+ test_expect_success "resolve single mode change ($strategy, $us)" '
+ git checkout -f $us &&
+ git merge -s $strategy $them &&
+ git ls-files -s file1 | grep ^100755
+ '
+
+ test_expect_success FILEMODE "verify executable bit on file ($strategy, $us)" '
+ test -x file1
+ '
+}
+
+do_one_mode recursive a1 b1
+do_one_mode recursive b1 a1
+do_one_mode resolve a1 b1
+do_one_mode resolve b1 a1
+
+test_expect_success 'set up mode change in both branches' '
+ 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 &&
+ {
+ echo "100755 $H 2 file2"
+ echo "100644 $H 3 file2"
+ } >expect
+'
+
+do_both_modes () {
+ strategy=$1
+ test_expect_success "detect conflict on double mode change ($strategy)" '
+ git reset --hard &&
+ git checkout -f a2 &&
+ test_must_fail git merge -s $strategy b2 &&
+ git ls-files -u >actual &&
+ test_cmp actual expect &&
+ git ls-files -s file2 | grep ^100755
+ '
+
+ test_expect_success FILEMODE "verify executable bit on file ($strategy)" '
+ test -x file2
+ '
+}
+
+# both sides are equivalent, so no need to run both ways
+do_both_modes recursive
+do_both_modes resolve
+
+test_expect_success 'set up delete/modechange scenario' '
+ git reset --hard &&
+ git checkout -b deletion master &&
+ git rm file1 &&
+ git commit -m deletion
+'
+
+do_delete_modechange () {
+ strategy=$1
+ us=$2
+ them=$3
+ test_expect_success "detect delete/modechange conflict ($strategy, $us)" '
+ git reset --hard &&
+ git checkout $us &&
+ test_must_fail git merge -s $strategy $them
+ '
+}
+
+do_delete_modechange recursive b1 deletion
+do_delete_modechange recursive deletion b1
+do_delete_modechange resolve b1 deletion
+do_delete_modechange resolve deletion b1
+
+test_done
diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh
deleted file mode 100755
index 8a3304f..0000000
--- a/t/t6031-merge-recursive.sh
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/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
index eac5eba..8077738 100755
--- a/t/t6032-merge-large-rename.sh
+++ b/t/t6032-merge-large-rename.sh
@@ -20,7 +20,7 @@ test_expect_success 'setup (initial)' '
make_text() {
echo $1: $2
- for i in `count 20`; do
+ for i in $(count 20); do
echo $1: $i
done
echo $1: $3
@@ -28,10 +28,10 @@ make_text() {
test_rename() {
test_expect_success "rename ($1, $2)" '
- n='$1'
- expect='$2'
+ n='$1' &&
+ expect='$2' &&
git checkout -f master &&
- git branch -D test$n || true &&
+ test_might_fail git branch -D test$n &&
git reset --hard initial &&
for i in $(count $n); do
make_text $i initial initial >$i
@@ -70,4 +70,34 @@ test_expect_success 'set merge.renamelimit to 5' '
test_rename 5 ok
test_rename 6 fail
+test_expect_success 'setup large simple rename' '
+ git config --unset merge.renamelimit &&
+ git config --unset diff.renamelimit &&
+
+ git reset --hard initial &&
+ for i in $(count 200); do
+ make_text foo bar baz >$i
+ done &&
+ git add . &&
+ git commit -m create-files &&
+
+ git branch simple-change &&
+ git checkout -b simple-rename &&
+
+ mkdir builtin &&
+ git mv [0-9]* builtin/ &&
+ git commit -m renamed &&
+
+ git checkout simple-change &&
+ >unrelated-change &&
+ git add unrelated-change &&
+ git commit -m unrelated-change
+'
+
+test_expect_success 'massive simple rename does not spam added files' '
+ sane_unset GIT_MERGE_VERBOSITY &&
+ git merge --no-stat simple-rename | grep -v Removing >output &&
+ test_line_count -lt 5 output
+'
+
test_done
diff --git a/t/t6034-merge-rename-nocruft.sh b/t/t6034-merge-rename-nocruft.sh
index 65be95f..89871aa 100755
--- a/t/t6034-merge-rename-nocruft.sh
+++ b/t/t6034-merge-rename-nocruft.sh
@@ -73,33 +73,12 @@ 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
+ git write-tree &&
+ test_path_is_file B &&
+ test_path_is_file N &&
+ test_path_is_file R &&
+ test_path_is_missing A &&
+ test_path_is_missing M
'
# This test broke in 8371234ecaaf6e14fe3f2082a855eff1bbd79ae9
@@ -108,32 +87,13 @@ 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
+ git write-tree &&
+ test_path_is_file A &&
+ echo dirty >expect &&
+ test_cmp expect A &&
+ test_path_is_file B &&
+ test_path_is_file N &&
+ test_path_is_missing M
'
test_done
diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh
index cd3190c..9324ea4 100755
--- a/t/t6035-merge-dir-to-symlink.sh
+++ b/t/t6035-merge-dir-to-symlink.sh
@@ -3,12 +3,6 @@
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 &&
@@ -18,18 +12,26 @@ test_expect_success 'create a commit where dir a/b changed to symlink' '
git commit -m base &&
git tag start &&
rm -rf a/b &&
- ln -s b-2 a/b &&
git add -A &&
+ test_ln_s_add b-2 a/b &&
git commit -m "dir to symlink"
'
-test_expect_success 'keep a/b-2/c/d across checkout' '
+test_expect_success 'checkout does not clobber untracked symlink' '
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_must_fail git checkout start^0
+'
+
+test_expect_success 'a/b-2/c/d is kept when clobbering symlink b' '
+ git checkout HEAD^0 &&
+ git reset --hard master &&
+ git rm --cached a/b &&
+ git commit -m "untracked symlink remains" &&
+ git checkout -f start^0 &&
+ test -f a/b-2/c/d
'
test_expect_success 'checkout should not have deleted a/b-2/c/d' '
@@ -48,46 +50,123 @@ test_expect_success 'setup for merge test' '
git tag baseline
'
-test_expect_success 'do not lose a/b-2/c/d in merge (resolve)' '
+test_expect_success 'Handle D/F conflict, do not lose a/b-2/c/d in merge (resolve)' '
git reset --hard &&
git checkout baseline^0 &&
git merge -s resolve master &&
- test -h a/b &&
test -f a/b-2/c/d
'
-test_expect_failure 'do not lose a/b-2/c/d in merge (recursive)' '
+test_expect_success SYMLINKS 'a/b was resolved as symlink' '
+ test -h a/b
+'
+
+test_expect_success 'Handle D/F conflict, do not lose a/b-2/c/d in merge (recursive)' '
git reset --hard &&
git checkout baseline^0 &&
git merge -s recursive master &&
- test -h a/b &&
test -f a/b-2/c/d
'
+test_expect_success SYMLINKS 'a/b was resolved as symlink' '
+ test -h a/b
+'
+
+test_expect_success 'Handle F/D conflict, do not lose a/b-2/c/d in merge (resolve)' '
+ git reset --hard &&
+ git checkout master^0 &&
+ git merge -s resolve baseline^0 &&
+ test -f a/b-2/c/d
+'
+
+test_expect_success SYMLINKS 'a/b was resolved as symlink' '
+ test -h a/b
+'
+
+test_expect_success 'Handle F/D conflict, do not lose a/b-2/c/d in merge (recursive)' '
+ git reset --hard &&
+ git checkout master^0 &&
+ git merge -s recursive baseline^0 &&
+ test -f a/b-2/c/d
+'
+
+test_expect_success SYMLINKS 'a/b was resolved as symlink' '
+ test -h a/b
+'
+
+test_expect_failure 'do not lose untracked in merge (resolve)' '
+ git reset --hard &&
+ git checkout baseline^0 &&
+ >a/b/c/e &&
+ test_must_fail git merge -s resolve master &&
+ test -f a/b/c/e &&
+ test -f a/b-2/c/d
+'
+
+test_expect_success 'do not lose untracked in merge (recursive)' '
+ git reset --hard &&
+ git checkout baseline^0 &&
+ >a/b/c/e &&
+ test_must_fail git merge -s recursive master &&
+ test -f a/b/c/e &&
+ test -f a/b-2/c/d
+'
+
+test_expect_success 'do not lose modifications in merge (resolve)' '
+ git reset --hard &&
+ git checkout baseline^0 &&
+ echo more content >>a/b/c/d &&
+ test_must_fail git merge -s resolve master
+'
+
+test_expect_success 'do not lose modifications in merge (recursive)' '
+ git reset --hard &&
+ git checkout baseline^0 &&
+ echo more content >>a/b/c/d &&
+ test_must_fail git merge -s recursive master
+'
+
test_expect_success 'setup a merge where dir a/b-2 changed to symlink' '
git reset --hard &&
git checkout start^0 &&
rm -rf a/b-2 &&
- ln -s b a/b-2 &&
git add -A &&
+ test_ln_s_add b a/b-2 &&
git commit -m "dir a/b-2 to symlink" &&
git tag test2
'
-test_expect_success 'merge should not have conflicts (resolve)' '
+test_expect_success 'merge should not have D/F conflicts (resolve)' '
git reset --hard &&
git checkout baseline^0 &&
git merge -s resolve test2 &&
- test -h a/b-2 &&
test -f a/b/c/d
'
-test_expect_failure 'merge should not have conflicts (recursive)' '
+test_expect_success SYMLINKS 'a/b-2 was resolved as symlink' '
+ test -h a/b-2
+'
+
+test_expect_success 'merge should not have D/F conflicts (recursive)' '
git reset --hard &&
git checkout baseline^0 &&
git merge -s recursive test2 &&
- test -h a/b-2 &&
test -f a/b/c/d
'
+test_expect_success SYMLINKS 'a/b-2 was resolved as symlink' '
+ test -h a/b-2
+'
+
+test_expect_success 'merge should not have F/D conflicts (recursive)' '
+ git reset --hard &&
+ git checkout -b foo test2 &&
+ git merge -s recursive baseline^0 &&
+ test -f a/b/c/d
+'
+
+test_expect_success SYMLINKS 'a/b-2 was resolved as symlink' '
+ test -h a/b-2
+'
+
test_done
diff --git a/t/t6036-recursive-corner-cases.sh b/t/t6036-recursive-corner-cases.sh
index b874141..18aa88b 100755
--- a/t/t6036-recursive-corner-cases.sh
+++ b/t/t6036-recursive-corner-cases.sh
@@ -1,9 +1,15 @@
#!/bin/sh
-test_description='recursive merge corner cases'
+test_description='recursive merge corner cases involving criss-cross merges'
. ./test-lib.sh
+get_clean_checkout () {
+ git reset --hard &&
+ git clean -fdqx &&
+ git checkout "$1"
+}
+
#
# L1 L2
# o---o
@@ -14,8 +20,8 @@ test_description='recursive merge corner cases'
# R1 R2
#
-test_expect_success setup '
- ten="0 1 2 3 4 5 6 7 8 9"
+test_expect_success 'setup basic criss-cross + rename with no modifications' '
+ ten="0 1 2 3 4 5 6 7 8 9" &&
for i in $ten
do
echo line $i in a sample file
@@ -45,11 +51,639 @@ test_expect_success setup '
git tag R2
'
-test_expect_success merge '
+test_expect_success 'merge simple rename+criss-cross with no modifications' '
git reset --hard &&
git checkout L2^0 &&
- test_must_fail git merge -s recursive R2^0
+ test_must_fail git merge -s recursive R2^0 &&
+
+ test 2 = $(git ls-files -s | wc -l) &&
+ test 2 = $(git ls-files -u | wc -l) &&
+ test 2 = $(git ls-files -o | wc -l) &&
+
+ test $(git rev-parse :2:three) = $(git rev-parse L2:three) &&
+ test $(git rev-parse :3:three) = $(git rev-parse R2:three) &&
+
+ test $(git rev-parse L2:three) = $(git hash-object three~HEAD) &&
+ test $(git rev-parse R2:three) = $(git hash-object three~R2^0)
+'
+
+#
+# Same as before, but modify L1 slightly:
+#
+# L1m L2
+# o---o
+# / \ / \
+# o X ?
+# \ / \ /
+# o---o
+# R1 R2
+#
+
+test_expect_success 'setup criss-cross + rename merges with basic modification' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ 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 &&
+ echo more >>two &&
+ git add two &&
+ 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 criss-cross + rename merges with basic modification' '
+ git reset --hard &&
+ git checkout L2^0 &&
+
+ test_must_fail git merge -s recursive R2^0 &&
+
+ test 2 = $(git ls-files -s | wc -l) &&
+ test 2 = $(git ls-files -u | wc -l) &&
+ test 2 = $(git ls-files -o | wc -l) &&
+
+ test $(git rev-parse :2:three) = $(git rev-parse L2:three) &&
+ test $(git rev-parse :3:three) = $(git rev-parse R2:three) &&
+
+ test $(git rev-parse L2:three) = $(git hash-object three~HEAD) &&
+ test $(git rev-parse R2:three) = $(git hash-object three~R2^0)
+'
+
+#
+# For the next test, we start with three commits in two lines of development
+# which setup a rename/add conflict:
+# Commit A: File 'a' exists
+# Commit B: Rename 'a' -> 'new_a'
+# Commit C: Modify 'a', create different 'new_a'
+# Later, two different people merge and resolve differently:
+# Commit D: Merge B & C, ignoring separately created 'new_a'
+# Commit E: Merge B & C making use of some piece of secondary 'new_a'
+# Finally, someone goes to merge D & E. Does git detect the conflict?
+#
+# B D
+# o---o
+# / \ / \
+# A o X ? F
+# \ / \ /
+# o---o
+# C E
+#
+
+test_expect_success 'setup differently handled merges of rename/add conflict' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ printf "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n" >a &&
+ git add a &&
+ test_tick && git commit -m A &&
+
+ git branch B &&
+ git checkout -b C &&
+ echo 10 >>a &&
+ echo "other content" >>new_a &&
+ git add a new_a &&
+ test_tick && git commit -m C &&
+
+ git checkout B &&
+ git mv a new_a &&
+ test_tick && git commit -m B &&
+
+ git checkout B^0 &&
+ test_must_fail git merge C &&
+ git clean -f &&
+ test_tick && git commit -m D &&
+ git tag D &&
+
+ git checkout C^0 &&
+ test_must_fail git merge B &&
+ rm new_a~HEAD new_a &&
+ printf "Incorrectly merged content" >>new_a &&
+ git add -u &&
+ test_tick && git commit -m E &&
+ git tag E
+'
+
+test_expect_success 'git detects differently handled merges conflict' '
+ git reset --hard &&
+ git checkout D^0 &&
+
+ test_must_fail git merge -s recursive E^0 &&
+
+ test 3 = $(git ls-files -s | wc -l) &&
+ test 3 = $(git ls-files -u | wc -l) &&
+ test 0 = $(git ls-files -o | wc -l) &&
+
+ test $(git rev-parse :2:new_a) = $(git rev-parse D:new_a) &&
+ test $(git rev-parse :3:new_a) = $(git rev-parse E:new_a) &&
+
+ git cat-file -p B:new_a >>merged &&
+ git cat-file -p C:new_a >>merge-me &&
+ >empty &&
+ test_must_fail git merge-file \
+ -L "Temporary merge branch 2" \
+ -L "" \
+ -L "Temporary merge branch 1" \
+ merged empty merge-me &&
+ sed -e "s/^\([<=>]\)/\1\1\1/" merged >merged-internal &&
+ test $(git rev-parse :1:new_a) = $(git hash-object merged-internal)
+'
+
+#
+# criss-cross + modify/delete:
+#
+# B D
+# o---o
+# / \ / \
+# A o X ? F
+# \ / \ /
+# o---o
+# C E
+#
+# Commit A: file with contents 'A\n'
+# Commit B: file with contents 'B\n'
+# Commit C: file not present
+# Commit D: file with contents 'B\n'
+# Commit E: file not present
+#
+# Merging commits D & E should result in modify/delete conflict.
+
+test_expect_success 'setup criss-cross + modify/delete resolved differently' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ echo A >file &&
+ git add file &&
+ test_tick &&
+ git commit -m A &&
+
+ git branch B &&
+ git checkout -b C &&
+ git rm file &&
+ test_tick &&
+ git commit -m C &&
+
+ git checkout B &&
+ echo B >file &&
+ git add file &&
+ test_tick &&
+ git commit -m B &&
+
+ git checkout B^0 &&
+ test_must_fail git merge C &&
+ echo B >file &&
+ git add file &&
+ test_tick &&
+ git commit -m D &&
+ git tag D &&
+
+ git checkout C^0 &&
+ test_must_fail git merge B &&
+ git rm file &&
+ test_tick &&
+ git commit -m E &&
+ git tag E
+'
+
+test_expect_success 'git detects conflict merging criss-cross+modify/delete' '
+ git checkout D^0 &&
+
+ test_must_fail git merge -s recursive E^0 &&
+
+ test 2 -eq $(git ls-files -s | wc -l) &&
+ test 2 -eq $(git ls-files -u | wc -l) &&
+
+ test $(git rev-parse :1:file) = $(git rev-parse master:file) &&
+ test $(git rev-parse :2:file) = $(git rev-parse B:file)
+'
+
+test_expect_success 'git detects conflict merging criss-cross+modify/delete, reverse direction' '
+ git reset --hard &&
+ git checkout E^0 &&
+
+ test_must_fail git merge -s recursive D^0 &&
+
+ test 2 -eq $(git ls-files -s | wc -l) &&
+ test 2 -eq $(git ls-files -u | wc -l) &&
+
+ test $(git rev-parse :1:file) = $(git rev-parse master:file) &&
+ test $(git rev-parse :3:file) = $(git rev-parse B:file)
+'
+
+#
+# criss-cross + d/f conflict via add/add:
+# Commit A: Neither file 'a' nor directory 'a/' exists.
+# Commit B: Introduce 'a'
+# Commit C: Introduce 'a/file'
+# Commit D: Merge B & C, keeping 'a' and deleting 'a/'
+#
+# Two different later cases:
+# Commit E1: Merge B & C, deleting 'a' but keeping 'a/file'
+# Commit E2: Merge B & C, deleting 'a' but keeping a slightly modified 'a/file'
+#
+# B D
+# o---o
+# / \ / \
+# A o X ? F
+# \ / \ /
+# o---o
+# C E1 or E2
+#
+# Merging D & E1 requires we first create a virtual merge base X from
+# merging A & B in memory. Now, if X could keep both 'a' and 'a/file' in
+# the index, then the merge of D & E1 could be resolved cleanly with both
+# 'a' and 'a/file' removed. Since git does not currently allow creating
+# such a tree, the best we can do is have X contain both 'a~<unique>' and
+# 'a/file' resulting in the merge of D and E1 having a rename/delete
+# conflict for 'a'. (Although this merge appears to be unsolvable with git
+# currently, git could do a lot better than it currently does with these
+# d/f conflicts, which is the purpose of this test.)
+#
+# Merge of D & E2 has similar issues for path 'a', but should always result
+# in a modify/delete conflict for path 'a/file'.
+#
+# We run each merge in both directions, to check for directional issues
+# with D/F conflict handling.
+#
+
+test_expect_success 'setup differently handled merges of directory/file conflict' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ >ignore-me &&
+ git add ignore-me &&
+ test_tick &&
+ git commit -m A &&
+ git tag A &&
+
+ git branch B &&
+ git checkout -b C &&
+ mkdir a &&
+ echo 10 >a/file &&
+ git add a/file &&
+ test_tick &&
+ git commit -m C &&
+
+ git checkout B &&
+ echo 5 >a &&
+ git add a &&
+ test_tick &&
+ git commit -m B &&
+
+ git checkout B^0 &&
+ test_must_fail git merge C &&
+ git clean -f &&
+ rm -rf a/ &&
+ echo 5 >a &&
+ git add a &&
+ test_tick &&
+ git commit -m D &&
+ git tag D &&
+
+ git checkout C^0 &&
+ test_must_fail git merge B &&
+ git clean -f &&
+ git rm --cached a &&
+ echo 10 >a/file &&
+ git add a/file &&
+ test_tick &&
+ git commit -m E1 &&
+ git tag E1 &&
+
+ git checkout C^0 &&
+ test_must_fail git merge B &&
+ git clean -f &&
+ git rm --cached a &&
+ printf "10\n11\n" >a/file &&
+ git add a/file &&
+ test_tick &&
+ git commit -m E2 &&
+ git tag E2
+'
+
+test_expect_success 'merge of D & E1 fails but has appropriate contents' '
+ get_clean_checkout D^0 &&
+
+ test_must_fail git merge -s recursive E1^0 &&
+
+ test 2 -eq $(git ls-files -s | wc -l) &&
+ test 1 -eq $(git ls-files -u | wc -l) &&
+ test 0 -eq $(git ls-files -o | wc -l) &&
+
+ test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
+ test $(git rev-parse :2:a) = $(git rev-parse B:a)
+'
+
+test_expect_success 'merge of E1 & D fails but has appropriate contents' '
+ get_clean_checkout E1^0 &&
+
+ test_must_fail git merge -s recursive D^0 &&
+
+ test 2 -eq $(git ls-files -s | wc -l) &&
+ test 1 -eq $(git ls-files -u | wc -l) &&
+ test 0 -eq $(git ls-files -o | wc -l) &&
+
+ test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
+ test $(git rev-parse :3:a) = $(git rev-parse B:a)
+'
+
+test_expect_success 'merge of D & E2 fails but has appropriate contents' '
+ get_clean_checkout D^0 &&
+
+ test_must_fail git merge -s recursive E2^0 &&
+
+ test 4 -eq $(git ls-files -s | wc -l) &&
+ test 3 -eq $(git ls-files -u | wc -l) &&
+ test 1 -eq $(git ls-files -o | wc -l) &&
+
+ test $(git rev-parse :2:a) = $(git rev-parse B:a) &&
+ test $(git rev-parse :3:a/file) = $(git rev-parse E2:a/file) &&
+ test $(git rev-parse :1:a/file) = $(git rev-parse C:a/file) &&
+ test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
+
+ test -f a~HEAD
+'
+
+test_expect_success 'merge of E2 & D fails but has appropriate contents' '
+ get_clean_checkout E2^0 &&
+
+ test_must_fail git merge -s recursive D^0 &&
+
+ test 4 -eq $(git ls-files -s | wc -l) &&
+ test 3 -eq $(git ls-files -u | wc -l) &&
+ test 1 -eq $(git ls-files -o | wc -l) &&
+
+ test $(git rev-parse :3:a) = $(git rev-parse B:a) &&
+ test $(git rev-parse :2:a/file) = $(git rev-parse E2:a/file) &&
+ test $(git rev-parse :1:a/file) = $(git rev-parse C:a/file) &&
+ test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
+
+ test -f a~D^0
+'
+
+#
+# criss-cross with rename/rename(1to2)/modify followed by
+# rename/rename(2to1)/modify:
+#
+# B D
+# o---o
+# / \ / \
+# A o X ? F
+# \ / \ /
+# o---o
+# C E
+#
+# Commit A: new file: a
+# Commit B: rename a->b, modifying by adding a line
+# Commit C: rename a->c
+# Commit D: merge B&C, resolving conflict by keeping contents in newname
+# Commit E: merge B&C, resolving conflict similar to D but adding another line
+#
+# There is a conflict merging B & C, but one of filename not of file
+# content. Whoever created D and E chose specific resolutions for that
+# conflict resolution. Now, since: (1) there is no content conflict
+# merging B & C, (2) D does not modify that merged content further, and (3)
+# both D & E resolve the name conflict in the same way, the modification to
+# newname in E should not cause any conflicts when it is merged with D.
+# (Note that this can be accomplished by having the virtual merge base have
+# the merged contents of b and c stored in a file named a, which seems like
+# the most logical choice anyway.)
+#
+# Comment from Junio: I do not necessarily agree with the choice "a", but
+# it feels sound to say "B and C do not agree what the final pathname
+# should be, but we know this content was derived from the common A:a so we
+# use one path whose name is arbitrary in the virtual merge base X between
+# D and E" and then further let the rename detection to notice that that
+# arbitrary path gets renamed between X-D to "newname" and X-E also to
+# "newname" to resolve it as both sides renaming it to the same new
+# name. It is akin to what we do at the content level, i.e. "B and C do not
+# agree what the final contents should be, so we leave the conflict marker
+# but that may cancel out at the final merge stage".
+
+test_expect_success 'setup rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
+ git reset --hard &&
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ printf "1\n2\n3\n4\n5\n6\n" >a &&
+ git add a &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a b &&
+ echo 7 >>b &&
+ git add -u &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git mv a c &&
+ git commit -m C &&
+
+ git checkout -q B^0 &&
+ git merge --no-commit -s ours C^0 &&
+ git mv b newname &&
+ git commit -m "Merge commit C^0 into HEAD" &&
+ git tag D &&
+
+ git checkout -q C^0 &&
+ git merge --no-commit -s ours B^0 &&
+ git mv c newname &&
+ printf "7\n8\n" >>newname &&
+ git add -u &&
+ git commit -m "Merge commit B^0 into HEAD" &&
+ git tag E
+'
+
+test_expect_success 'handle rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
+ git checkout D^0 &&
+
+ git merge -s recursive E^0 &&
+
+ test 1 -eq $(git ls-files -s | wc -l) &&
+ test 0 -eq $(git ls-files -u | wc -l) &&
+ test 0 -eq $(git ls-files -o | wc -l) &&
+
+ test $(git rev-parse HEAD:newname) = $(git rev-parse E:newname)
+'
+
+#
+# criss-cross with rename/rename(1to2)/add-source + resolvable modify/modify:
+#
+# B D
+# o---o
+# / \ / \
+# A o X ? F
+# \ / \ /
+# o---o
+# C E
+#
+# Commit A: new file: a
+# Commit B: rename a->b
+# Commit C: rename a->c, add different a
+# Commit D: merge B&C, keeping b&c and (new) a modified at beginning
+# Commit E: merge B&C, keeping b&c and (new) a modified at end
+#
+# Merging commits D & E should result in no conflict; doing so correctly
+# requires getting the virtual merge base (from merging B&C) right, handling
+# renaming carefully (both in the virtual merge base and later), and getting
+# content merge handled.
+
+test_expect_success 'setup criss-cross + rename/rename/add + modify/modify' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ printf "lots\nof\nwords\nand\ncontent\n" >a &&
+ git add a &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a b &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git mv a c &&
+ printf "2\n3\n4\n5\n6\n7\n" >a &&
+ git add a &&
+ git commit -m C &&
+
+ git checkout B^0 &&
+ git merge --no-commit -s ours C^0 &&
+ git checkout C -- a c &&
+ mv a old_a &&
+ echo 1 >a &&
+ cat old_a >>a &&
+ rm old_a &&
+ git add -u &&
+ git commit -m "Merge commit C^0 into HEAD" &&
+ git tag D &&
+
+ git checkout C^0 &&
+ git merge --no-commit -s ours B^0 &&
+ git checkout B -- b &&
+ echo 8 >>a &&
+ git add -u &&
+ git commit -m "Merge commit B^0 into HEAD" &&
+ git tag E
+'
+
+test_expect_failure 'detect rename/rename/add-source for virtual merge-base' '
+ git checkout D^0 &&
+
+ git merge -s recursive E^0 &&
+
+ test 3 -eq $(git ls-files -s | wc -l) &&
+ test 0 -eq $(git ls-files -u | wc -l) &&
+ test 0 -eq $(git ls-files -o | wc -l) &&
+
+ test $(git rev-parse HEAD:b) = $(git rev-parse A:a) &&
+ test $(git rev-parse HEAD:c) = $(git rev-parse A:a) &&
+ test "$(cat a)" = "$(printf "1\n2\n3\n4\n5\n6\n7\n8\n")"
+'
+
+#
+# criss-cross with rename/rename(1to2)/add-dest + simple modify:
+#
+# B D
+# o---o
+# / \ / \
+# A o X ? F
+# \ / \ /
+# o---o
+# C E
+#
+# Commit A: new file: a
+# Commit B: rename a->b, add c
+# Commit C: rename a->c
+# Commit D: merge B&C, keeping A:a and B:c
+# Commit E: merge B&C, keeping A:a and slightly modified c from B
+#
+# Merging commits D & E should result in no conflict. The virtual merge
+# base of B & C needs to not delete B:c for that to work, though...
+
+test_expect_success 'setup criss-cross+rename/rename/add-dest + simple modify' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ >a &&
+ git add a &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a b &&
+ printf "1\n2\n3\n4\n5\n6\n7\n" >c &&
+ git add c &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git mv a c &&
+ git commit -m C &&
+
+ git checkout B^0 &&
+ git merge --no-commit -s ours C^0 &&
+ git mv b a &&
+ git commit -m "D is like B but renames b back to a" &&
+ git tag D &&
+
+ git checkout B^0 &&
+ git merge --no-commit -s ours C^0 &&
+ git mv b a &&
+ echo 8 >>c &&
+ git add c &&
+ git commit -m "E like D but has mod in c" &&
+ git tag E
+'
+
+test_expect_success 'virtual merge base handles rename/rename(1to2)/add-dest' '
+ git checkout D^0 &&
+
+ git merge -s recursive E^0 &&
+
+ test 2 -eq $(git ls-files -s | wc -l) &&
+ test 0 -eq $(git ls-files -u | wc -l) &&
+ test 0 -eq $(git ls-files -o | wc -l) &&
+
+ test $(git rev-parse HEAD:a) = $(git rev-parse A:a) &&
+ test $(git rev-parse HEAD:c) = $(git rev-parse E:c)
'
test_done
diff --git a/t/t6037-merge-ours-theirs.sh b/t/t6037-merge-ours-theirs.sh
index 8ab3d61..3889eca 100755
--- a/t/t6037-merge-ours-theirs.sh
+++ b/t/t6037-merge-ours-theirs.sh
@@ -53,12 +53,24 @@ test_expect_success 'recursive favouring ours' '
! grep 1 file
'
-test_expect_success 'pull with -X' '
+test_expect_success 'binary file with -Xours/-Xtheirs' '
+ echo file binary >.gitattributes &&
+
+ git reset --hard master &&
+ git merge -s recursive -X theirs side &&
+ git diff --exit-code side HEAD -- file &&
+
+ git reset --hard master &&
+ git merge -s recursive -X ours side &&
+ git diff --exit-code master HEAD -- file
+'
+
+test_expect_success 'pull passes -X to underlying merge' '
git reset --hard master && git pull -s recursive -Xours . side &&
git reset --hard master && git pull -s recursive -X ours . side &&
git reset --hard master && git pull -s recursive -Xtheirs . side &&
git reset --hard master && git pull -s recursive -X theirs . side &&
- git reset --hard master && ! git pull -s recursive -X bork . side
+ git reset --hard master && test_must_fail git pull -s recursive -X bork . side
'
test_done
diff --git a/t/t6038-merge-text-auto.sh b/t/t6038-merge-text-auto.sh
new file mode 100755
index 0000000..85c10b0
--- /dev/null
+++ b/t/t6038-merge-text-auto.sh
@@ -0,0 +1,205 @@
+#!/bin/sh
+
+test_description='CRLF merge conflict across text=auto change
+
+* [master] remove .gitattributes
+ ! [side] add line from b
+--
+ + [side] add line from b
+* [master] remove .gitattributes
+* [master^] add line from a
+* [master~2] normalize file
+*+ [side^] Initial
+'
+
+. ./test-lib.sh
+
+test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
+
+test_expect_success setup '
+ git config core.autocrlf false &&
+
+ echo first line | append_cr >file &&
+ echo first line >control_file &&
+ echo only line >inert_file &&
+
+ git add file control_file inert_file &&
+ test_tick &&
+ git commit -m "Initial" &&
+ git tag initial &&
+ git branch side &&
+
+ echo "* text=auto" >.gitattributes &&
+ touch file &&
+ git add .gitattributes file &&
+ test_tick &&
+ git commit -m "normalize file" &&
+
+ echo same line | append_cr >>file &&
+ echo same line >>control_file &&
+ git add file control_file &&
+ test_tick &&
+ git commit -m "add line from a" &&
+ git tag a &&
+
+ git rm .gitattributes &&
+ rm file &&
+ git checkout file &&
+ test_tick &&
+ git commit -m "remove .gitattributes" &&
+ git tag c &&
+
+ git checkout side &&
+ echo same line | append_cr >>file &&
+ echo same line >>control_file &&
+ git add file control_file &&
+ test_tick &&
+ git commit -m "add line from b" &&
+ git tag b &&
+
+ git checkout master
+'
+
+test_expect_success 'set up fuzz_conflict() helper' '
+ fuzz_conflict() {
+ sed $SED_OPTIONS -e "s/^\([<>=]......\) .*/\1/" "$@"
+ }
+'
+
+test_expect_success 'Merge after setting text=auto' '
+ cat <<-\EOF >expected &&
+ first line
+ same line
+ EOF
+
+ if test_have_prereq NATIVE_CRLF; then
+ append_cr <expected >expected.temp &&
+ mv expected.temp expected
+ fi &&
+ git config merge.renormalize true &&
+ git rm -fr . &&
+ rm -f .gitattributes &&
+ git reset --hard a &&
+ git merge b &&
+ test_cmp expected file
+'
+
+test_expect_success 'Merge addition of text=auto' '
+ cat <<-\EOF >expected &&
+ first line
+ same line
+ EOF
+
+ if test_have_prereq NATIVE_CRLF; then
+ append_cr <expected >expected.temp &&
+ mv expected.temp expected
+ fi &&
+ git config merge.renormalize true &&
+ git rm -fr . &&
+ rm -f .gitattributes &&
+ git reset --hard b &&
+ git merge a &&
+ test_cmp expected file
+'
+
+test_expect_success 'Detect CRLF/LF conflict after setting text=auto' '
+ echo "<<<<<<<" >expected &&
+ if test_have_prereq NATIVE_CRLF; then
+ echo first line | append_cr >>expected &&
+ echo same line | append_cr >>expected &&
+ echo ======= | append_cr >>expected
+ else
+ echo first line >>expected &&
+ echo same line >>expected &&
+ echo ======= >>expected
+ fi &&
+ echo first line | append_cr >>expected &&
+ echo same line | append_cr >>expected &&
+ echo ">>>>>>>" >>expected &&
+ git config merge.renormalize false &&
+ rm -f .gitattributes &&
+ git reset --hard a &&
+ test_must_fail git merge b &&
+ fuzz_conflict file >file.fuzzy &&
+ test_cmp expected file.fuzzy
+'
+
+test_expect_success 'Detect LF/CRLF conflict from addition of text=auto' '
+ echo "<<<<<<<" >expected &&
+ echo first line | append_cr >>expected &&
+ echo same line | append_cr >>expected &&
+ if test_have_prereq NATIVE_CRLF; then
+ echo ======= | append_cr >>expected &&
+ echo first line | append_cr >>expected &&
+ echo same line | append_cr >>expected
+ else
+ echo ======= >>expected &&
+ echo first line >>expected &&
+ echo same line >>expected
+ fi &&
+ echo ">>>>>>>" >>expected &&
+ git config merge.renormalize false &&
+ rm -f .gitattributes &&
+ git reset --hard b &&
+ test_must_fail git merge a &&
+ fuzz_conflict file >file.fuzzy &&
+ test_cmp expected file.fuzzy
+'
+
+test_expect_failure 'checkout -m after setting text=auto' '
+ cat <<-\EOF >expected &&
+ first line
+ same line
+ EOF
+
+ git config merge.renormalize true &&
+ git rm -fr . &&
+ rm -f .gitattributes &&
+ git reset --hard initial &&
+ git checkout a -- . &&
+ git checkout -m b &&
+ test_cmp expected file
+'
+
+test_expect_failure 'checkout -m addition of text=auto' '
+ cat <<-\EOF >expected &&
+ first line
+ same line
+ EOF
+
+ git config merge.renormalize true &&
+ git rm -fr . &&
+ rm -f .gitattributes file &&
+ git reset --hard initial &&
+ git checkout b -- . &&
+ git checkout -m a &&
+ test_cmp expected file
+'
+
+test_expect_failure 'cherry-pick patch from after text=auto was added' '
+ append_cr <<-\EOF >expected &&
+ first line
+ same line
+ EOF
+
+ git config merge.renormalize true &&
+ git rm -fr . &&
+ git reset --hard b &&
+ test_must_fail git cherry-pick a >err 2>&1 &&
+ grep "[Nn]othing added" err &&
+ test_cmp expected file
+'
+
+test_expect_success 'Test delete/normalize conflict' '
+ git checkout -f side &&
+ git rm -fr . &&
+ rm -f .gitattributes &&
+ git reset --hard initial &&
+ git rm file &&
+ git commit -m "remove file" &&
+ git checkout master &&
+ git reset --hard a^ &&
+ git merge side
+'
+
+test_done
diff --git a/t/t6039-merge-ignorecase.sh b/t/t6039-merge-ignorecase.sh
new file mode 100755
index 0000000..531850d
--- /dev/null
+++ b/t/t6039-merge-ignorecase.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+test_description='git-merge with case-changing rename on case-insensitive file system'
+
+. ./test-lib.sh
+
+if ! test_have_prereq CASE_INSENSITIVE_FS
+then
+ skip_all='skipping case insensitive tests - case sensitive file system'
+ test_done
+fi
+
+test_expect_success 'merge with case-changing rename' '
+ test $(git config core.ignorecase) = true &&
+ >TestCase &&
+ git add TestCase &&
+ git commit -m "add TestCase" &&
+ git tag baseline &&
+ git checkout -b with-camel &&
+ >foo &&
+ git add foo &&
+ git commit -m "intervening commit" &&
+ git checkout master &&
+ git rm TestCase &&
+ >testcase &&
+ git add testcase &&
+ git commit -m "rename to testcase" &&
+ git checkout with-camel &&
+ git merge master -m "merge" &&
+ test_path_is_file testcase
+'
+
+test_expect_success 'merge with case-changing rename on both sides' '
+ git checkout master &&
+ git reset --hard baseline &&
+ git branch -D with-camel &&
+ git checkout -b with-camel &&
+ git mv TestCase testcase &&
+ git commit -m "recase on branch" &&
+ >foo &&
+ git add foo &&
+ git commit -m "intervening commit" &&
+ git checkout master &&
+ git rm TestCase &&
+ >testcase &&
+ git add testcase &&
+ git commit -m "rename to testcase" &&
+ git checkout with-camel &&
+ git merge master -m "merge" &&
+ test_path_is_file testcase
+'
+
+test_done
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index 1785e17..3d5c238 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -12,10 +12,9 @@ advance () {
}
test_expect_success setup '
- for i in a b c;
- do
- advance $i || break
- done &&
+ advance a &&
+ advance b &&
+ advance c &&
git clone . test &&
(
cd test &&
@@ -28,18 +27,25 @@ test_expect_success setup '
git reset --hard HEAD^ &&
git checkout -b b4 origin &&
advance e &&
- advance f
+ advance f &&
+ git checkout -b brokenbase origin &&
+ git checkout -b b5 --track brokenbase &&
+ advance g &&
+ git branch -d brokenbase &&
+ git checkout -b b6 origin
) &&
git checkout -b follower --track master &&
- advance g
+ advance h
'
-script='s/^..\(b.\)[ 0-9a-f]*\[\([^]]*\)\].*/\1 \2/p'
+script='s/^..\(b.\) *[0-9a-f]* \(.*\)$/\1 \2/p'
cat >expect <<\EOF
-b1 ahead 1, behind 1
-b2 ahead 1, behind 1
-b3 behind 1
-b4 ahead 2
+b1 [ahead 1, behind 1] d
+b2 [ahead 1, behind 1] d
+b3 [behind 1] b
+b4 [ahead 2] f
+b5 g
+b6 c
EOF
test_expect_success 'branch -v' '
@@ -48,46 +54,138 @@ test_expect_success 'branch -v' '
git branch -v
) |
sed -n -e "$script" >actual &&
- test_cmp expect actual
+ test_i18ncmp expect actual
+'
+
+cat >expect <<\EOF
+b1 [origin/master: ahead 1, behind 1] d
+b2 [origin/master: ahead 1, behind 1] d
+b3 [origin/master: behind 1] b
+b4 [origin/master: ahead 2] f
+b5 [brokenbase: gone] g
+b6 [origin/master] c
+EOF
+
+test_expect_success 'branch -vv' '
+ (
+ cd test &&
+ git branch -vv
+ ) |
+ sed -n -e "$script" >actual &&
+ test_i18ncmp expect actual
'
-test_expect_success 'checkout' '
+test_expect_success 'checkout (diverged from upstream)' '
(
cd test && git checkout b1
) >actual &&
- grep "have 1 and 1 different" actual
+ test_i18ngrep "have 1 and 1 different" actual
'
test_expect_success 'checkout with local tracked branch' '
git checkout master &&
- git checkout follower >actual
- grep "is ahead of" actual
+ git checkout follower >actual &&
+ test_i18ngrep "is ahead of" actual
+'
+
+test_expect_success 'checkout (upstream is gone)' '
+ (
+ cd test &&
+ git checkout b5
+ ) >actual &&
+ test_i18ngrep "is based on .*, but the upstream is gone." actual
+'
+
+test_expect_success 'checkout (up-to-date with upstream)' '
+ (
+ cd test && git checkout b6
+ ) >actual &&
+ test_i18ngrep "Your branch is up-to-date with .origin/master" actual
'
-test_expect_success 'status' '
+test_expect_success 'status (diverged from upstream)' '
(
cd test &&
git checkout b1 >/dev/null &&
# reports nothing to commit
test_must_fail git commit --dry-run
) >actual &&
- grep "have 1 and 1 different" actual
+ test_i18ngrep "have 1 and 1 different" actual
+'
+
+test_expect_success 'status (upstream is gone)' '
+ (
+ cd test &&
+ git checkout b5 >/dev/null &&
+ # reports nothing to commit
+ test_must_fail git commit --dry-run
+ ) >actual &&
+ test_i18ngrep "is based on .*, but the upstream is gone." actual
'
-test_expect_success 'status when tracking lightweight tags' '
+test_expect_success 'status (up-to-date with upstream)' '
+ (
+ cd test &&
+ git checkout b6 >/dev/null &&
+ # reports nothing to commit
+ test_must_fail git commit --dry-run
+ ) >actual &&
+ test_i18ngrep "Your branch is up-to-date with .origin/master" actual
+'
+
+cat >expect <<\EOF
+## b1...origin/master [ahead 1, behind 1]
+EOF
+
+test_expect_success 'status -s -b (diverged from upstream)' '
+ (
+ cd test &&
+ git checkout b1 >/dev/null &&
+ git status -s -b | head -1
+ ) >actual &&
+ test_i18ncmp expect actual
+'
+
+cat >expect <<\EOF
+## b5...brokenbase [gone]
+EOF
+
+test_expect_success 'status -s -b (upstream is gone)' '
+ (
+ cd test &&
+ git checkout b5 >/dev/null &&
+ git status -s -b | head -1
+ ) >actual &&
+ test_i18ncmp expect actual
+'
+
+cat >expect <<\EOF
+## b6...origin/master
+EOF
+
+test_expect_success 'status -s -b (up-to-date with upstream)' '
+ (
+ cd test &&
+ git checkout b6 >/dev/null &&
+ git status -s -b | head -1
+ ) >actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'fail to track lightweight tags' '
git checkout master &&
git tag light &&
- git branch --track lighttrack light >actual &&
- grep "set up to track" actual &&
- git checkout lighttrack
+ test_must_fail git branch --track lighttrack light >actual &&
+ test_i18ngrep ! "set up to track" actual &&
+ test_must_fail git checkout lighttrack
'
-test_expect_success 'status when tracking annotated tags' '
+test_expect_success 'fail to track 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_must_fail git branch --track heavytrack heavy >actual &&
+ test_i18ngrep ! "set up to track" actual &&
+ test_must_fail git checkout heavytrack
'
test_expect_success 'setup tracking with branch --set-upstream on existing branch' '
@@ -110,4 +208,18 @@ test_expect_success '--set-upstream does not change branch' '
grep -q "^refs/heads/master$" actual &&
cmp expect2 actual2
'
+
+test_expect_success '--set-upstream @{-1}' '
+ git checkout from-master &&
+ git checkout from-master2 &&
+ git config branch.from-master2.merge > expect2 &&
+ git branch --set-upstream @{-1} follower &&
+ git config branch.from-master.merge > actual &&
+ git config branch.from-master2.merge > actual2 &&
+ git branch --set-upstream from-master follower &&
+ git config branch.from-master.merge > expect &&
+ test_cmp expect2 actual2 &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t6041-bisect-submodule.sh b/t/t6041-bisect-submodule.sh
new file mode 100755
index 0000000..62b8a2e
--- /dev/null
+++ b/t/t6041-bisect-submodule.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='bisect can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+git_bisect () {
+ git status -su >expect &&
+ ls -1pR * >>expect &&
+ tar cf "$TRASH_DIRECTORY/tmp.tar" * &&
+ GOOD=$(git rev-parse --verify HEAD) &&
+ git checkout "$1" &&
+ echo "foo" >bar &&
+ git add bar &&
+ git commit -m "bisect bad" &&
+ BAD=$(git rev-parse --verify HEAD) &&
+ git reset --hard HEAD^^ &&
+ git submodule update &&
+ git bisect start &&
+ git bisect good $GOOD &&
+ rm -rf * &&
+ tar xf "$TRASH_DIRECTORY/tmp.tar" &&
+ git status -su >actual &&
+ ls -1pR * >>actual &&
+ test_cmp expect actual &&
+ git bisect bad $BAD
+}
+
+test_submodule_switch "git_bisect"
+
+test_done
diff --git a/t/t6042-merge-rename-corner-cases.sh b/t/t6042-merge-rename-corner-cases.sh
new file mode 100755
index 0000000..411550d
--- /dev/null
+++ b/t/t6042-merge-rename-corner-cases.sh
@@ -0,0 +1,578 @@
+#!/bin/sh
+
+test_description="recursive merge corner cases w/ renames but not criss-crosses"
+# t6036 has corner cases that involve both criss-cross merges and renames
+
+. ./test-lib.sh
+
+test_expect_success 'setup rename/delete + untracked file' '
+ echo "A pretty inscription" >ring &&
+ git add ring &&
+ test_tick &&
+ git commit -m beginning &&
+
+ git branch people &&
+ git checkout -b rename-the-ring &&
+ git mv ring one-ring-to-rule-them-all &&
+ test_tick &&
+ git commit -m fullname &&
+
+ git checkout people &&
+ git rm ring &&
+ echo gollum >owner &&
+ git add owner &&
+ test_tick &&
+ git commit -m track-people-instead-of-objects &&
+ echo "Myyy PRECIOUSSS" >ring
+'
+
+test_expect_success "Does git preserve Gollum's precious artifact?" '
+ test_must_fail git merge -s recursive rename-the-ring &&
+
+ # Make sure git did not delete an untracked file
+ test -f ring
+'
+
+# Testcase setup for rename/modify/add-source:
+# Commit A: new file: a
+# Commit B: modify a slightly
+# Commit C: rename a->b, add completely different a
+#
+# We should be able to merge B & C cleanly
+
+test_expect_success 'setup rename/modify/add-source conflict' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ printf "1\n2\n3\n4\n5\n6\n7\n" >a &&
+ git add a &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ echo 8 >>a &&
+ git add a &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git mv a b &&
+ echo something completely different >a &&
+ git add a &&
+ git commit -m C
+'
+
+test_expect_failure 'rename/modify/add-source conflict resolvable' '
+ git checkout B^0 &&
+
+ git merge -s recursive C^0 &&
+
+ test $(git rev-parse B:a) = $(git rev-parse b) &&
+ test $(git rev-parse C:a) = $(git rev-parse a)
+'
+
+test_expect_success 'setup resolvable conflict missed if rename missed' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ printf "1\n2\n3\n4\n5\n" >a &&
+ echo foo >b &&
+ git add a b &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a c &&
+ echo "Completely different content" >a &&
+ git add a &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ echo 6 >>a &&
+ git add a &&
+ git commit -m C
+'
+
+test_expect_failure 'conflict caused if rename not detected' '
+ git checkout -q C^0 &&
+ git merge -s recursive B^0 &&
+
+ test 3 -eq $(git ls-files -s | wc -l) &&
+ test 0 -eq $(git ls-files -u | wc -l) &&
+ test 0 -eq $(git ls-files -o | wc -l) &&
+
+ test_line_count = 6 c &&
+ test $(git rev-parse HEAD:a) = $(git rev-parse B:a) &&
+ test $(git rev-parse HEAD:b) = $(git rev-parse A:b)
+'
+
+test_expect_success 'setup conflict resolved wrong if rename missed' '
+ git reset --hard &&
+ git clean -f &&
+
+ git checkout -b D A &&
+ echo 7 >>a &&
+ git add a &&
+ git mv a c &&
+ echo "Completely different content" >a &&
+ git add a &&
+ git commit -m D &&
+
+ git checkout -b E A &&
+ git rm a &&
+ echo "Completely different content" >>a &&
+ git add a &&
+ git commit -m E
+'
+
+test_expect_failure 'missed conflict if rename not detected' '
+ git checkout -q E^0 &&
+ test_must_fail git merge -s recursive D^0
+'
+
+# Tests for undetected rename/add-source causing a file to erroneously be
+# deleted (and for mishandled rename/rename(1to1) causing the same issue).
+#
+# This test uses a rename/rename(1to1)+add-source conflict (1to1 means the
+# same file is renamed on both sides to the same thing; it should trigger
+# the 1to2 logic, which it would do if the add-source didn't cause issues
+# for git's rename detection):
+# Commit A: new file: a
+# Commit B: rename a->b
+# Commit C: rename a->b, add unrelated a
+
+test_expect_success 'setup undetected rename/add-source causes data loss' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ printf "1\n2\n3\n4\n5\n" >a &&
+ git add a &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a b &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git mv a b &&
+ echo foobar >a &&
+ git add a &&
+ git commit -m C
+'
+
+test_expect_failure 'detect rename/add-source and preserve all data' '
+ git checkout B^0 &&
+
+ git merge -s recursive C^0 &&
+
+ test 2 -eq $(git ls-files -s | wc -l) &&
+ test 2 -eq $(git ls-files -u | wc -l) &&
+ test 0 -eq $(git ls-files -o | wc -l) &&
+
+ test -f a &&
+ test -f b &&
+
+ test $(git rev-parse HEAD:b) = $(git rev-parse A:a) &&
+ test $(git rev-parse HEAD:a) = $(git rev-parse C:a)
+'
+
+test_expect_failure 'detect rename/add-source and preserve all data, merge other way' '
+ git checkout C^0 &&
+
+ git merge -s recursive B^0 &&
+
+ test 2 -eq $(git ls-files -s | wc -l) &&
+ test 2 -eq $(git ls-files -u | wc -l) &&
+ test 0 -eq $(git ls-files -o | wc -l) &&
+
+ test -f a &&
+ test -f b &&
+
+ test $(git rev-parse HEAD:b) = $(git rev-parse A:a) &&
+ test $(git rev-parse HEAD:a) = $(git rev-parse C:a)
+'
+
+test_expect_success 'setup content merge + rename/directory conflict' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ printf "1\n2\n3\n4\n5\n6\n" >file &&
+ git add file &&
+ test_tick &&
+ git commit -m base &&
+ git tag base &&
+
+ git checkout -b right &&
+ echo 7 >>file &&
+ mkdir newfile &&
+ echo junk >newfile/realfile &&
+ git add file newfile/realfile &&
+ test_tick &&
+ git commit -m right &&
+
+ git checkout -b left-conflict base &&
+ echo 8 >>file &&
+ git add file &&
+ git mv file newfile &&
+ test_tick &&
+ git commit -m left &&
+
+ git checkout -b left-clean base &&
+ echo 0 >newfile &&
+ cat file >>newfile &&
+ git add newfile &&
+ git rm file &&
+ test_tick &&
+ git commit -m left
+'
+
+test_expect_success 'rename/directory conflict + clean content merge' '
+ git reset --hard &&
+ git reset --hard &&
+ git clean -fdqx &&
+
+ git checkout left-clean^0 &&
+
+ test_must_fail git merge -s recursive right^0 &&
+
+ test 2 -eq $(git ls-files -s | wc -l) &&
+ test 1 -eq $(git ls-files -u | wc -l) &&
+ test 1 -eq $(git ls-files -o | wc -l) &&
+
+ echo 0 >expect &&
+ git cat-file -p base:file >>expect &&
+ echo 7 >>expect &&
+ test_cmp expect newfile~HEAD &&
+
+ test $(git rev-parse :2:newfile) = $(git hash-object expect) &&
+
+ test -f newfile/realfile &&
+ test -f newfile~HEAD
+'
+
+test_expect_success 'rename/directory conflict + content merge conflict' '
+ git reset --hard &&
+ git reset --hard &&
+ git clean -fdqx &&
+
+ git checkout left-conflict^0 &&
+
+ test_must_fail git merge -s recursive right^0 &&
+
+ test 4 -eq $(git ls-files -s | wc -l) &&
+ test 3 -eq $(git ls-files -u | wc -l) &&
+ test 1 -eq $(git ls-files -o | wc -l) &&
+
+ git cat-file -p left-conflict:newfile >left &&
+ git cat-file -p base:file >base &&
+ git cat-file -p right:file >right &&
+ test_must_fail git merge-file \
+ -L "HEAD:newfile" \
+ -L "" \
+ -L "right^0:file" \
+ left base right &&
+ test_cmp left newfile~HEAD &&
+
+ test $(git rev-parse :1:newfile) = $(git rev-parse base:file) &&
+ test $(git rev-parse :2:newfile) = $(git rev-parse left-conflict:newfile) &&
+ test $(git rev-parse :3:newfile) = $(git rev-parse right:file) &&
+
+ test -f newfile/realfile &&
+ test -f newfile~HEAD
+'
+
+test_expect_success 'setup content merge + rename/directory conflict w/ disappearing dir' '
+ git reset --hard &&
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ mkdir sub &&
+ printf "1\n2\n3\n4\n5\n6\n" >sub/file &&
+ git add sub/file &&
+ test_tick &&
+ git commit -m base &&
+ git tag base &&
+
+ git checkout -b right &&
+ echo 7 >>sub/file &&
+ git add sub/file &&
+ test_tick &&
+ git commit -m right &&
+
+ git checkout -b left base &&
+ echo 0 >newfile &&
+ cat sub/file >>newfile &&
+ git rm sub/file &&
+ mv newfile sub &&
+ git add sub &&
+ test_tick &&
+ git commit -m left
+'
+
+test_expect_success 'disappearing dir in rename/directory conflict handled' '
+ git reset --hard &&
+ git clean -fdqx &&
+
+ git checkout left^0 &&
+
+ git merge -s recursive right^0 &&
+
+ test 1 -eq $(git ls-files -s | wc -l) &&
+ test 0 -eq $(git ls-files -u | wc -l) &&
+ test 0 -eq $(git ls-files -o | wc -l) &&
+
+ echo 0 >expect &&
+ git cat-file -p base:sub/file >>expect &&
+ echo 7 >>expect &&
+ test_cmp expect sub &&
+
+ test -f sub
+'
+
+# Test for all kinds of things that can go wrong with rename/rename (2to1):
+# Commit A: new files: a & b
+# Commit B: rename a->c, modify b
+# Commit C: rename b->c, modify a
+#
+# Merging of B & C should NOT be clean. Questions:
+# * Both a & b should be removed by the merge; are they?
+# * The two c's should contain modifications to a & b; do they?
+# * The index should contain two files, both for c; does it?
+# * The working copy should have two files, both of form c~<unique>; does it?
+# * Nothing else should be present. Is anything?
+
+test_expect_success 'setup rename/rename (2to1) + modify/modify' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ printf "1\n2\n3\n4\n5\n" >a &&
+ printf "5\n4\n3\n2\n1\n" >b &&
+ git add a b &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a c &&
+ echo 0 >>b &&
+ git add b &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git mv b c &&
+ echo 6 >>a &&
+ git add a &&
+ git commit -m C
+'
+
+test_expect_success 'handle rename/rename (2to1) conflict correctly' '
+ git checkout B^0 &&
+
+ test_must_fail git merge -s recursive C^0 >out &&
+ test_i18ngrep "CONFLICT (rename/rename)" out &&
+
+ test 2 -eq $(git ls-files -s | wc -l) &&
+ test 2 -eq $(git ls-files -u | wc -l) &&
+ test 2 -eq $(git ls-files -u c | wc -l) &&
+ test 3 -eq $(git ls-files -o | wc -l) &&
+
+ test ! -f a &&
+ test ! -f b &&
+ test -f c~HEAD &&
+ test -f c~C^0 &&
+
+ test $(git hash-object c~HEAD) = $(git rev-parse C:a) &&
+ test $(git hash-object c~C^0) = $(git rev-parse B:b)
+'
+
+# Testcase setup for simple rename/rename (1to2) conflict:
+# Commit A: new file: a
+# Commit B: rename a->b
+# Commit C: rename a->c
+test_expect_success 'setup simple rename/rename (1to2) conflict' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ echo stuff >a &&
+ git add a &&
+ test_tick &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a b &&
+ test_tick &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git mv a c &&
+ test_tick &&
+ git commit -m C
+'
+
+test_expect_success 'merge has correct working tree contents' '
+ git checkout C^0 &&
+
+ test_must_fail git merge -s recursive B^0 &&
+
+ test 3 -eq $(git ls-files -s | wc -l) &&
+ test 3 -eq $(git ls-files -u | wc -l) &&
+ test 0 -eq $(git ls-files -o | wc -l) &&
+
+ test $(git rev-parse :1:a) = $(git rev-parse A:a) &&
+ test $(git rev-parse :3:b) = $(git rev-parse A:a) &&
+ test $(git rev-parse :2:c) = $(git rev-parse A:a) &&
+
+ test ! -f a &&
+ test $(git hash-object b) = $(git rev-parse A:a) &&
+ test $(git hash-object c) = $(git rev-parse A:a)
+'
+
+# Testcase setup for rename/rename(1to2)/add-source conflict:
+# Commit A: new file: a
+# Commit B: rename a->b
+# Commit C: rename a->c, add completely different a
+#
+# Merging of B & C should NOT be clean; there's a rename/rename conflict
+
+test_expect_success 'setup rename/rename(1to2)/add-source conflict' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ printf "1\n2\n3\n4\n5\n6\n7\n" >a &&
+ git add a &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a b &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git mv a c &&
+ echo something completely different >a &&
+ git add a &&
+ git commit -m C
+'
+
+test_expect_failure 'detect conflict with rename/rename(1to2)/add-source merge' '
+ git checkout B^0 &&
+
+ test_must_fail git merge -s recursive C^0 &&
+
+ test 4 -eq $(git ls-files -s | wc -l) &&
+ test 0 -eq $(git ls-files -o | wc -l) &&
+
+ test $(git rev-parse 3:a) = $(git rev-parse C:a) &&
+ test $(git rev-parse 1:a) = $(git rev-parse A:a) &&
+ test $(git rev-parse 2:b) = $(git rev-parse B:b) &&
+ test $(git rev-parse 3:c) = $(git rev-parse C:c) &&
+
+ test -f a &&
+ test -f b &&
+ test -f c
+'
+
+test_expect_success 'setup rename/rename(1to2)/add-source resolvable conflict' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ >a &&
+ git add a &&
+ test_tick &&
+ git commit -m base &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a b &&
+ test_tick &&
+ git commit -m one &&
+
+ git checkout -b C A &&
+ git mv a b &&
+ echo important-info >a &&
+ git add a &&
+ test_tick &&
+ git commit -m two
+'
+
+test_expect_failure 'rename/rename/add-source still tracks new a file' '
+ git checkout C^0 &&
+ git merge -s recursive B^0 &&
+
+ test 2 -eq $(git ls-files -s | wc -l) &&
+ test 0 -eq $(git ls-files -o | wc -l) &&
+
+ test $(git rev-parse HEAD:a) = $(git rev-parse C:a) &&
+ test $(git rev-parse HEAD:b) = $(git rev-parse A:a)
+'
+
+test_expect_success 'setup rename/rename(1to2)/add-dest conflict' '
+ git rm -rf . &&
+ git clean -fdqx &&
+ rm -rf .git &&
+ git init &&
+
+ echo stuff >a &&
+ git add a &&
+ test_tick &&
+ git commit -m base &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a b &&
+ echo precious-data >c &&
+ git add c &&
+ test_tick &&
+ git commit -m one &&
+
+ git checkout -b C A &&
+ git mv a c &&
+ echo important-info >b &&
+ git add b &&
+ test_tick &&
+ git commit -m two
+'
+
+test_expect_success 'rename/rename/add-dest merge still knows about conflicting file versions' '
+ git checkout C^0 &&
+ test_must_fail git merge -s recursive B^0 &&
+
+ test 5 -eq $(git ls-files -s | wc -l) &&
+ test 2 -eq $(git ls-files -u b | wc -l) &&
+ test 2 -eq $(git ls-files -u c | wc -l) &&
+ test 4 -eq $(git ls-files -o | wc -l) &&
+
+ test $(git rev-parse :1:a) = $(git rev-parse A:a) &&
+ test $(git rev-parse :2:b) = $(git rev-parse C:b) &&
+ test $(git rev-parse :3:b) = $(git rev-parse B:b) &&
+ test $(git rev-parse :2:c) = $(git rev-parse C:c) &&
+ test $(git rev-parse :3:c) = $(git rev-parse B:c) &&
+
+ test $(git hash-object c~HEAD) = $(git rev-parse C:c) &&
+ test $(git hash-object c~B\^0) = $(git rev-parse B:c) &&
+ test $(git hash-object b~HEAD) = $(git rev-parse C:b) &&
+ test $(git hash-object b~B\^0) = $(git rev-parse B:b) &&
+
+ test ! -f b &&
+ test ! -f c
+'
+
+test_done
diff --git a/t/t6044-merge-unrelated-index-changes.sh b/t/t6044-merge-unrelated-index-changes.sh
new file mode 100755
index 0000000..0102348
--- /dev/null
+++ b/t/t6044-merge-unrelated-index-changes.sh
@@ -0,0 +1,153 @@
+#!/bin/sh
+
+test_description="merges with unrelated index changes"
+
+. ./test-lib.sh
+
+# Testcase for some simple merges
+# A
+# o-----o B
+# \
+# \---o C
+# \
+# \-o D
+# \
+# o E
+# Commit A: some file a
+# Commit B: adds file b, modifies end of a
+# Commit C: adds file c
+# Commit D: adds file d, modifies beginning of a
+# Commit E: renames a->subdir/a, adds subdir/e
+
+test_expect_success 'setup trivial merges' '
+ test_seq 1 10 >a &&
+ git add a &&
+ test_tick && git commit -m A &&
+
+ git branch A &&
+ git branch B &&
+ git branch C &&
+ git branch D &&
+ git branch E &&
+
+ git checkout B &&
+ echo b >b &&
+ echo 11 >>a &&
+ git add a b &&
+ test_tick && git commit -m B &&
+
+ git checkout C &&
+ echo c >c &&
+ git add c &&
+ test_tick && git commit -m C &&
+
+ git checkout D &&
+ test_seq 2 10 >a &&
+ echo d >d &&
+ git add a d &&
+ test_tick && git commit -m D &&
+
+ git checkout E &&
+ mkdir subdir &&
+ git mv a subdir/a &&
+ echo e >subdir/e &&
+ git add subdir &&
+ test_tick && git commit -m E
+'
+
+test_expect_success 'ff update' '
+ git reset --hard &&
+ git checkout A^0 &&
+
+ touch random_file && git add random_file &&
+
+ git merge E^0 &&
+
+ test_must_fail git rev-parse HEAD:random_file &&
+ test "$(git diff --name-only --cached E)" = "random_file"
+'
+
+test_expect_success 'ff update, important file modified' '
+ git reset --hard &&
+ git checkout A^0 &&
+
+ mkdir subdir &&
+ touch subdir/e &&
+ git add subdir/e &&
+
+ test_must_fail git merge E^0
+'
+
+test_expect_success 'resolve, trivial' '
+ git reset --hard &&
+ git checkout B^0 &&
+
+ touch random_file && git add random_file &&
+
+ test_must_fail git merge -s resolve C^0
+'
+
+test_expect_success 'resolve, non-trivial' '
+ git reset --hard &&
+ git checkout B^0 &&
+
+ touch random_file && git add random_file &&
+
+ test_must_fail git merge -s resolve D^0
+'
+
+test_expect_success 'recursive' '
+ git reset --hard &&
+ git checkout B^0 &&
+
+ touch random_file && git add random_file &&
+
+ test_must_fail git merge -s recursive C^0
+'
+
+test_expect_success 'octopus, unrelated file touched' '
+ git reset --hard &&
+ git checkout B^0 &&
+
+ touch random_file && git add random_file &&
+
+ test_must_fail git merge C^0 D^0
+'
+
+test_expect_success 'octopus, related file removed' '
+ git reset --hard &&
+ git checkout B^0 &&
+
+ git rm b &&
+
+ test_must_fail git merge C^0 D^0
+'
+
+test_expect_success 'octopus, related file modified' '
+ git reset --hard &&
+ git checkout B^0 &&
+
+ echo 12 >>a && git add a &&
+
+ test_must_fail git merge C^0 D^0
+'
+
+test_expect_success 'ours' '
+ git reset --hard &&
+ git checkout B^0 &&
+
+ touch random_file && git add random_file &&
+
+ test_must_fail git merge -s ours C^0
+'
+
+test_expect_success 'subtree' '
+ git reset --hard &&
+ git checkout B^0 &&
+
+ touch random_file && git add random_file &&
+
+ test_must_fail git merge -s subtree E^0
+'
+
+test_done
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
index 203ffdb..c630aba 100755
--- a/t/t6050-replace.sh
+++ b/t/t6050-replace.sh
@@ -7,8 +7,9 @@ test_description='Tests replace refs functionality'
exec </dev/null
. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
-add_and_commit_file()
+add_and_commit_file ()
{
_file="$1"
_msg="$2"
@@ -18,6 +19,38 @@ add_and_commit_file()
git commit --quiet -m "$_file: $_msg"
}
+commit_buffer_contains_parents ()
+{
+ git cat-file commit "$1" >payload &&
+ sed -n -e '/^$/q' -e '/^parent /p' <payload >actual &&
+ shift &&
+ for _parent
+ do
+ echo "parent $_parent"
+ done >expected &&
+ test_cmp expected actual
+}
+
+commit_peeling_shows_parents ()
+{
+ _parent_number=1
+ _commit="$1"
+ shift &&
+ for _parent
+ do
+ _found=$(git rev-parse --verify $_commit^$_parent_number) || return 1
+ test "$_found" = "$_parent" || return 1
+ _parent_number=$(( $_parent_number + 1 ))
+ done &&
+ test_must_fail git rev-parse --verify $_commit^$_parent_number
+}
+
+commit_has_parents ()
+{
+ commit_buffer_contains_parents "$@" &&
+ commit_peeling_shows_parents "$@"
+}
+
HASH1=
HASH2=
HASH3=
@@ -27,36 +60,36 @@ HASH6=
HASH7=
test_expect_success 'set up buggy branch' '
- echo "line 1" >> hello &&
- echo "line 2" >> hello &&
- echo "line 3" >> hello &&
- echo "line 4" >> hello &&
+ echo "line 1" >>hello &&
+ echo "line 2" >>hello &&
+ echo "line 3" >>hello &&
+ echo "line 4" >>hello &&
add_and_commit_file hello "4 lines" &&
HASH1=$(git rev-parse --verify HEAD) &&
- echo "line BUG" >> hello &&
- echo "line 6" >> hello &&
- echo "line 7" >> hello &&
- echo "line 8" >> hello &&
+ echo "line BUG" >>hello &&
+ echo "line 6" >>hello &&
+ echo "line 7" >>hello &&
+ echo "line 8" >>hello &&
add_and_commit_file hello "4 more lines with a BUG" &&
HASH2=$(git rev-parse --verify HEAD) &&
- echo "line 9" >> hello &&
- echo "line 10" >> hello &&
+ echo "line 9" >>hello &&
+ echo "line 10" >>hello &&
add_and_commit_file hello "2 more lines" &&
HASH3=$(git rev-parse --verify HEAD) &&
- echo "line 11" >> hello &&
+ echo "line 11" >>hello &&
add_and_commit_file hello "1 more line" &&
HASH4=$(git rev-parse --verify HEAD) &&
- sed -e "s/BUG/5/" hello > hello.new &&
+ sed -e "s/BUG/5/" hello >hello.new &&
mv hello.new hello &&
add_and_commit_file hello "BUG fixed" &&
HASH5=$(git rev-parse --verify HEAD) &&
- echo "line 12" >> hello &&
- echo "line 13" >> hello &&
+ echo "line 12" >>hello &&
+ echo "line 13" >>hello &&
add_and_commit_file hello "2 more lines" &&
- HASH6=$(git rev-parse --verify HEAD)
- echo "line 14" >> hello &&
- echo "line 15" >> hello &&
- echo "line 16" >> hello &&
+ 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)
'
@@ -95,7 +128,7 @@ test_expect_success 'tag replaced commit' '
'
test_expect_success '"git fsck" works' '
- git fsck master > fsck_master.out &&
+ git fsck master >fsck_master.out &&
grep "dangling commit $R" fsck_master.out &&
grep "dangling tag $(cat .git/refs/tags/mytag)" fsck_master.out &&
test -z "$(git fsck)"
@@ -104,26 +137,27 @@ test_expect_success '"git fsck" works' '
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 ..
+ (
+ 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
+ )
'
test_expect_success '"git replace" listing and deleting' '
test "$HASH2" = "$(git replace -l)" &&
test "$HASH2" = "$(git replace)" &&
aa=${HASH2%??????????????????????????????????????} &&
- test "$HASH2" = "$(git replace -l "$aa*")" &&
+ test "$HASH2" = "$(git replace --list "$aa*")" &&
test_must_fail git replace -d $R &&
- test_must_fail git replace -d &&
+ test_must_fail git replace --delete &&
test_must_fail git replace -l -d $HASH2 &&
git replace -d $HASH2 &&
git show $HASH2 | grep "A U Thor" &&
@@ -139,6 +173,17 @@ test_expect_success '"git replace" replacing' '
test "$HASH2" = "$(git replace)"
'
+test_expect_success '"git replace" resolves sha1' '
+ SHORTHASH2=$(git rev-parse --short=8 $HASH2) &&
+ git replace -d $SHORTHASH2 &&
+ git replace $SHORTHASH2 $R &&
+ git show $HASH2 | grep "O Thor" &&
+ test_must_fail git replace $HASH2 $R &&
+ git replace -f $HASH2 $R &&
+ test_must_fail git replace --force &&
+ test "$HASH2" = "$(git replace)"
+'
+
# This creates a side branch where the bug in H2
# does not appear because P2 is created by applying
# H2 and squashing H5 into it.
@@ -177,10 +222,11 @@ test_expect_success 'create parallel branch without the bug' '
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 ..
+ (
+ cd clone_dir &&
+ git checkout parallel &&
+ git log --pretty=oneline | grep $PARA2
+ )
'
test_expect_success 'push branch with replacement' '
@@ -191,25 +237,34 @@ test_expect_success 'push branch with replacement' '
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 ..
+ (
+ cd clone_dir &&
+ git checkout parallel2 &&
+ git log --pretty=oneline | grep $PARA3 &&
+ git show $PARA3 | grep "A U Thor"
+ )
'
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 ..
+ (
+ cd clone_dir &&
+ git fetch origin refs/heads/tofetch:refs/heads/parallel3 &&
+ git log --pretty=oneline parallel3 >output.txt &&
+ ! grep $PARA3 output.txt &&
+ git show $PARA3 >para3.txt &&
+ grep "A U Thor" para3.txt &&
+ git fetch origin "refs/replace/*:refs/replace/*" &&
+ git log --pretty=oneline parallel3 >output.txt &&
+ grep $PARA3 output.txt &&
+ git show $PARA3 >para3.txt &&
+ grep "O Thor" para3.txt
+ )
'
test_expect_success 'bisect and replacements' '
git bisect start $HASH7 $HASH1 &&
- test "$S" = "$(git rev-parse --verify HEAD)" &&
+ test "$PARA3" = "$(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)" &&
@@ -219,6 +274,174 @@ test_expect_success 'bisect and replacements' '
git bisect reset
'
-#
-#
+test_expect_success 'index-pack and replacements' '
+ git --no-replace-objects rev-list --objects HEAD |
+ git --no-replace-objects pack-objects test- &&
+ git index-pack test-*.pack
+'
+
+test_expect_success 'not just commits' '
+ echo replaced >file &&
+ git add file &&
+ REPLACED=$(git rev-parse :file) &&
+ mv file file.replaced &&
+
+ echo original >file &&
+ git add file &&
+ ORIGINAL=$(git rev-parse :file) &&
+ git update-ref refs/replace/$ORIGINAL $REPLACED &&
+ mv file file.original &&
+
+ git checkout file &&
+ test_cmp file.replaced file
+'
+
+test_expect_success 'replaced and replacement objects must be of the same type' '
+ test_must_fail git replace mytag $HASH1 &&
+ test_must_fail git replace HEAD^{tree} HEAD~1 &&
+ BLOB=$(git rev-parse :file) &&
+ test_must_fail git replace HEAD^ $BLOB
+'
+
+test_expect_success '-f option bypasses the type check' '
+ git replace -f mytag $HASH1 &&
+ git replace --force HEAD^{tree} HEAD~1 &&
+ git replace -f HEAD^ $BLOB
+'
+
+test_expect_success 'git cat-file --batch works on replace objects' '
+ git replace | grep $PARA3 &&
+ echo $PARA3 | git cat-file --batch
+'
+
+test_expect_success 'test --format bogus' '
+ test_must_fail git replace --format bogus >/dev/null 2>&1
+'
+
+test_expect_success 'test --format short' '
+ git replace --format=short >actual &&
+ git replace >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'test --format medium' '
+ H1=$(git --no-replace-objects rev-parse HEAD~1) &&
+ HT=$(git --no-replace-objects rev-parse HEAD^{tree}) &&
+ MYTAG=$(git --no-replace-objects rev-parse mytag) &&
+ {
+ echo "$H1 -> $BLOB" &&
+ echo "$BLOB -> $REPLACED" &&
+ echo "$HT -> $H1" &&
+ echo "$PARA3 -> $S" &&
+ echo "$MYTAG -> $HASH1"
+ } | sort >expected &&
+ git replace -l --format medium | sort >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'test --format long' '
+ {
+ echo "$H1 (commit) -> $BLOB (blob)" &&
+ echo "$BLOB (blob) -> $REPLACED (blob)" &&
+ echo "$HT (tree) -> $H1 (commit)" &&
+ echo "$PARA3 (commit) -> $S (commit)" &&
+ echo "$MYTAG (tag) -> $HASH1 (commit)"
+ } | sort >expected &&
+ git replace --format=long | sort >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'setup fake editors' '
+ write_script fakeeditor <<-\EOF &&
+ sed -e "s/A U Thor/A fake Thor/" "$1" >"$1.new"
+ mv "$1.new" "$1"
+ EOF
+ write_script failingfakeeditor <<-\EOF
+ ./fakeeditor "$@"
+ false
+ EOF
+'
+
+test_expect_success '--edit with and without already replaced object' '
+ test_must_fail env GIT_EDITOR=./fakeeditor git replace --edit "$PARA3" &&
+ GIT_EDITOR=./fakeeditor git replace --force --edit "$PARA3" &&
+ git replace -l | grep "$PARA3" &&
+ git cat-file commit "$PARA3" | grep "A fake Thor" &&
+ git replace -d "$PARA3" &&
+ GIT_EDITOR=./fakeeditor git replace --edit "$PARA3" &&
+ git replace -l | grep "$PARA3" &&
+ git cat-file commit "$PARA3" | grep "A fake Thor"
+'
+
+test_expect_success '--edit and change nothing or command failed' '
+ git replace -d "$PARA3" &&
+ test_must_fail env GIT_EDITOR=true git replace --edit "$PARA3" &&
+ test_must_fail env GIT_EDITOR="./failingfakeeditor" git replace --edit "$PARA3" &&
+ GIT_EDITOR=./fakeeditor git replace --edit "$PARA3" &&
+ git replace -l | grep "$PARA3" &&
+ git cat-file commit "$PARA3" | grep "A fake Thor"
+'
+
+test_expect_success 'replace ref cleanup' '
+ test -n "$(git replace)" &&
+ git replace -d $(git replace) &&
+ test -z "$(git replace)"
+'
+
+test_expect_success '--graft with and without already replaced object' '
+ test $(git log --oneline | wc -l) = 7 &&
+ git replace --graft $HASH5 &&
+ test $(git log --oneline | wc -l) = 3 &&
+ commit_has_parents $HASH5 &&
+ test_must_fail git replace --graft $HASH5 $HASH4 $HASH3 &&
+ git replace --force -g $HASH5 $HASH4 $HASH3 &&
+ commit_has_parents $HASH5 $HASH4 $HASH3 &&
+ git replace -d $HASH5
+'
+
+test_expect_success GPG 'set up a signed commit' '
+ echo "line 17" >>hello &&
+ echo "line 18" >>hello &&
+ git add hello &&
+ test_tick &&
+ git commit --quiet -S -m "hello: 2 more lines in a signed commit" &&
+ HASH8=$(git rev-parse --verify HEAD) &&
+ git verify-commit $HASH8
+'
+
+test_expect_success GPG '--graft with a signed commit' '
+ git cat-file commit $HASH8 >orig &&
+ git replace --graft $HASH8 &&
+ git cat-file commit $HASH8 >repl &&
+ commit_has_parents $HASH8 &&
+ test_must_fail git verify-commit $HASH8 &&
+ sed -n -e "/^tree /p" -e "/^author /p" -e "/^committer /p" orig >expected &&
+ echo >>expected &&
+ sed -e "/^$/q" repl >actual &&
+ test_cmp expected actual &&
+ git replace -d $HASH8
+'
+
+test_expect_success GPG 'set up a merge commit with a mergetag' '
+ git reset --hard HEAD &&
+ git checkout -b test_branch HEAD~2 &&
+ echo "line 1 from test branch" >>hello &&
+ echo "line 2 from test branch" >>hello &&
+ git add hello &&
+ test_tick &&
+ git commit -m "hello: 2 more lines from a test branch" &&
+ HASH9=$(git rev-parse --verify HEAD) &&
+ git tag -s -m "tag for testing with a mergetag" test_tag HEAD &&
+ git checkout master &&
+ git merge -s ours test_tag &&
+ HASH10=$(git rev-parse --verify HEAD) &&
+ git cat-file commit $HASH10 | grep "^mergetag object"
+'
+
+test_expect_success GPG '--graft on a commit with a mergetag' '
+ test_must_fail git replace --graft $HASH10 $HASH8^1 &&
+ git replace --graft $HASH10 $HASH8^1 $HASH9 &&
+ git replace -d $HASH10
+'
+
test_done
diff --git a/t/t6060-merge-index.sh b/t/t6060-merge-index.sh
new file mode 100755
index 0000000..debadbd
--- /dev/null
+++ b/t/t6060-merge-index.sh
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+test_description='basic git merge-index / git-merge-one-file tests'
+. ./test-lib.sh
+
+test_expect_success 'setup diverging branches' '
+ for i in 1 2 3 4 5 6 7 8 9 10; do
+ echo $i
+ done >file &&
+ git add file &&
+ git commit -m base &&
+ git tag base &&
+ sed s/2/two/ <file >tmp &&
+ mv tmp file &&
+ git commit -a -m two &&
+ git tag two &&
+ git checkout -b other HEAD^ &&
+ sed s/10/ten/ <file >tmp &&
+ mv tmp file &&
+ git commit -a -m ten &&
+ git tag ten
+'
+
+cat >expect-merged <<'EOF'
+1
+two
+3
+4
+5
+6
+7
+8
+9
+ten
+EOF
+
+test_expect_success 'read-tree does not resolve content merge' '
+ git read-tree -i -m base ten two &&
+ echo file >expect &&
+ git diff-files --name-only --diff-filter=U >unmerged &&
+ test_cmp expect unmerged
+'
+
+test_expect_success 'git merge-index git-merge-one-file resolves' '
+ git merge-index git-merge-one-file -a &&
+ git diff-files --name-only --diff-filter=U >unmerged &&
+ >expect &&
+ test_cmp expect unmerged &&
+ test_cmp expect-merged file &&
+ git cat-file blob :file >file-index &&
+ test_cmp expect-merged file-index
+'
+
+test_expect_success 'setup bare merge' '
+ git clone --bare . bare.git &&
+ (cd bare.git &&
+ GIT_INDEX_FILE=$PWD/merge.index &&
+ export GIT_INDEX_FILE &&
+ git read-tree -i -m base ten two
+ )
+'
+
+test_expect_success 'merge-one-file fails without a work tree' '
+ (cd bare.git &&
+ GIT_INDEX_FILE=$PWD/merge.index &&
+ export GIT_INDEX_FILE &&
+ test_must_fail git merge-index git-merge-one-file -a
+ )
+'
+
+test_expect_success 'merge-one-file respects GIT_WORK_TREE' '
+ (cd bare.git &&
+ mkdir work &&
+ GIT_WORK_TREE=$PWD/work &&
+ export GIT_WORK_TREE &&
+ GIT_INDEX_FILE=$PWD/merge.index &&
+ export GIT_INDEX_FILE &&
+ git merge-index git-merge-one-file -a &&
+ git cat-file blob :file >work/file-index
+ ) &&
+ test_cmp expect-merged bare.git/work/file &&
+ test_cmp expect-merged bare.git/work/file-index
+'
+
+test_expect_success 'merge-one-file respects core.worktree' '
+ mkdir subdir &&
+ git clone . subdir/child &&
+ (cd subdir &&
+ GIT_DIR=$PWD/child/.git &&
+ export GIT_DIR &&
+ git config core.worktree "$PWD/child" &&
+ git read-tree -i -m base ten two &&
+ git merge-index git-merge-one-file -a &&
+ git cat-file blob :file >file-index
+ ) &&
+ test_cmp expect-merged subdir/child/file &&
+ test_cmp expect-merged subdir/file-index
+'
+
+test_done
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index e673c25..1c6952d 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -6,39 +6,100 @@
test_description='Test git rev-parse with different parent options'
. ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
-
-date >path0
-git update-index --add path0
-save_tag tree git write-tree
-hide_error save_tag start unique_commit "start" tree
-save_tag second unique_commit "second" tree -p start
-hide_error save_tag start2 unique_commit "start2" tree
-save_tag two_parents unique_commit "next" tree -p second -p start2
-save_tag final unique_commit "final" tree -p two_parents
-
-test_expect_success 'start is valid' 'git rev-parse start | grep "^[0-9a-f]\{40\}$"'
-test_expect_success 'start^0' "test $(cat .git/refs/tags/start) = $(git rev-parse start^0)"
-test_expect_success 'start^1 not valid' "if git rev-parse --verify start^1; then false; else :; fi"
-test_expect_success 'second^1 = second^' "test $(git rev-parse second^1) = $(git rev-parse second^)"
-test_expect_success 'final^1^1^1' "test $(git rev-parse start) = $(git rev-parse final^1^1^1)"
-test_expect_success 'final^1^1^1 = final^^^' "test $(git rev-parse final^1^1^1) = $(git rev-parse final^^^)"
-test_expect_success 'final^1^2' "test $(git rev-parse start2) = $(git rev-parse final^1^2)"
-test_expect_success 'final^1^2 != final^1^1' "test $(git rev-parse final^1^2) != $(git rev-parse final^1^1)"
-test_expect_success 'final^1^3 not valid' "if git rev-parse --verify final^1^3; then false; else :; fi"
-test_expect_success '--verify start2^1' 'test_must_fail git rev-parse --verify start2^1'
-test_expect_success '--verify start2^0' 'git rev-parse --verify start2^0'
-test_expect_success 'final^1^@ = final^1^1 final^1^2' "test \"$(git rev-parse final^1^@)\" = \"$(git rev-parse final^1^1 final^1^2)\""
-test_expect_success 'final^1^! = final^1 ^final^1^1 ^final^1^2' "test \"$(git rev-parse final^1^\!)\" = \"$(git rev-parse final^1 ^final^1^1 ^final^1^2)\""
-
-test_expect_success 'repack for next test' 'git repack -a -d'
+
+test_cmp_rev_output () {
+ git rev-parse --verify "$1" >expect &&
+ eval "$2" >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'setup' '
+ test_commit start &&
+ test_commit second &&
+ git checkout --orphan tmp &&
+ test_commit start2 &&
+ git checkout master &&
+ git merge -m next --allow-unrelated-histories start2 &&
+ test_commit final &&
+
+ test_seq 40 |
+ while read i
+ do
+ git checkout --orphan "b$i" &&
+ test_tick &&
+ git commit --allow-empty -m "$i" &&
+ commit=$(git rev-parse --verify HEAD) &&
+ printf "$commit " >>.git/info/grafts
+ done
+'
+
+test_expect_success 'start is valid' '
+ git rev-parse start | grep "^[0-9a-f]\{40\}$"
+'
+
+test_expect_success 'start^0' '
+ test_cmp_rev_output tags/start "git rev-parse start^0"
+'
+
+test_expect_success 'start^1 not valid' '
+ test_must_fail git rev-parse --verify start^1
+'
+
+test_expect_success 'second^1 = second^' '
+ test_cmp_rev_output second^ "git rev-parse second^1"
+'
+
+test_expect_success 'final^1^1^1' '
+ test_cmp_rev_output start "git rev-parse final^1^1^1"
+'
+
+test_expect_success 'final^1^1^1 = final^^^' '
+ test_cmp_rev_output final^^^ "git rev-parse final^1^1^1"
+'
+
+test_expect_success 'final^1^2' '
+ test_cmp_rev_output start2 "git rev-parse final^1^2"
+'
+
+test_expect_success 'final^1^2 != final^1^1' '
+ test $(git rev-parse final^1^2) != $(git rev-parse final^1^1)
+'
+
+test_expect_success 'final^1^3 not valid' '
+ test_must_fail git rev-parse --verify final^1^3
+'
+
+test_expect_success '--verify start2^1' '
+ test_must_fail git rev-parse --verify start2^1
+'
+
+test_expect_success '--verify start2^0' '
+ git rev-parse --verify start2^0
+'
+
+test_expect_success 'final^1^@ = final^1^1 final^1^2' '
+ git rev-parse final^1^1 final^1^2 >expect &&
+ git rev-parse final^1^@ >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'final^1^! = final^1 ^final^1^1 ^final^1^2' '
+ git rev-parse final^1 ^final^1^1 ^final^1^2 >expect &&
+ git rev-parse final^1^! >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'large graft octopus' '
+ test_cmp_rev_output b31 "git rev-parse --verify b1^30"
+'
+
+test_expect_success 'repack for next test' '
+ git repack -a -d
+'
+
test_expect_success 'short SHA-1 works' '
- start=`git rev-parse --verify start` &&
- echo $start &&
- abbrv=`echo $start | sed s/.\$//` &&
- echo $abbrv &&
- abbrv=`git rev-parse --verify $abbrv` &&
- echo $abbrv &&
- test $start = $abbrv'
+ start=$(git rev-parse --verify start) &&
+ test_cmp_rev_output start "git rev-parse ${start%?}"
+'
test_done
diff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh
new file mode 100755
index 0000000..656ac7f
--- /dev/null
+++ b/t/t6110-rev-list-sparse.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+test_description='operations that cull histories in unusual ways'
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit A &&
+ test_commit B &&
+ test_commit C &&
+ git checkout -b side HEAD^ &&
+ test_commit D &&
+ test_commit E &&
+ git merge master
+'
+
+test_expect_success 'rev-list --first-parent --boundary' '
+ git rev-list --first-parent --boundary HEAD^..
+'
+
+test_done
diff --git a/t/t6111-rev-list-treesame.sh b/t/t6111-rev-list-treesame.sh
new file mode 100755
index 0000000..32474c2
--- /dev/null
+++ b/t/t6111-rev-list-treesame.sh
@@ -0,0 +1,193 @@
+#!/bin/sh
+#
+# ,---E--. *H----------. * marks !TREESAME parent paths
+# / \ / \*
+# *A--*B---D--*F-*G---------K-*L-*M
+# \ /* \ /
+# `-C-' `-*I-*J
+#
+# A creates "file", B and F change it.
+# Odd merge G takes the old version from B.
+# I changes it, but J reverts it, so K is TREESAME to both parents.
+# H and L both change "file", and M merges those changes.
+
+test_description='TREESAME and limiting'
+
+. ./test-lib.sh
+
+note () {
+ git tag "$1"
+}
+
+unnote () {
+ git name-rev --tags --stdin | sed -e "s|$_x40 (tags/\([^)]*\))\([ ]\)|\1\2|g"
+}
+
+test_expect_success setup '
+ test_commit "Initial file" file "Hi there" A &&
+ git branch other-branch &&
+
+ test_commit "file=Hello" file "Hello" B &&
+ git branch third-branch &&
+
+ git checkout other-branch &&
+ test_commit "Added other" other "Hello" C &&
+
+ git checkout master &&
+ test_merge D other-branch &&
+
+ git checkout third-branch &&
+ test_commit "Third file" third "Nothing" E &&
+
+ git checkout master &&
+ test_commit "file=Blah" file "Blah" F &&
+
+ test_tick && git merge --no-commit third-branch &&
+ git checkout third-branch file &&
+ git commit &&
+ note G &&
+ git branch fiddler-branch &&
+
+ git checkout -b part2-branch &&
+ test_commit "file=Part 2" file "Part 2" H &&
+
+ git checkout fiddler-branch &&
+ test_commit "Bad commit" file "Silly" I &&
+
+ test_tick && git revert I && note J &&
+
+ git checkout master &&
+ test_tick && git merge --no-ff fiddler-branch &&
+ note K &&
+
+ test_commit "file=Part 1" file "Part 1" L &&
+
+ test_tick && test_must_fail git merge part2-branch &&
+ test_commit M file "Parts 1+2"
+'
+
+check_outcome () {
+ outcome=$1
+ shift
+
+ case "$1" in
+ *"("*)
+ FMT="%P %H | %s"
+ munge_actual="
+ s/^\([^ ]*\) \([^ ]*\) .*/(\1)\2/
+ s/ //g
+ s/()//
+ "
+ ;;
+ *)
+ FMT="%H | %s"
+ munge_actual="s/^\([^ ]*\) .*/\1/"
+ ;;
+ esac &&
+ printf "%s\n" $1 >expect &&
+ shift
+
+ param="$*" &&
+ test_expect_$outcome "log $param" '
+ git log --format="$FMT" $param |
+ unnote >actual &&
+ sed -e "$munge_actual" <actual >check &&
+ test_cmp expect check
+ '
+}
+
+check_result () {
+ check_outcome success "$@"
+}
+
+# Odd merge G drops a change in F. Important that G is listed in all
+# except the most basic list. Achieving this means normal merge D will also be
+# shown in normal full-history, as we can't distinguish unless we do a
+# simplification pass. After simplification, D is dropped but G remains.
+# Also, merge simplification of G should not drop the parent B that the default
+# simple history follows.
+check_result 'M L K J I H G F E D C B A'
+check_result '(LH)M (K)L (GJ)K (I)J (G)I (G)H (FE)G (D)F (B)E (BC)D (A)C (A)B A'
+check_result 'M H L K J I G E F D C B A' --topo-order
+check_result 'M L H B A' -- file
+check_result '(LH)M (B)L (B)H (A)B A' --parents -- file
+check_result 'M L J I H G F D B A' --full-history -- file
+check_result '(LH)M (K)L (GJ)K (I)J (G)I (G)H (FB)G (D)F (BA)D (A)B A' --full-history --parents -- file
+check_result '(LH)M (G)H (J)L (I)J (G)I (FB)G (B)F (A)B A' --simplify-merges -- file
+check_result 'M L K G F D B A' --first-parent
+check_result 'M L G F B A' --first-parent -- file
+
+# Check that odd merge G remains shown when F is the bottom.
+check_result 'M L K J I H G E' F..M
+check_result 'M H L K J I G E' F..M --topo-order
+check_result 'M L H' F..M -- file
+check_result '(LH)M (B)L (B)H' --parents F..M -- file
+check_result 'M L J I H G' F..M --full-history -- file
+check_result '(LH)M (K)L (GJ)K (I)J (G)I (G)H (FB)G' F..M --full-history --parents -- file
+check_result '(LH)M (G)H (J)L (I)J (G)I (FB)G' F..M --simplify-merges -- file
+check_result 'M L K J I H G' F..M --ancestry-path
+check_result 'M L J I H G' F..M --ancestry-path -- file
+check_result '(LH)M (K)L (GJ)K (I)J (G)I (G)H (FE)G' F..M --ancestry-path --parents -- file
+check_result '(LH)M (G)H (J)L (I)J (G)I (FE)G' F..M --ancestry-path --simplify-merges -- file
+check_result 'M L K G' F..M --first-parent
+check_result 'M L G' F..M --first-parent -- file
+
+# Note that G is pruned when E is the bottom, even if it's the same commit list
+# If we want history since E, then we're quite happy to ignore G that took E.
+check_result 'M L K J I H G' E..M --ancestry-path
+check_result 'M L J I H' E..M --ancestry-path -- file
+check_result '(LH)M (K)L (EJ)K (I)J (E)I (E)H' E..M --ancestry-path --parents -- file
+check_result '(LH)M (E)H (J)L (I)J (E)I' E..M --ancestry-path --simplify-merges -- file
+
+# Should still be able to ignore I-J branch in simple log, despite limiting
+# to G.
+check_result 'M L K J I H' G..M
+check_result 'M H L K J I' G..M --topo-order
+check_result 'M L H' G..M -- file
+check_result '(LH)M (G)L (G)H' G..M --parents -- file
+check_result 'M L J I H' G..M --full-history -- file
+check_result 'M L K J I H' G..M --full-history --parents -- file
+check_result 'M H L J I' G..M --simplify-merges -- file
+check_result 'M L K J I H' G..M --ancestry-path
+check_result 'M L J I H' G..M --ancestry-path -- file
+check_result 'M L K J I H' G..M --ancestry-path --parents -- file
+check_result 'M H L J I' G..M --ancestry-path --simplify-merges -- file
+
+# B..F should be able to simplify the merge D from irrelevant side branch C.
+# Default log should also be free to follow B-D, and ignore C.
+# But --full-history shouldn't drop D on its own - without simplification,
+# we can't decide if the merge from INTERESTING commit C was sensible.
+check_result 'F D C' B..F
+check_result 'F' B..F -- file
+check_result '(B)F' B..F --parents -- file
+check_result 'F D' B..F --full-history -- file
+check_result '(D)F (BA)D' B..F --full-history --parents -- file
+check_result '(B)F' B..F --simplify-merges -- file
+check_result 'F D' B..F --ancestry-path
+check_result 'F' B..F --ancestry-path -- file
+check_result 'F' B..F --ancestry-path --parents -- file
+check_result 'F' B..F --ancestry-path --simplify-merges -- file
+check_result 'F D' B..F --first-parent
+check_result 'F' B..F --first-parent -- file
+
+# E...F should be equivalent to E F ^B, and be able to drop D as above.
+check_result 'F' E F ^B -- file # includes D
+check_result 'F' E...F -- file # includes D
+
+# Any sort of full history of C..F should show D, as it's the connection to C,
+# and it differs from it.
+check_result 'F D B' C..F
+check_result 'F B' C..F -- file
+check_result '(B)F (A)B' C..F --parents -- file
+check_result 'F D B' C..F --full-history -- file
+check_result '(D)F (BC)D (A)B' C..F --full-history --parents -- file
+check_result '(D)F (BC)D (A)B' C..F --simplify-merges -- file
+check_result 'F D' C..F --ancestry-path
+check_result 'F D' C..F --ancestry-path -- file
+check_result 'F D' C..F --ancestry-path --parents -- file
+check_result 'F D' C..F --ancestry-path --simplify-merges -- file
+check_result 'F D B' C..F --first-parent
+check_result 'F B' C..F --first-parent -- file
+
+
+test_done
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 876d1ab..85f2694 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -110,6 +110,17 @@ check_describe tags/e --all HEAD^^^
check_describe B-0-* --long HEAD^^2^
check_describe A-3-* --long HEAD^^2
+check_describe c-7-* --tags
+check_describe e-3-* --first-parent --tags
+
+test_expect_success 'describe --contains defaults to HEAD without commit-ish' '
+ echo "A^0" >expect &&
+ git checkout A &&
+ test_when_finished "git checkout -" &&
+ git describe --contains >actual &&
+ test_cmp expect actual
+'
+
: >err.expect
check_describe A --all A^0
test_expect_success 'no warning was displayed for A' '
@@ -124,7 +135,7 @@ 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_i18ncmp err.expect err.actual
'
test_expect_success 'rename tag Q back to A' '
mv .git/refs/tags/Q .git/refs/tags/A
@@ -171,4 +182,28 @@ check_describe "test2-lightweight-*" --tags --match="test2-*"
check_describe "test2-lightweight-*" --long --tags --match="test2-*" HEAD^
+test_expect_success 'name-rev with exact tags' '
+ echo A >expect &&
+ tag_object=$(git rev-parse refs/tags/A) &&
+ git name-rev --tags --name-only $tag_object >actual &&
+ test_cmp expect actual &&
+
+ echo "A^0" >expect &&
+ tagged_commit=$(git rev-parse "refs/tags/A^0") &&
+ git name-rev --tags --name-only $tagged_commit >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'describe --contains with the exact tags' '
+ echo "A^0" >expect &&
+ tag_object=$(git rev-parse refs/tags/A) &&
+ git describe --contains $tag_object >actual &&
+ test_cmp expect actual &&
+
+ echo "A^0" >expect &&
+ tagged_commit=$(git rev-parse "refs/tags/A^0") &&
+ git describe --contains $tagged_commit >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t6130-pathspec-noglob.sh b/t/t6130-pathspec-noglob.sh
new file mode 100755
index 0000000..6583532
--- /dev/null
+++ b/t/t6130-pathspec-noglob.sh
@@ -0,0 +1,162 @@
+#!/bin/sh
+
+test_description='test globbing (and noglob) of pathspec limiting'
+. ./test-lib.sh
+
+test_expect_success 'create commits with glob characters' '
+ test_commit unrelated bar &&
+ test_commit vanilla foo &&
+ # insert file "f*" in the commit, but in a way that avoids
+ # the name "f*" in the worktree, because it is not allowed
+ # on Windows (the tests below do not depend on the presence
+ # of the file in the worktree)
+ git update-index --add --cacheinfo 100644 "$(git rev-parse HEAD:foo)" "f*" &&
+ test_tick &&
+ git commit -m star &&
+ test_commit bracket "f[o][o]"
+'
+
+test_expect_success 'vanilla pathspec matches literally' '
+ echo vanilla >expect &&
+ git log --format=%s -- foo >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'star pathspec globs' '
+ cat >expect <<-\EOF &&
+ bracket
+ star
+ vanilla
+ EOF
+ git log --format=%s -- "f*" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'star pathspec globs' '
+ cat >expect <<-\EOF &&
+ bracket
+ star
+ vanilla
+ EOF
+ git log --format=%s -- ":(glob)f*" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'bracket pathspec globs and matches literal brackets' '
+ cat >expect <<-\EOF &&
+ bracket
+ vanilla
+ EOF
+ git log --format=%s -- "f[o][o]" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'bracket pathspec globs and matches literal brackets' '
+ cat >expect <<-\EOF &&
+ bracket
+ vanilla
+ EOF
+ git log --format=%s -- ":(glob)f[o][o]" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-glob option matches literally (vanilla)' '
+ echo vanilla >expect &&
+ git --literal-pathspecs log --format=%s -- foo >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-glob option matches literally (vanilla)' '
+ echo vanilla >expect &&
+ git log --format=%s -- ":(literal)foo" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-glob option matches literally (star)' '
+ echo star >expect &&
+ git --literal-pathspecs log --format=%s -- "f*" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-glob option matches literally (star)' '
+ echo star >expect &&
+ git log --format=%s -- ":(literal)f*" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-glob option matches literally (bracket)' '
+ echo bracket >expect &&
+ git --literal-pathspecs log --format=%s -- "f[o][o]" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-glob option matches literally (bracket)' '
+ echo bracket >expect &&
+ git log --format=%s -- ":(literal)f[o][o]" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-glob option disables :(literal)' '
+ : >expect &&
+ git --literal-pathspecs log --format=%s -- ":(literal)foo" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-glob environment variable works' '
+ echo star >expect &&
+ GIT_LITERAL_PATHSPECS=1 git log --format=%s -- "f*" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'blame takes global pathspec flags' '
+ git --literal-pathspecs blame -- foo &&
+ git --icase-pathspecs blame -- foo &&
+ git --glob-pathspecs blame -- foo &&
+ git --noglob-pathspecs blame -- foo
+'
+
+test_expect_success 'setup xxx/bar' '
+ mkdir xxx &&
+ test_commit xxx xxx/bar
+'
+
+test_expect_success '**/ works with :(glob)' '
+ cat >expect <<-\EOF &&
+ xxx
+ unrelated
+ EOF
+ git log --format=%s -- ":(glob)**/bar" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '**/ does not work with --noglob-pathspecs' '
+ : >expect &&
+ git --noglob-pathspecs log --format=%s -- "**/bar" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '**/ works with :(glob) and --noglob-pathspecs' '
+ cat >expect <<-\EOF &&
+ xxx
+ unrelated
+ EOF
+ git --noglob-pathspecs log --format=%s -- ":(glob)**/bar" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '**/ works with --glob-pathspecs' '
+ cat >expect <<-\EOF &&
+ xxx
+ unrelated
+ EOF
+ git --glob-pathspecs log --format=%s -- "**/bar" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '**/ does not work with :(literal) and --glob-pathspecs' '
+ : >expect &&
+ git --glob-pathspecs log --format=%s -- ":(literal)**/bar" >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh
new file mode 100755
index 0000000..39fc3f6
--- /dev/null
+++ b/t/t6131-pathspec-icase.sh
@@ -0,0 +1,109 @@
+#!/bin/sh
+
+test_description='test case insensitive pathspec limiting'
+. ./test-lib.sh
+
+if test_have_prereq CASE_INSENSITIVE_FS
+then
+ skip_all='skipping case sensitive tests - case insensitive file system'
+ test_done
+fi
+
+test_expect_success 'create commits with glob characters' '
+ test_commit bar bar &&
+ test_commit bAr bAr &&
+ test_commit BAR BAR &&
+ mkdir foo &&
+ test_commit foo/bar foo/bar &&
+ test_commit foo/bAr foo/bAr &&
+ test_commit foo/BAR foo/BAR &&
+ mkdir fOo &&
+ test_commit fOo/bar fOo/bar &&
+ test_commit fOo/bAr fOo/bAr &&
+ test_commit fOo/BAR fOo/BAR &&
+ mkdir FOO &&
+ test_commit FOO/bar FOO/bar &&
+ test_commit FOO/bAr FOO/bAr &&
+ test_commit FOO/BAR FOO/BAR
+'
+
+test_expect_success 'tree_entry_interesting matches bar' '
+ echo bar >expect &&
+ git log --format=%s -- "bar" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'tree_entry_interesting matches :(icase)bar' '
+ cat <<-EOF >expect &&
+ BAR
+ bAr
+ bar
+ EOF
+ git log --format=%s -- ":(icase)bar" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'tree_entry_interesting matches :(icase)bar with prefix' '
+ cat <<-EOF >expect &&
+ fOo/BAR
+ fOo/bAr
+ fOo/bar
+ EOF
+ ( cd fOo && git log --format=%s -- ":(icase)bar" ) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'tree_entry_interesting matches :(icase)bar with empty prefix' '
+ cat <<-EOF >expect &&
+ FOO/BAR
+ FOO/bAr
+ FOO/bar
+ fOo/BAR
+ fOo/bAr
+ fOo/bar
+ foo/BAR
+ foo/bAr
+ foo/bar
+ EOF
+ ( cd fOo && git log --format=%s -- ":(icase)../foo/bar" ) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'match_pathspec matches :(icase)bar' '
+ cat <<-EOF >expect &&
+ BAR
+ bAr
+ bar
+ EOF
+ git ls-files ":(icase)bar" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'match_pathspec matches :(icase)bar with prefix' '
+ cat <<-EOF >expect &&
+ fOo/BAR
+ fOo/bAr
+ fOo/bar
+ EOF
+ ( cd fOo && git ls-files --full-name ":(icase)bar" ) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'match_pathspec matches :(icase)bar with empty prefix' '
+ cat <<-EOF >expect &&
+ bar
+ fOo/BAR
+ fOo/bAr
+ fOo/bar
+ EOF
+ ( cd fOo && git ls-files --full-name ":(icase)bar" ../bar ) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '"git diff" can take magic :(icase) pathspec' '
+ echo FOO/BAR >expect &&
+ git diff --name-only HEAD^ HEAD -- ":(icase)foo/bar" >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t6132-pathspec-exclude.sh b/t/t6132-pathspec-exclude.sh
new file mode 100755
index 0000000..d51595c
--- /dev/null
+++ b/t/t6132-pathspec-exclude.sh
@@ -0,0 +1,184 @@
+#!/bin/sh
+
+test_description='test case exclude pathspec'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ for p in file sub/file sub/sub/file sub/file2 sub/sub/sub/file sub2/file; do
+ if echo $p | grep /; then
+ mkdir -p $(dirname $p)
+ fi &&
+ : >$p &&
+ git add $p &&
+ git commit -m $p
+ done &&
+ git log --oneline --format=%s >actual &&
+ cat <<EOF >expect &&
+sub2/file
+sub/sub/sub/file
+sub/file2
+sub/sub/file
+sub/file
+file
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'exclude only should error out' '
+ test_must_fail git log --oneline --format=%s -- ":(exclude)sub"
+'
+
+test_expect_success 't_e_i() exclude sub' '
+ git log --oneline --format=%s -- . ":(exclude)sub" >actual &&
+ cat <<EOF >expect &&
+sub2/file
+file
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 't_e_i() exclude sub/sub/file' '
+ git log --oneline --format=%s -- . ":(exclude)sub/sub/file" >actual &&
+ cat <<EOF >expect &&
+sub2/file
+sub/sub/sub/file
+sub/file2
+sub/file
+file
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 't_e_i() exclude sub using mnemonic' '
+ git log --oneline --format=%s -- . ":!sub" >actual &&
+ cat <<EOF >expect &&
+sub2/file
+file
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 't_e_i() exclude :(icase)SUB' '
+ git log --oneline --format=%s -- . ":(exclude,icase)SUB" >actual &&
+ cat <<EOF >expect &&
+sub2/file
+file
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 't_e_i() exclude sub2 from sub' '
+ (
+ cd sub &&
+ git log --oneline --format=%s -- :/ ":/!sub2" >actual &&
+ cat <<EOF >expect &&
+sub/sub/sub/file
+sub/file2
+sub/sub/file
+sub/file
+file
+EOF
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 't_e_i() exclude sub/*file' '
+ git log --oneline --format=%s -- . ":(exclude)sub/*file" >actual &&
+ cat <<EOF >expect &&
+sub2/file
+sub/file2
+file
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 't_e_i() exclude :(glob)sub/*/file' '
+ git log --oneline --format=%s -- . ":(exclude,glob)sub/*/file" >actual &&
+ cat <<EOF >expect &&
+sub2/file
+sub/sub/sub/file
+sub/file2
+sub/file
+file
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude sub' '
+ git ls-files -- . ":(exclude)sub" >actual &&
+ cat <<EOF >expect &&
+file
+sub2/file
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude sub/sub/file' '
+ git ls-files -- . ":(exclude)sub/sub/file" >actual &&
+ cat <<EOF >expect &&
+file
+sub/file
+sub/file2
+sub/sub/sub/file
+sub2/file
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude sub using mnemonic' '
+ git ls-files -- . ":!sub" >actual &&
+ cat <<EOF >expect &&
+file
+sub2/file
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude :(icase)SUB' '
+ git ls-files -- . ":(exclude,icase)SUB" >actual &&
+ cat <<EOF >expect &&
+file
+sub2/file
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude sub2 from sub' '
+ (
+ cd sub &&
+ git ls-files -- :/ ":/!sub2" >actual &&
+ cat <<EOF >expect &&
+../file
+file
+file2
+sub/file
+sub/sub/file
+EOF
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'm_p_d() exclude sub/*file' '
+ git ls-files -- . ":(exclude)sub/*file" >actual &&
+ cat <<EOF >expect &&
+file
+sub/file2
+sub2/file
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude :(glob)sub/*/file' '
+ git ls-files -- . ":(exclude,glob)sub/*/file" >actual &&
+ cat <<EOF >expect &&
+file
+sub/file
+sub/file2
+sub/sub/sub/file
+sub2/file
+EOF
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t6133-pathspec-rev-dwim.sh b/t/t6133-pathspec-rev-dwim.sh
new file mode 100755
index 0000000..a290ffc
--- /dev/null
+++ b/t/t6133-pathspec-rev-dwim.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='test dwim of revs versus pathspecs in revision parser'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit base &&
+ echo content >"br[ack]ets" &&
+ git add . &&
+ test_tick &&
+ git commit -m brackets
+'
+
+test_expect_success 'non-rev wildcard dwims to pathspec' '
+ git log -- "*.t" >expect &&
+ git log "*.t" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'tree:path with metacharacters dwims to rev' '
+ git show "HEAD:br[ack]ets" -- >expect &&
+ git show "HEAD:br[ack]ets" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '^{foo} with metacharacters dwims to rev' '
+ git log "HEAD^{/b.*}" -- >expect &&
+ git log "HEAD^{/b.*}" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '@{foo} with metacharacters dwims to rev' '
+ git log "HEAD@{now [or thereabouts]}" -- >expect &&
+ git log "HEAD@{now [or thereabouts]}" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success ':/*.t from a subdir dwims to a pathspec' '
+ mkdir subdir &&
+ (
+ cd subdir &&
+ git log -- ":/*.t" >expect &&
+ git log ":/*.t" >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_done
diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh
index 42f8ece..2e2fb0e 100755
--- a/t/t6200-fmt-merge-msg.sh
+++ b/t/t6200-fmt-merge-msg.sh
@@ -35,15 +35,18 @@ test_expect_success setup '
echo "l3" >two &&
test_tick &&
- git commit -a -m "Left #3" &&
+ GIT_COMMITTER_NAME="Another Committer" \
+ GIT_AUTHOR_NAME="Another Author" git commit -a -m "Left #3" &&
echo "l4" >two &&
test_tick &&
- git commit -a -m "Left #4" &&
+ GIT_COMMITTER_NAME="Another Committer" \
+ GIT_AUTHOR_NAME="Another Author" git commit -a -m "Left #4" &&
echo "l5" >two &&
test_tick &&
- git commit -a -m "Left #5" &&
+ GIT_COMMITTER_NAME="Another Committer" \
+ GIT_AUTHOR_NAME="Another Author" git commit -a -m "Left #5" &&
git tag tag-l5 &&
git checkout right &&
@@ -70,14 +73,13 @@ test_expect_success setup '
i=$(($i+1))
done &&
- git show-branch
-'
+ git show-branch &&
-cat >expected <<\EOF
-Merge branch 'left'
-EOF
+ apos="'\''"
+'
-test_expect_success 'merge-msg test #1' '
+test_expect_success 'message for merging local branch' '
+ echo "Merge branch ${apos}left${apos}" >expected &&
git checkout master &&
git fetch . left &&
@@ -86,11 +88,8 @@ test_expect_success 'merge-msg test #1' '
test_cmp expected actual
'
-cat >expected <<EOF
-Merge branch 'left' of $(pwd)
-EOF
-
-test_expect_success 'merge-msg test #2' '
+test_expect_success 'message for merging external branch' '
+ echo "Merge branch ${apos}left${apos} of $(pwd)" >expected &&
git checkout master &&
git fetch "$(pwd)" left &&
@@ -99,139 +98,258 @@ test_expect_success 'merge-msg test #2' '
test_cmp expected actual
'
-cat >expected <<\EOF
-Merge branch 'left'
+test_expect_success '[merge] summary/log configuration' '
+ cat >expected <<-EOF &&
+ Merge branch ${apos}left${apos}
-* left:
- Left #5
- Left #4
- Left #3
- Common #2
- Common #1
-EOF
+ # By Another Author (3) and A U Thor (2)
+ # Via Another Committer
+ * 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 &&
+ test_config merge.log true &&
+ test_unconfig merge.summary &&
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 fmt-merge-msg <.git/FETCH_HEAD >actual1 &&
- git config --unset-all merge.log
- git config --unset-all merge.summary
- git config merge.summary true &&
+ test_unconfig merge.log &&
+ test_config merge.summary true &&
git checkout master &&
test_tick &&
git fetch . left &&
- git fmt-merge-msg <.git/FETCH_HEAD >actual &&
- test_cmp expected actual
+ git fmt-merge-msg <.git/FETCH_HEAD >actual2 &&
+
+ test_cmp expected actual1 &&
+ test_cmp expected actual2
'
-cat >expected <<\EOF
-Merge branches 'left' and 'right'
+test_expect_success 'setup FETCH_HEAD' '
+ git checkout master &&
+ test_tick &&
+ git fetch . left
+'
-* left:
- Left #5
- Left #4
- Left #3
- Common #2
- Common #1
+test_expect_success 'merge.log=3 limits shortlog length' '
+ cat >expected <<-EOF &&
+ Merge branch ${apos}left${apos}
-* right:
- Right #5
- Right #4
- Right #3
- Common #2
- Common #1
-EOF
+ # By Another Author (3) and A U Thor (2)
+ # Via Another Committer
+ * left: (5 commits)
+ Left #5
+ Left #4
+ Left #3
+ ...
+ EOF
-test_expect_success 'merge-msg test #4-1' '
+ git -c merge.log=3 fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
- git config --unset-all merge.log
- git config --unset-all merge.summary
- git config merge.log true &&
+test_expect_success 'merge.log=5 shows all 5 commits' '
+ cat >expected <<-EOF &&
+ Merge branch ${apos}left${apos}
+
+ # By Another Author (3) and A U Thor (2)
+ # Via Another Committer
+ * left:
+ Left #5
+ Left #4
+ Left #3
+ Common #2
+ Common #1
+ EOF
+
+ git -c merge.log=5 fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
- git checkout master &&
- test_tick &&
- git fetch . left right &&
+test_expect_success '--log=5 with custom comment character' '
+ cat >expected <<-EOF &&
+ Merge branch ${apos}left${apos}
+
+ x By Another Author (3) and A U Thor (2)
+ x Via Another Committer
+ * left:
+ Left #5
+ Left #4
+ Left #3
+ Common #2
+ Common #1
+ EOF
+
+ git -c core.commentchar="x" fmt-merge-msg --log=5 <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
- git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+test_expect_success 'merge.log=0 disables shortlog' '
+ echo "Merge branch ${apos}left${apos}" >expected &&
+ git -c merge.log=0 fmt-merge-msg <.git/FETCH_HEAD >actual &&
test_cmp expected actual
'
-test_expect_success 'merge-msg test #4-2' '
+test_expect_success '--log=3 limits shortlog length' '
+ cat >expected <<-EOF &&
+ Merge branch ${apos}left${apos}
- git config --unset-all merge.log
- git config --unset-all merge.summary
- git config merge.summary true &&
+ # By Another Author (3) and A U Thor (2)
+ # Via Another Committer
+ * left: (5 commits)
+ Left #5
+ Left #4
+ Left #3
+ ...
+ EOF
- git checkout master &&
- test_tick &&
- git fetch . left right &&
+ git fmt-merge-msg --log=3 <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
- git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+test_expect_success '--log=5 shows all 5 commits' '
+ cat >expected <<-EOF &&
+ Merge branch ${apos}left${apos}
+
+ # By Another Author (3) and A U Thor (2)
+ # Via Another Committer
+ * left:
+ Left #5
+ Left #4
+ Left #3
+ Common #2
+ Common #1
+ EOF
+
+ git fmt-merge-msg --log=5 <.git/FETCH_HEAD >actual &&
test_cmp expected actual
'
-test_expect_success 'merge-msg test #5-1' '
+test_expect_success '--no-log disables shortlog' '
+ echo "Merge branch ${apos}left${apos}" >expected &&
+ git fmt-merge-msg --no-log <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
- git config --unset-all merge.log
- git config --unset-all merge.summary
- git config merge.log yes &&
+test_expect_success '--log=0 disables shortlog' '
+ echo "Merge branch ${apos}left${apos}" >expected &&
+ git fmt-merge-msg --no-log <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+test_expect_success 'fmt-merge-msg -m' '
+ echo "Sync with left" >expected &&
+ cat >expected.log <<-EOF &&
+ Sync with left
+
+ # By Another Author (3) and A U Thor (2)
+ # Via Another Committer
+ * ${apos}left${apos} of $(pwd):
+ Left #5
+ Left #4
+ Left #3
+ Common #2
+ Common #1
+ EOF
+
+ test_unconfig merge.log &&
+ test_unconfig merge.summary &&
git checkout master &&
- test_tick &&
- git fetch . left right &&
+ git fetch "$(pwd)" left &&
+ git fmt-merge-msg -m "Sync with left" <.git/FETCH_HEAD >actual &&
+ git fmt-merge-msg --log -m "Sync with left" \
+ <.git/FETCH_HEAD >actual.log &&
+ test_config merge.log true &&
+ git fmt-merge-msg -m "Sync with left" \
+ <.git/FETCH_HEAD >actual.log-config &&
+ git fmt-merge-msg --no-log -m "Sync with left" \
+ <.git/FETCH_HEAD >actual.nolog &&
+
+ test_cmp expected actual &&
+ test_cmp expected.log actual.log &&
+ test_cmp expected.log actual.log-config &&
+ test_cmp expected actual.nolog
+'
- git fmt-merge-msg <.git/FETCH_HEAD >actual &&
- test_cmp expected actual
+test_expect_success 'setup: expected shortlog for two branches' '
+ cat >expected <<-EOF
+ Merge branches ${apos}left${apos} and ${apos}right${apos}
+
+ # By Another Author (3) and A U Thor (2)
+ # Via Another Committer
+ * left:
+ Left #5
+ Left #4
+ Left #3
+ Common #2
+ Common #1
+
+ * right:
+ Right #5
+ Right #4
+ Right #3
+ Common #2
+ Common #1
+ EOF
'
-test_expect_success 'merge-msg test #5-2' '
+test_expect_success 'shortlog for two branches' '
+ test_config merge.log true &&
+ test_unconfig merge.summary &&
+ git checkout master &&
+ test_tick &&
+ git fetch . left right &&
+ git fmt-merge-msg <.git/FETCH_HEAD >actual1 &&
- git config --unset-all merge.log
- git config --unset-all merge.summary
- git config merge.summary yes &&
+ test_unconfig merge.log &&
+ test_config merge.summary true &&
+ git checkout master &&
+ test_tick &&
+ git fetch . left right &&
+ git fmt-merge-msg <.git/FETCH_HEAD >actual2 &&
+ test_config merge.log yes &&
+ test_unconfig merge.summary &&
git checkout master &&
test_tick &&
git fetch . left right &&
+ git fmt-merge-msg <.git/FETCH_HEAD >actual3 &&
- git fmt-merge-msg <.git/FETCH_HEAD >actual &&
- test_cmp expected actual
+ test_unconfig merge.log &&
+ test_config merge.summary yes &&
+ git checkout master &&
+ test_tick &&
+ git fetch . left right &&
+ git fmt-merge-msg <.git/FETCH_HEAD >actual4 &&
+
+ test_cmp expected actual1 &&
+ test_cmp expected actual2 &&
+ test_cmp expected actual3 &&
+ test_cmp expected actual4
'
test_expect_success 'merge-msg -F' '
-
- git config --unset-all merge.log
- git config --unset-all merge.summary
- git config merge.summary yes &&
-
+ test_unconfig merge.log &&
+ test_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 &&
-
+ test_unconfig merge.log &&
+ test_config merge.summary yes &&
git checkout master &&
test_tick &&
git fetch . left right &&
@@ -245,10 +363,10 @@ test_expect_success 'merge-msg -F in subdirectory' '
'
test_expect_success 'merge-msg with nothing to merge' '
+ test_unconfig merge.log &&
+ test_config merge.summary yes &&
- git config --unset-all merge.log
- git config --unset-all merge.summary
- git config merge.summary yes &&
+ >empty &&
(
cd remote &&
@@ -258,23 +376,21 @@ test_expect_success 'merge-msg with nothing to merge' '
git fmt-merge-msg <.git/FETCH_HEAD >../actual
) &&
- test_cmp /dev/null actual
+ test_cmp empty actual
'
-cat >expected <<\EOF
-Merge tag 'tag-r3'
-
-* tag 'tag-r3':
- Right #3
- Common #2
- Common #1
-EOF
-
test_expect_success 'merge-msg tag' '
+ cat >expected <<-EOF &&
+ Merge tag ${apos}tag-r3${apos}
+
+ * tag ${apos}tag-r3${apos}:
+ Right #3
+ Common #2
+ Common #1
+ EOF
- git config --unset-all merge.log
- git config --unset-all merge.summary
- git config merge.summary yes &&
+ test_unconfig merge.log &&
+ test_config merge.summary yes &&
git checkout master &&
test_tick &&
@@ -284,27 +400,27 @@ test_expect_success 'merge-msg tag' '
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 &&
+ cat >expected <<-EOF &&
+ Merge tags ${apos}tag-r3${apos} and ${apos}tag-l5${apos}
+
+ * tag ${apos}tag-r3${apos}:
+ Right #3
+ Common #2
+ Common #1
+
+ # By Another Author (3) and A U Thor (2)
+ # Via Another Committer
+ * tag ${apos}tag-l5${apos}:
+ Left #5
+ Left #4
+ Left #3
+ Common #2
+ Common #1
+ EOF
+
+ test_unconfig merge.log &&
+ test_config merge.summary yes &&
git checkout master &&
test_tick &&
@@ -314,27 +430,27 @@ test_expect_success 'merge-msg two tags' '
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 &&
+ cat >expected <<-EOF &&
+ Merge branch ${apos}left${apos}, tag ${apos}tag-r3${apos}
+
+ * tag ${apos}tag-r3${apos}:
+ Right #3
+ Common #2
+ Common #1
+
+ # By Another Author (3) and A U Thor (2)
+ # Via Another Committer
+ * left:
+ Left #5
+ Left #4
+ Left #3
+ Common #2
+ Common #1
+ EOF
+
+ test_unconfig merge.log &&
+ test_config merge.summary yes &&
git checkout master &&
test_tick &&
@@ -344,28 +460,70 @@ test_expect_success 'merge-msg tag and branch' '
test_cmp expected actual
'
-cat >expected <<\EOF
-Merge branch 'long'
+test_expect_success 'merge-msg lots of commits' '
+ {
+ cat <<-EOF &&
+ Merge branch ${apos}long${apos}
-* long: (35 commits)
-EOF
+ * long: (35 commits)
+ EOF
-test_expect_success 'merge-msg lots of commits' '
+ i=29 &&
+ while test $i -gt 9
+ do
+ echo " $i" &&
+ i=$(($i-1))
+ done &&
+ echo " ..."
+ } >expected &&
+
+ test_config merge.summary yes &&
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_expect_success 'merge-msg with "merging" an annotated tag' '
+ test_config merge.log true &&
+
+ git checkout master^0 &&
+ git commit --allow-empty -m "One step ahead" &&
+ git tag -a -m "An annotated one" annote HEAD &&
+
+ git checkout master &&
+ git fetch . annote &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ {
+ cat <<-\EOF
+ Merge tag '\''annote'\''
+
+ An annotated one
+
+ * tag '\''annote'\'':
+ One step ahead
+ EOF
+ } >expected &&
+ test_cmp expected actual &&
+
+ test_when_finished "git reset --hard" &&
+ annote=$(git rev-parse annote) &&
+ git merge --no-commit $annote &&
+ {
+ cat <<-EOF
+ Merge tag '\''$annote'\''
+
+ An annotated one
+
+ * tag '\''$annote'\'':
+ One step ahead
+ EOF
+ } >expected &&
+ test_cmp expected .git/MERGE_MSG
+'
+
test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 7dc8a51..19a2823 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -6,9 +6,10 @@
test_description='for-each-ref test'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
-# Mon Jul 3 15:18:43 2006 +0000
-datestamp=1151939923
+# Mon Jul 3 23:18:43 2006 +0000
+datestamp=1151968723
setdate_and_increment () {
GIT_COMMITTER_DATE="$datestamp +0200"
datestamp=$(expr "$datestamp" + 1)
@@ -17,74 +18,94 @@ setdate_and_increment () {
export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
}
-test_expect_success 'Create sample commit with known timestamp' '
+test_expect_success setup '
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 tag -a -m "Tagging at $datestamp" testtag &&
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
+ git config branch.master.merge refs/heads/master &&
+ git remote add myfork elsewhere &&
+ git config remote.pushdefault myfork &&
+ git config push.default current
'
test_atom() {
case "$1" in
head) ref=refs/heads/master ;;
tag) ref=refs/tags/testtag ;;
+ *) ref=$1 ;;
esac
printf '%s\n' "$3" >expected
- test_expect_${4:-success} "basic atom: $1 $2" "
+ test_expect_${4:-success} $PREREQ "basic atom: $1 $2" "
git for-each-ref --format='%($2)' $ref >actual &&
- test_cmp expected actual
+ sanitize_pgp <actual >actual.clean &&
+ test_cmp expected actual.clean
"
}
test_atom head refname refs/heads/master
+test_atom head refname:short master
+test_atom head refname:strip=1 heads/master
+test_atom head refname:strip=2 master
test_atom head upstream refs/remotes/origin/master
+test_atom head upstream:short origin/master
+test_atom head push refs/remotes/myfork/master
+test_atom head push:short myfork/master
test_atom head objecttype commit
test_atom head objectsize 171
-test_atom head objectname 67a36f10722846e891fbada1ba48ed035de75581
-test_atom head tree 0e51c00fcb93dffc755546f27593d511e1bdb46f
+test_atom head objectname $(git rev-parse refs/heads/master)
+test_atom head objectname:short $(git rev-parse --short refs/heads/master)
+test_atom head tree $(git rev-parse refs/heads/master^{tree})
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 '*objectname' ''
+test_atom head '*objecttype' ''
+test_atom head author 'A U Thor <author@example.com> 1151968724 +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 authordate 'Tue Jul 4 01:18:44 2006 +0200'
+test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +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 committerdate 'Tue Jul 4 01: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 creator 'C O Mitter <committer@example.com> 1151968723 +0200'
+test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'
test_atom head subject 'Initial'
+test_atom head contents:subject 'Initial'
test_atom head body ''
+test_atom head contents:body ''
+test_atom head contents:signature ''
test_atom head contents 'Initial
'
+test_atom head HEAD '*'
test_atom tag refname refs/tags/testtag
+test_atom tag refname:short testtag
test_atom tag upstream ''
+test_atom tag push ''
test_atom tag objecttype tag
test_atom tag objectsize 154
-test_atom tag objectname 98b46b1d36e5b07909de1b3886224e3e81e87322
+test_atom tag objectname $(git rev-parse refs/tags/testtag)
+test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)
test_atom tag tree ''
test_atom tag parent ''
test_atom tag numparent ''
-test_atom tag object '67a36f10722846e891fbada1ba48ed035de75581'
+test_atom tag object $(git rev-parse refs/tags/testtag^0)
test_atom tag type 'commit'
+test_atom tag '*objectname' 'ea122842f48be4afb2d1fc6a4b96c05885ab7463'
+test_atom tag '*objecttype' 'commit'
test_atom tag author ''
test_atom tag authorname ''
test_atom tag authoremail ''
@@ -94,21 +115,35 @@ 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 tagger 'C O Mitter <committer@example.com> 1151968725 +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 taggerdate 'Tue Jul 4 01:18:45 2006 +0200'
+test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'
+test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'
+test_atom tag subject 'Tagging at 1151968727'
+test_atom tag contents:subject 'Tagging at 1151968727'
test_atom tag body ''
-test_atom tag contents 'Tagging at 1151939927
+test_atom tag contents:body ''
+test_atom tag contents:signature ''
+test_atom tag contents 'Tagging at 1151968727
'
+test_atom tag HEAD ' '
test_expect_success 'Check invalid atoms names are errors' '
test_must_fail git for-each-ref --format="%(INVALID)" refs/heads
'
+test_expect_success 'arguments to :strip must be positive integers' '
+ test_must_fail git for-each-ref --format="%(refname:strip=0)" &&
+ test_must_fail git for-each-ref --format="%(refname:strip=-1)" &&
+ test_must_fail git for-each-ref --format="%(refname:strip=foo)"
+'
+
+test_expect_success 'stripping refnames too far gives an error' '
+ test_must_fail git for-each-ref --format="%(refname:strip=3)"
+'
+
test_expect_success 'Check format specifiers are ignored in naming date atoms' '
git for-each-ref --format="%(authordate)" refs/heads &&
git for-each-ref --format="%(authordate:default) %(authordate)" refs/heads &&
@@ -129,84 +164,130 @@ 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_date () {
+ f=$1 &&
+ committer_date=$2 &&
+ author_date=$3 &&
+ tagger_date=$4 &&
+ cat >expected <<-EOF &&
+ 'refs/heads/master' '$committer_date' '$author_date'
+ 'refs/tags/testtag' '$tagger_date'
+ EOF
+ (
+ git for-each-ref --shell \
+ --format="%(refname) %(committerdate${f:+:$f}) %(authordate${f:+:$f})" \
+ refs/heads &&
+ git for-each-ref --shell \
+ --format="%(refname) %(taggerdate${f:+:$f})" \
+ refs/tags
+ ) >actual &&
+ test_cmp expected actual
+}
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_date "" \
+ "Tue Jul 4 01:18:43 2006 +0200" \
+ "Tue Jul 4 01:18:44 2006 +0200" \
+ "Tue Jul 4 01:18:45 2006 +0200"
'
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
+ test_date default \
+ "Tue Jul 4 01:18:43 2006 +0200" \
+ "Tue Jul 4 01:18:44 2006 +0200" \
+ "Tue Jul 4 01:18:45 2006 +0200"
+'
+
+test_expect_success 'Check format "default-local" date fields output' '
+ test_date default-local "Mon Jul 3 23:18:43 2006" "Mon Jul 3 23:18:44 2006" "Mon Jul 3 23:18:45 2006"
'
# 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
+# We just check that this is the same as "relative" for now.
+test_expect_success 'Check format "relative-local" date fields output' '
+ test_date relative-local \
+ "$(git for-each-ref --format="%(committerdate:relative)" refs/heads)" \
+ "$(git for-each-ref --format="%(authordate:relative)" refs/heads)" \
+ "$(git for-each-ref --format="%(taggerdate:relative)" refs/tags)"
+'
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
+ test_date short 2006-07-04 2006-07-04 2006-07-04
'
-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 "short-local" date fields output' '
+ test_date short-local 2006-07-03 2006-07-03 2006-07-03
+'
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
+ test_date local \
+ "Mon Jul 3 23:18:43 2006" \
+ "Mon Jul 3 23:18:44 2006" \
+ "Mon Jul 3 23:18:45 2006"
'
-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
+ test_date iso8601 \
+ "2006-07-04 01:18:43 +0200" \
+ "2006-07-04 01:18:44 +0200" \
+ "2006-07-04 01:18:45 +0200"
'
-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 "iso8601-local" date fields output' '
+ test_date iso8601-local "2006-07-03 23:18:43 +0000" "2006-07-03 23:18:44 +0000" "2006-07-03 23:18:45 +0000"
+'
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_date rfc2822 \
+ "Tue, 4 Jul 2006 01:18:43 +0200" \
+ "Tue, 4 Jul 2006 01:18:44 +0200" \
+ "Tue, 4 Jul 2006 01:18:45 +0200"
+'
+
+test_expect_success 'Check format "rfc2822-local" date fields output' '
+ test_date rfc2822-local "Mon, 3 Jul 2006 23:18:43 +0000" "Mon, 3 Jul 2006 23:18:44 +0000" "Mon, 3 Jul 2006 23:18:45 +0000"
+'
+
+test_expect_success 'Check format "raw" date fields output' '
+ test_date raw "1151968723 +0200" "1151968724 +0200" "1151968725 +0200"
+'
+
+test_expect_success 'Check format "raw-local" date fields output' '
+ test_date raw-local "1151968723 +0000" "1151968724 +0000" "1151968725 +0000"
+'
+
+test_expect_success 'Check format of strftime date fields' '
+ echo "my date is 2006-07-04" >expected &&
+ git for-each-ref \
+ --format="%(authordate:format:my date is %Y-%m-%d)" \
+ refs/heads >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Check format of strftime-local date fields' '
+ echo "my date is 2006-07-03" >expected &&
+ git for-each-ref \
+ --format="%(authordate:format-local:my date is %Y-%m-%d)" \
+ refs/heads >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'exercise strftime with odd fields' '
+ echo >expected &&
+ git for-each-ref --format="%(authordate:format:)" refs/heads >actual &&
+ test_cmp expected actual &&
+ long="long format -- $_z40$_z40$_z40$_z40$_z40$_z40$_z40" &&
+ echo $long >expected &&
+ git for-each-ref --format="%(authordate:format:$long)" refs/heads >actual &&
test_cmp expected actual
'
@@ -275,32 +356,30 @@ for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do
"
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
+test_expect_success 'setup for upstream:track[short]' '
+ test_commit two
'
-cat >expected <<EOF
-origin/master
-EOF
+test_atom head upstream:track '[ahead 1]'
+test_atom head upstream:trackshort '>'
+test_atom head push:track '[ahead 1]'
+test_atom head push:trackshort '>'
-test_expect_success 'Check short upstream format' '
- git for-each-ref --format="%(upstream:short)" refs/heads >actual &&
- test_cmp expected actual
+test_expect_success 'Check that :track[short] cannot be used with other atoms' '
+ test_must_fail git for-each-ref --format="%(refname:track)" 2>/dev/null &&
+ test_must_fail git for-each-ref --format="%(refname:trackshort)" 2>/dev/null
'
-cat >expected <<EOF
-67a36f1
-EOF
+test_expect_success 'Check that :track[short] works when upstream is invalid' '
+ cat >expected <<-\EOF &&
+
-test_expect_success 'Check short objectname format' '
- git for-each-ref --format="%(objectname:short)" refs/heads >actual &&
+ EOF
+ test_when_finished "git config branch.master.merge refs/heads/master" &&
+ git config branch.master.merge refs/heads/does-not-exist &&
+ git for-each-ref \
+ --format="%(upstream:track)$LF%(upstream:trackshort)" \
+ refs/heads >actual &&
test_cmp expected actual
'
@@ -308,6 +387,23 @@ test_expect_success 'Check for invalid refname format' '
test_must_fail git for-each-ref --format="%(refname:INVALID)"
'
+get_color ()
+{
+ git config --get-color no.such.slot "$1"
+}
+
+cat >expected <<EOF
+$(git rev-parse --short refs/heads/master) $(get_color green)master$(get_color reset)
+$(git rev-parse --short refs/remotes/origin/master) $(get_color green)origin/master$(get_color reset)
+$(git rev-parse --short refs/tags/testtag) $(get_color green)testtag$(get_color reset)
+$(git rev-parse --short refs/tags/two) $(get_color green)two$(get_color reset)
+EOF
+
+test_expect_success 'Check %(color:...) ' '
+ git for-each-ref --format="%(objectname:short) %(color:green)%(refname:short)" >actual &&
+ test_cmp expected actual
+'
+
cat >expected <<\EOF
heads/master
tags/master
@@ -359,4 +455,102 @@ test_expect_success 'an unusual tag with an incomplete line' '
'
+test_expect_success 'create tag with subject and body content' '
+ cat >>msg <<-\EOF &&
+ the subject line
+
+ first body line
+ second body line
+ EOF
+ git tag -F msg subject-body
+'
+test_atom refs/tags/subject-body subject 'the subject line'
+test_atom refs/tags/subject-body body 'first body line
+second body line
+'
+test_atom refs/tags/subject-body contents 'the subject line
+
+first body line
+second body line
+'
+
+test_expect_success 'create tag with multiline subject' '
+ cat >msg <<-\EOF &&
+ first subject line
+ second subject line
+
+ first body line
+ second body line
+ EOF
+ git tag -F msg multiline
+'
+test_atom refs/tags/multiline subject 'first subject line second subject line'
+test_atom refs/tags/multiline contents:subject 'first subject line second subject line'
+test_atom refs/tags/multiline body 'first body line
+second body line
+'
+test_atom refs/tags/multiline contents:body 'first body line
+second body line
+'
+test_atom refs/tags/multiline contents:signature ''
+test_atom refs/tags/multiline contents 'first subject line
+second subject line
+
+first body line
+second body line
+'
+
+test_expect_success GPG 'create signed tags' '
+ git tag -s -m "" signed-empty &&
+ git tag -s -m "subject line" signed-short &&
+ cat >msg <<-\EOF &&
+ subject line
+
+ body contents
+ EOF
+ git tag -s -F msg signed-long
+'
+
+sig='-----BEGIN PGP SIGNATURE-----
+-----END PGP SIGNATURE-----
+'
+
+PREREQ=GPG
+test_atom refs/tags/signed-empty subject ''
+test_atom refs/tags/signed-empty contents:subject ''
+test_atom refs/tags/signed-empty body "$sig"
+test_atom refs/tags/signed-empty contents:body ''
+test_atom refs/tags/signed-empty contents:signature "$sig"
+test_atom refs/tags/signed-empty contents "$sig"
+
+test_atom refs/tags/signed-short subject 'subject line'
+test_atom refs/tags/signed-short contents:subject 'subject line'
+test_atom refs/tags/signed-short body "$sig"
+test_atom refs/tags/signed-short contents:body ''
+test_atom refs/tags/signed-short contents:signature "$sig"
+test_atom refs/tags/signed-short contents "subject line
+$sig"
+
+test_atom refs/tags/signed-long subject 'subject line'
+test_atom refs/tags/signed-long contents:subject 'subject line'
+test_atom refs/tags/signed-long body "body contents
+$sig"
+test_atom refs/tags/signed-long contents:body 'body contents
+'
+test_atom refs/tags/signed-long contents:signature "$sig"
+test_atom refs/tags/signed-long contents "subject line
+
+body contents
+$sig"
+
+cat >expected <<EOF
+$(git rev-parse refs/tags/bogo) <committer@example.com> refs/tags/bogo
+$(git rev-parse refs/tags/master) <committer@example.com> refs/tags/master
+EOF
+
+test_expect_success 'Verify sort with multiple keys' '
+ git for-each-ref --format="%(objectname) %(taggeremail) %(refname)" --sort=objectname --sort=taggeremail \
+ refs/tags/bogo refs/tags/master > actual &&
+ test_cmp expected actual
+'
test_done
diff --git a/t/t6301-for-each-ref-errors.sh b/t/t6301-for-each-ref-errors.sh
new file mode 100755
index 0000000..cdb67a0
--- /dev/null
+++ b/t/t6301-for-each-ref-errors.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+test_description='for-each-ref errors for broken refs'
+
+. ./test-lib.sh
+
+ZEROS=$_z40
+MISSING=abababababababababababababababababababab
+
+test_expect_success setup '
+ git commit --allow-empty -m "Initial" &&
+ git tag testtag &&
+ git for-each-ref >full-list &&
+ git for-each-ref --format="%(objectname) %(refname)" >brief-list
+'
+
+test_expect_success 'Broken refs are reported correctly' '
+ r=refs/heads/bogus &&
+ : >.git/$r &&
+ test_when_finished "rm -f .git/$r" &&
+ echo "warning: ignoring broken ref $r" >broken-err &&
+ git for-each-ref >out 2>err &&
+ test_cmp full-list out &&
+ test_cmp broken-err err
+'
+
+test_expect_success 'NULL_SHA1 refs are reported correctly' '
+ r=refs/heads/zeros &&
+ echo $ZEROS >.git/$r &&
+ test_when_finished "rm -f .git/$r" &&
+ echo "warning: ignoring broken ref $r" >zeros-err &&
+ git for-each-ref >out 2>err &&
+ test_cmp full-list out &&
+ test_cmp zeros-err err &&
+ git for-each-ref --format="%(objectname) %(refname)" >brief-out 2>brief-err &&
+ test_cmp brief-list brief-out &&
+ test_cmp zeros-err brief-err
+'
+
+test_expect_success 'Missing objects are reported correctly' '
+ r=refs/heads/missing &&
+ echo $MISSING >.git/$r &&
+ test_when_finished "rm -f .git/$r" &&
+ echo "fatal: missing object $MISSING for $r" >missing-err &&
+ test_must_fail git for-each-ref 2>err &&
+ test_cmp missing-err err &&
+ (
+ cat brief-list &&
+ echo "$MISSING $r"
+ ) | sort -k 2 >missing-brief-expected &&
+ git for-each-ref --format="%(objectname) %(refname)" >brief-out 2>brief-err &&
+ test_cmp missing-brief-expected brief-out &&
+ test_must_be_empty brief-err
+'
+
+test_done
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
new file mode 100755
index 0000000..d0ab09f
--- /dev/null
+++ b/t/t6302-for-each-ref-filter.sh
@@ -0,0 +1,330 @@
+#!/bin/sh
+
+test_description='test for-each-refs usage of ref-filter APIs'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
+
+test_expect_success 'setup some history and refs' '
+ test_commit one &&
+ test_commit two &&
+ test_commit three &&
+ git checkout -b side &&
+ test_commit four &&
+ git tag -m "An annotated tag" annotated-tag &&
+ git tag -m "Annonated doubly" doubly-annotated-tag annotated-tag &&
+
+ # Note that these "signed" tags might not actually be signed.
+ # Tests which care about the distinction should be marked
+ # with the GPG prereq.
+ if test_have_prereq GPG
+ then
+ sign=-s
+ else
+ sign=
+ fi &&
+ git tag $sign -m "A signed tag" signed-tag &&
+ git tag $sign -m "Signed doubly" doubly-signed-tag signed-tag &&
+
+ git checkout master &&
+ git update-ref refs/odd/spot master
+'
+
+test_expect_success 'filtering with --points-at' '
+ cat >expect <<-\EOF &&
+ refs/heads/master
+ refs/odd/spot
+ refs/tags/three
+ EOF
+ git for-each-ref --format="%(refname)" --points-at=master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check signed tags with --points-at' '
+ sed -e "s/Z$//" >expect <<-\EOF &&
+ refs/heads/side Z
+ refs/tags/annotated-tag four
+ refs/tags/four Z
+ refs/tags/signed-tag four
+ EOF
+ git for-each-ref --format="%(refname) %(*subject)" --points-at=side >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'filtering with --merged' '
+ cat >expect <<-\EOF &&
+ refs/heads/master
+ refs/odd/spot
+ refs/tags/one
+ refs/tags/three
+ refs/tags/two
+ EOF
+ git for-each-ref --format="%(refname)" --merged=master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'filtering with --no-merged' '
+ cat >expect <<-\EOF &&
+ refs/heads/side
+ refs/tags/annotated-tag
+ refs/tags/doubly-annotated-tag
+ refs/tags/doubly-signed-tag
+ refs/tags/four
+ refs/tags/signed-tag
+ EOF
+ git for-each-ref --format="%(refname)" --no-merged=master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'filtering with --contains' '
+ cat >expect <<-\EOF &&
+ refs/heads/master
+ refs/heads/side
+ refs/odd/spot
+ refs/tags/annotated-tag
+ refs/tags/doubly-annotated-tag
+ refs/tags/doubly-signed-tag
+ refs/tags/four
+ refs/tags/signed-tag
+ refs/tags/three
+ refs/tags/two
+ EOF
+ git for-each-ref --format="%(refname)" --contains=two >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '%(color) must fail' '
+ test_must_fail git for-each-ref --format="%(color)%(refname)"
+'
+
+test_expect_success 'left alignment is default' '
+ cat >expect <<-\EOF &&
+ refname is refs/heads/master |refs/heads/master
+ refname is refs/heads/side |refs/heads/side
+ refname is refs/odd/spot |refs/odd/spot
+ refname is refs/tags/annotated-tag|refs/tags/annotated-tag
+ refname is refs/tags/doubly-annotated-tag|refs/tags/doubly-annotated-tag
+ refname is refs/tags/doubly-signed-tag|refs/tags/doubly-signed-tag
+ refname is refs/tags/four |refs/tags/four
+ refname is refs/tags/one |refs/tags/one
+ refname is refs/tags/signed-tag|refs/tags/signed-tag
+ refname is refs/tags/three |refs/tags/three
+ refname is refs/tags/two |refs/tags/two
+ EOF
+ git for-each-ref --format="%(align:30)refname is %(refname)%(end)|%(refname)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'middle alignment' '
+ cat >expect <<-\EOF &&
+ | refname is refs/heads/master |refs/heads/master
+ | refname is refs/heads/side |refs/heads/side
+ | refname is refs/odd/spot |refs/odd/spot
+ |refname is refs/tags/annotated-tag|refs/tags/annotated-tag
+ |refname is refs/tags/doubly-annotated-tag|refs/tags/doubly-annotated-tag
+ |refname is refs/tags/doubly-signed-tag|refs/tags/doubly-signed-tag
+ | refname is refs/tags/four |refs/tags/four
+ | refname is refs/tags/one |refs/tags/one
+ |refname is refs/tags/signed-tag|refs/tags/signed-tag
+ | refname is refs/tags/three |refs/tags/three
+ | refname is refs/tags/two |refs/tags/two
+ EOF
+ git for-each-ref --format="|%(align:middle,30)refname is %(refname)%(end)|%(refname)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'right alignment' '
+ cat >expect <<-\EOF &&
+ | refname is refs/heads/master|refs/heads/master
+ | refname is refs/heads/side|refs/heads/side
+ | refname is refs/odd/spot|refs/odd/spot
+ |refname is refs/tags/annotated-tag|refs/tags/annotated-tag
+ |refname is refs/tags/doubly-annotated-tag|refs/tags/doubly-annotated-tag
+ |refname is refs/tags/doubly-signed-tag|refs/tags/doubly-signed-tag
+ | refname is refs/tags/four|refs/tags/four
+ | refname is refs/tags/one|refs/tags/one
+ |refname is refs/tags/signed-tag|refs/tags/signed-tag
+ | refname is refs/tags/three|refs/tags/three
+ | refname is refs/tags/two|refs/tags/two
+ EOF
+ git for-each-ref --format="|%(align:30,right)refname is %(refname)%(end)|%(refname)" >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<-\EOF
+| refname is refs/heads/master |refs/heads/master
+| refname is refs/heads/side |refs/heads/side
+| refname is refs/odd/spot |refs/odd/spot
+| refname is refs/tags/annotated-tag |refs/tags/annotated-tag
+|refname is refs/tags/doubly-annotated-tag |refs/tags/doubly-annotated-tag
+| refname is refs/tags/doubly-signed-tag |refs/tags/doubly-signed-tag
+| refname is refs/tags/four |refs/tags/four
+| refname is refs/tags/one |refs/tags/one
+| refname is refs/tags/signed-tag |refs/tags/signed-tag
+| refname is refs/tags/three |refs/tags/three
+| refname is refs/tags/two |refs/tags/two
+EOF
+
+test_align_permutations() {
+ while read -r option
+ do
+ test_expect_success "align:$option" '
+ git for-each-ref --format="|%(align:$option)refname is %(refname)%(end)|%(refname)" >actual &&
+ test_cmp expect actual
+ '
+ done
+}
+
+test_align_permutations <<-\EOF
+ middle,42
+ 42,middle
+ position=middle,42
+ 42,position=middle
+ middle,width=42
+ width=42,middle
+ position=middle,width=42
+ width=42,position=middle
+EOF
+
+# Last one wins (silently) when multiple arguments of the same type are given
+
+test_align_permutations <<-\EOF
+ 32,width=42,middle
+ width=30,42,middle
+ width=42,position=right,middle
+ 42,right,position=middle
+EOF
+
+# Individual atoms inside %(align:...) and %(end) must not be quoted.
+
+test_expect_success 'alignment with format quote' "
+ cat >expect <<-\EOF &&
+ |' '\''master| A U Thor'\'' '|
+ |' '\''side| A U Thor'\'' '|
+ |' '\''odd/spot| A U Thor'\'' '|
+ |' '\''annotated-tag| '\'' '|
+ |' '\''doubly-annotated-tag| '\'' '|
+ |' '\''doubly-signed-tag| '\'' '|
+ |' '\''four| A U Thor'\'' '|
+ |' '\''one| A U Thor'\'' '|
+ |' '\''signed-tag| '\'' '|
+ |' '\''three| A U Thor'\'' '|
+ |' '\''two| A U Thor'\'' '|
+ EOF
+ git for-each-ref --shell --format=\"|%(align:30,middle)'%(refname:short)| %(authorname)'%(end)|\" >actual &&
+ test_cmp expect actual
+"
+
+test_expect_success 'nested alignment with quote formatting' "
+ cat >expect <<-\EOF &&
+ |' master '|
+ |' side '|
+ |' odd/spot '|
+ |' annotated-tag '|
+ |'doubly-annotated-tag '|
+ |'doubly-signed-tag '|
+ |' four '|
+ |' one '|
+ |' signed-tag '|
+ |' three '|
+ |' two '|
+ EOF
+ git for-each-ref --shell --format='|%(align:30,left)%(align:15,right)%(refname:short)%(end)%(end)|' >actual &&
+ test_cmp expect actual
+"
+
+test_expect_success 'check `%(contents:lines=1)`' '
+ cat >expect <<-\EOF &&
+ master |three
+ side |four
+ odd/spot |three
+ annotated-tag |An annotated tag
+ doubly-annotated-tag |Annonated doubly
+ doubly-signed-tag |Signed doubly
+ four |four
+ one |one
+ signed-tag |A signed tag
+ three |three
+ two |two
+ EOF
+ git for-each-ref --format="%(refname:short) |%(contents:lines=1)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check `%(contents:lines=0)`' '
+ cat >expect <<-\EOF &&
+ master |
+ side |
+ odd/spot |
+ annotated-tag |
+ doubly-annotated-tag |
+ doubly-signed-tag |
+ four |
+ one |
+ signed-tag |
+ three |
+ two |
+ EOF
+ git for-each-ref --format="%(refname:short) |%(contents:lines=0)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check `%(contents:lines=99999)`' '
+ cat >expect <<-\EOF &&
+ master |three
+ side |four
+ odd/spot |three
+ annotated-tag |An annotated tag
+ doubly-annotated-tag |Annonated doubly
+ doubly-signed-tag |Signed doubly
+ four |four
+ one |one
+ signed-tag |A signed tag
+ three |three
+ two |two
+ EOF
+ git for-each-ref --format="%(refname:short) |%(contents:lines=99999)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '`%(contents:lines=-1)` should fail' '
+ test_must_fail git for-each-ref --format="%(refname:short) |%(contents:lines=-1)"
+'
+
+test_expect_success 'setup for version sort' '
+ test_commit foo1.3 &&
+ test_commit foo1.6 &&
+ test_commit foo1.10
+'
+
+test_expect_success 'version sort' '
+ git for-each-ref --sort=version:refname --format="%(refname:short)" refs/tags/ | grep "foo" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.3
+ foo1.6
+ foo1.10
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'version sort (shortened)' '
+ git for-each-ref --sort=v:refname --format="%(refname:short)" refs/tags/ | grep "foo" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.3
+ foo1.6
+ foo1.10
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'reverse version sort' '
+ git for-each-ref --sort=-version:refname --format="%(refname:short)" refs/tags/ | grep "foo" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.10
+ foo1.6
+ foo1.3
+ EOF
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh
new file mode 100755
index 0000000..5d7d414
--- /dev/null
+++ b/t/t6500-gc.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='basic git gc tests
+'
+
+. ./test-lib.sh
+
+test_expect_success 'gc empty repository' '
+ git gc
+'
+
+test_expect_success 'gc does not leave behind pid file' '
+ git gc &&
+ test_path_is_missing .git/gc.pid
+'
+
+test_expect_success 'gc --gobbledegook' '
+ test_expect_code 129 git gc --nonsense 2>err &&
+ test_i18ngrep "[Uu]sage: git gc" err
+'
+
+test_expect_success 'gc -h with invalid configuration' '
+ mkdir broken &&
+ (
+ cd broken &&
+ git init &&
+ echo "[gc] pruneexpire = CORRUPT" >>.git/config &&
+ test_expect_code 129 git gc -h >usage 2>&1
+ ) &&
+ test_i18ngrep "[Uu]sage" broken/usage
+'
+
+test_expect_success 'gc is not aborted due to a stale symref' '
+ git init remote &&
+ (
+ cd remote &&
+ test_commit initial &&
+ git clone . ../client &&
+ git branch -m develop &&
+ cd ../client &&
+ git fetch --prune &&
+ git gc
+ )
+'
+
+test_done
diff --git a/t/t6501-freshen-objects.sh b/t/t6501-freshen-objects.sh
new file mode 100755
index 0000000..cf076dc
--- /dev/null
+++ b/t/t6501-freshen-objects.sh
@@ -0,0 +1,147 @@
+#!/bin/sh
+#
+# This test covers the handling of objects which might have old
+# mtimes in the filesystem (because they were used previously)
+# and are just now becoming referenced again.
+#
+# We're going to do two things that are a little bit "fake" to
+# help make our simulation easier:
+#
+# 1. We'll turn off reflogs. You can still run into
+# problems with reflogs on, but your objects
+# don't get pruned until both the reflog expiration
+# has passed on their references, _and_ they are out
+# of prune's expiration period. Dropping reflogs
+# means we only have to deal with one variable in our tests,
+# but the results generalize.
+#
+# 2. We'll use a temporary index file to create our
+# works-in-progress. Most workflows would mention
+# referenced objects in the index, which prune takes
+# into account. However, many operations don't. For
+# example, a partial commit with "git commit foo"
+# will use a temporary index. Or they may not need
+# an index at all (e.g., creating a new commit
+# to refer to an existing tree).
+
+test_description='check pruning of dependent objects'
+. ./test-lib.sh
+
+# We care about reachability, so we do not want to use
+# the normal test_commit, which creates extra tags.
+add () {
+ echo "$1" >"$1" &&
+ git add "$1"
+}
+commit () {
+ test_tick &&
+ add "$1" &&
+ git commit -m "$1"
+}
+
+maybe_repack () {
+ if test -n "$repack"; then
+ git repack -ad
+ fi
+}
+
+for repack in '' true; do
+ title=${repack:+repack}
+ title=${title:-loose}
+
+ test_expect_success "make repo completely empty ($title)" '
+ rm -rf .git &&
+ git init
+ '
+
+ test_expect_success "disable reflogs ($title)" '
+ git config core.logallrefupdates false &&
+ git reflog expire --expire=all --all
+ '
+
+ test_expect_success "setup basic history ($title)" '
+ commit base
+ '
+
+ test_expect_success "create and abandon some objects ($title)" '
+ git checkout -b experiment &&
+ commit abandon &&
+ maybe_repack &&
+ git checkout master &&
+ git branch -D experiment
+ '
+
+ test_expect_success "simulate time passing ($title)" '
+ find .git/objects -type f |
+ xargs test-chmtime -v -86400
+ '
+
+ test_expect_success "start writing new commit with old blob ($title)" '
+ tree=$(
+ GIT_INDEX_FILE=index.tmp &&
+ export GIT_INDEX_FILE &&
+ git read-tree HEAD &&
+ add unrelated &&
+ add abandon &&
+ git write-tree
+ )
+ '
+
+ test_expect_success "simultaneous gc ($title)" '
+ git gc --prune=12.hours.ago
+ '
+
+ test_expect_success "finish writing out commit ($title)" '
+ commit=$(echo foo | git commit-tree -p HEAD $tree) &&
+ git update-ref HEAD $commit
+ '
+
+ # "abandon" blob should have been rescued by reference from new tree
+ test_expect_success "repository passes fsck ($title)" '
+ git fsck
+ '
+
+ test_expect_success "abandon objects again ($title)" '
+ git reset --hard HEAD^ &&
+ find .git/objects -type f |
+ xargs test-chmtime -v -86400
+ '
+
+ test_expect_success "start writing new commit with same tree ($title)" '
+ tree=$(
+ GIT_INDEX_FILE=index.tmp &&
+ export GIT_INDEX_FILE &&
+ git read-tree HEAD &&
+ add abandon &&
+ add unrelated &&
+ git write-tree
+ )
+ '
+
+ test_expect_success "simultaneous gc ($title)" '
+ git gc --prune=12.hours.ago
+ '
+
+ # tree should have been refreshed by write-tree
+ test_expect_success "finish writing out commit ($title)" '
+ commit=$(echo foo | git commit-tree -p HEAD $tree) &&
+ git update-ref HEAD $commit
+ '
+done
+
+test_expect_success 'do not complain about existing broken links' '
+ cat >broken-commit <<-\EOF &&
+ tree 0000000000000000000000000000000000000001
+ parent 0000000000000000000000000000000000000002
+ author whatever <whatever@example.com> 1234 -0000
+ committer whatever <whatever@example.com> 1234 -0000
+
+ some message
+ EOF
+ commit=$(git hash-object -t commit -w broken-commit) &&
+ git gc 2>stderr &&
+ verbose git cat-file -e $commit &&
+ test_must_be_empty stderr
+'
+
+test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 65a35d9..4a2570e 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -61,7 +61,7 @@ test_expect_success \
test_expect_success \
'checking -f on untracked file with existing target' \
'touch path0/untracked1 &&
- git mv -f untracked1 path0
+ test_must_fail git mv -f untracked1 path0 &&
test ! -f .git/index.lock &&
test -f untracked1 &&
test -f path0/untracked1'
@@ -70,10 +70,39 @@ test_expect_success \
rm -f idontexist untracked1 untracked2 \
path0/idontexist path0/untracked1 path0/untracked2 \
.git/index.lock
+rmdir path1
+
+test_expect_success \
+ 'moving to absent target with trailing slash' \
+ 'test_must_fail git mv path0/COPYING no-such-dir/ &&
+ test_must_fail git mv path0/COPYING no-such-dir// &&
+ git mv path0/ no-such-dir/ &&
+ test_path_is_dir no-such-dir'
+
+test_expect_success \
+ 'clean up' \
+ 'git reset --hard'
+
+test_expect_success \
+ 'moving to existing untracked target with trailing slash' \
+ 'mkdir path1 &&
+ git mv path0/ path1/ &&
+ test_path_is_dir path1/path0/'
+
+test_expect_success \
+ 'moving to existing tracked target with trailing slash' \
+ 'mkdir path2 &&
+ >path2/file && git add path2/file &&
+ git mv path1/path0/ path2/ &&
+ test_path_is_dir path2/path0/'
+
+test_expect_success \
+ 'clean up' \
+ 'git reset --hard'
test_expect_success \
'adding another file' \
- 'cp "$TEST_DIRECTORY"/../README path0/README &&
+ 'cp "$TEST_DIRECTORY"/../README.md path0/README &&
git add path0/README &&
git commit -m add2 -a'
@@ -127,15 +156,12 @@ test_expect_success "Michael Cassar's test case" '
echo b > partA/outline.txt &&
echo c > papers/unsorted/_another &&
git add papers partA &&
- T1=`git write-tree` &&
+ 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)
- }
+ T=$(git write-tree) &&
+ git ls-tree -r $T | verbose grep partA/outline.txt
'
rm -fr papers partA path?
@@ -207,7 +233,7 @@ test_expect_success 'git mv should not change sha1 of moved cache entry' '
git init &&
echo 1 >dirty &&
git add dirty &&
- entry="$(git ls-files --stage dirty | cut -f 1)"
+ 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 &&
@@ -218,13 +244,13 @@ test_expect_success 'git mv should not change sha1 of moved cache entry' '
rm -f dirty dirty2
-test_expect_success SYMLINKS 'git mv should overwrite symlink to a file' '
+test_expect_success 'git mv should overwrite symlink to a file' '
rm -fr .git &&
git init &&
echo 1 >moved &&
- ln -s moved symlink &&
- git add moved symlink &&
+ test_ln_s_add moved symlink &&
+ git add moved &&
test_must_fail git mv moved symlink &&
git mv -f moved symlink &&
! test -e moved &&
@@ -237,22 +263,232 @@ test_expect_success SYMLINKS 'git mv should overwrite symlink to a file' '
rm -f moved symlink
-test_expect_success SYMLINKS 'git mv should overwrite file with a symlink' '
+test_expect_success 'git mv should overwrite file with a symlink' '
rm -fr .git &&
git init &&
echo 1 >moved &&
- ln -s moved symlink &&
- git add moved symlink &&
+ test_ln_s_add moved symlink &&
+ git add moved &&
test_must_fail git mv symlink moved &&
git mv -f symlink moved &&
! test -e symlink &&
- test -h moved &&
git update-index --refresh &&
git diff-files --quiet
'
+test_expect_success SYMLINKS 'check moved symlink' '
+
+ test -h moved
+'
+
rm -f moved symlink
+test_expect_success 'setup submodule' '
+ git commit -m initial &&
+ git reset --hard &&
+ git submodule add ./. sub &&
+ echo content >file &&
+ git add file &&
+ git commit -m "added sub and file" &&
+ mkdir -p deep/directory/hierachy &&
+ git submodule add ./. deep/directory/hierachy/sub &&
+ git commit -m "added another submodule" &&
+ git branch submodule
+'
+
+test_expect_success 'git mv cannot move a submodule in a file' '
+ test_must_fail git mv sub file
+'
+
+test_expect_success 'git mv moves a submodule with a .git directory and no .gitmodules' '
+ entry="$(git ls-files --stage sub | cut -f 1)" &&
+ git rm .gitmodules &&
+ (
+ cd sub &&
+ rm -f .git &&
+ cp -R -P -p ../.git/modules/sub .git &&
+ GIT_WORK_TREE=. git config --unset core.worktree
+ ) &&
+ mkdir mod &&
+ git mv sub mod/sub &&
+ ! test -e sub &&
+ [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
+ (
+ cd mod/sub &&
+ git status
+ ) &&
+ git update-index --refresh &&
+ git diff-files --quiet
+'
+
+test_expect_success 'git mv moves a submodule with a .git directory and .gitmodules' '
+ rm -rf mod &&
+ git reset --hard &&
+ git submodule update &&
+ entry="$(git ls-files --stage sub | cut -f 1)" &&
+ (
+ cd sub &&
+ rm -f .git &&
+ cp -R -P -p ../.git/modules/sub .git &&
+ GIT_WORK_TREE=. git config --unset core.worktree
+ ) &&
+ mkdir mod &&
+ git mv sub mod/sub &&
+ ! test -e sub &&
+ [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
+ (
+ cd mod/sub &&
+ git status
+ ) &&
+ echo mod/sub >expected &&
+ git config -f .gitmodules submodule.sub.path >actual &&
+ test_cmp expected actual &&
+ git update-index --refresh &&
+ git diff-files --quiet
+'
+
+test_expect_success 'git mv moves a submodule with gitfile' '
+ rm -rf mod &&
+ git reset --hard &&
+ git submodule update &&
+ entry="$(git ls-files --stage sub | cut -f 1)" &&
+ mkdir mod &&
+ (
+ cd mod &&
+ git mv ../sub/ .
+ ) &&
+ ! test -e sub &&
+ [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
+ (
+ cd mod/sub &&
+ git status
+ ) &&
+ echo mod/sub >expected &&
+ git config -f .gitmodules submodule.sub.path >actual &&
+ test_cmp expected actual &&
+ git update-index --refresh &&
+ git diff-files --quiet
+'
+
+test_expect_success 'mv does not complain when no .gitmodules file is found' '
+ rm -rf mod &&
+ git reset --hard &&
+ git submodule update &&
+ git rm .gitmodules &&
+ entry="$(git ls-files --stage sub | cut -f 1)" &&
+ mkdir mod &&
+ git mv sub mod/sub 2>actual.err &&
+ ! test -s actual.err &&
+ ! test -e sub &&
+ [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
+ (
+ cd mod/sub &&
+ git status
+ ) &&
+ git update-index --refresh &&
+ git diff-files --quiet
+'
+
+test_expect_success 'mv will error out on a modified .gitmodules file unless staged' '
+ rm -rf mod &&
+ git reset --hard &&
+ git submodule update &&
+ git config -f .gitmodules foo.bar true &&
+ entry="$(git ls-files --stage sub | cut -f 1)" &&
+ mkdir mod &&
+ test_must_fail git mv sub mod/sub 2>actual.err &&
+ test -s actual.err &&
+ test -e sub &&
+ git diff-files --quiet -- sub &&
+ git add .gitmodules &&
+ git mv sub mod/sub 2>actual.err &&
+ ! test -s actual.err &&
+ ! test -e sub &&
+ [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
+ (
+ cd mod/sub &&
+ git status
+ ) &&
+ git update-index --refresh &&
+ git diff-files --quiet
+'
+
+test_expect_success 'mv issues a warning when section is not found in .gitmodules' '
+ rm -rf mod &&
+ git reset --hard &&
+ git submodule update &&
+ git config -f .gitmodules --remove-section submodule.sub &&
+ git add .gitmodules &&
+ entry="$(git ls-files --stage sub | cut -f 1)" &&
+ echo "warning: Could not find section in .gitmodules where path=sub" >expect.err &&
+ mkdir mod &&
+ git mv sub mod/sub 2>actual.err &&
+ test_i18ncmp expect.err actual.err &&
+ ! test -e sub &&
+ [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
+ (
+ cd mod/sub &&
+ git status
+ ) &&
+ git update-index --refresh &&
+ git diff-files --quiet
+'
+
+test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' '
+ rm -rf mod &&
+ git reset --hard &&
+ git submodule update &&
+ mkdir mod &&
+ git mv -n sub mod/sub 2>actual.err &&
+ test -f sub/.git &&
+ git diff-index --exit-code HEAD &&
+ git update-index --refresh &&
+ git diff-files --quiet -- sub .gitmodules
+'
+
+test_expect_success 'checking out a commit before submodule moved needs manual updates' '
+ git mv sub sub2 &&
+ git commit -m "moved sub to sub2" &&
+ git checkout -q HEAD^ 2>actual &&
+ test_i18ngrep "^warning: unable to rmdir sub2:" actual &&
+ git status -s sub2 >actual &&
+ echo "?? sub2/" >expected &&
+ test_cmp expected actual &&
+ ! test -f sub/.git &&
+ test -f sub2/.git &&
+ git submodule update &&
+ test -f sub/.git &&
+ rm -rf sub2 &&
+ git diff-index --exit-code HEAD &&
+ git update-index --refresh &&
+ git diff-files --quiet -- sub .gitmodules &&
+ git status -s sub2 >actual &&
+ ! test -s actual
+'
+
+test_expect_success 'mv -k does not accidentally destroy submodules' '
+ git checkout submodule &&
+ mkdir dummy dest &&
+ git mv -k dummy sub dest &&
+ git status --porcelain >actual &&
+ grep "^R sub -> dest/sub" actual &&
+ git reset --hard &&
+ git checkout .
+'
+
+test_expect_success 'moving a submodule in nested directories' '
+ (
+ cd deep &&
+ git mv directory ../ &&
+ # git status would fail if the update of linking git dir to
+ # work dir of the submodule failed.
+ git status &&
+ git config -f ../.gitmodules submodule.deep/directory/hierachy/sub.path >../actual &&
+ echo "directory/hierachy/sub" >../expect
+ ) &&
+ test_cmp actual expect
+'
+
test_done
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index 0da13a8..cb8fbd8 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -2,32 +2,37 @@
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_DIRECTORY/lib-gpg.sh"
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
-'
+ test_commit A &&
+ GIT_COMMITTER_DATE="@0 +0000" GIT_AUTHOR_DATE="@0 +0000" &&
+ test_commit --notick B &&
+ git checkout -b branch B &&
+ test_commit D &&
+ mkdir dir &&
+ test_commit dir/D &&
+ test_commit E &&
+ git checkout master &&
+ test_commit C &&
+ git checkout branch &&
+ git merge C &&
+ git tag F &&
+ test_commit G &&
+ test_commit H
+'
+# * (HEAD, branch) H
+# * G
+# * Merge commit 'C' into branch
+# |\
+# | * (master) C
+# * | E
+# * | dir/D
+# * | D
+# |/
+# * B
+# * A
+
H=$(git rev-parse H)
@@ -60,19 +65,33 @@ test_expect_success 'correct GIT_DIR while using -d' '
grep drepo "$TRASHDIR/backup-refs"
'
+test_expect_success 'tree-filter works with -d' '
+ git init drepo-tree &&
+ (
+ cd drepo-tree &&
+ test_commit one &&
+ git filter-branch -d "$TRASHDIR/dfoo" \
+ --tree-filter "echo changed >one.t" &&
+ echo changed >expect &&
+ git cat-file blob HEAD:one.t >actual &&
+ test_cmp expect actual &&
+ test_cmp one.t actual
+ )
+'
+
test_expect_success 'Fail if commit filter fails' '
test_must_fail git filter-branch -f --commit-filter "exit 1" HEAD
'
test_expect_success 'rewrite, renaming a specific file' '
- git filter-branch -f --tree-filter "mv d doh || :" HEAD
+ git filter-branch -f --tree-filter "mv D.t doh || :" HEAD
'
test_expect_success 'test that the file was renamed' '
- test d = "$(git show HEAD:doh --)" &&
- ! test -f d &&
+ test D = "$(git show HEAD:doh --)" &&
+ ! test -f D.t &&
test -f doh &&
- test d = "$(cat doh)"
+ test D = "$(cat doh)"
'
test_expect_success 'rewrite, renaming a specific directory' '
@@ -80,18 +99,18 @@ test_expect_success 'rewrite, renaming a specific directory' '
'
test_expect_success 'test that the directory was renamed' '
- test dir/d = "$(git show HEAD:diroh/d --)" &&
+ test dir/D = "$(git show HEAD:diroh/D.t --)" &&
! test -d dir &&
test -d diroh &&
! test -d diroh/dir &&
- test -f diroh/d &&
- test dir/d = "$(cat diroh/d)"
+ test -f diroh/D.t &&
+ test dir/D = "$(cat diroh/D.t)"
'
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
+ git filter-branch -f --tree-filter "mv B.t boh || :" D..modD
'
test_expect_success 'common ancestor is still common (unchanged)' '
@@ -104,13 +123,13 @@ test_expect_success 'filter subdirectory only' '
git add subdir/new &&
test_tick &&
git commit -m "subdir" &&
- echo H > a &&
+ echo H > A.t &&
test_tick &&
- git commit -m "not subdir" a &&
+ git commit -m "not subdir" A.t &&
echo A > subdir/new &&
test_tick &&
git commit -m "again subdir" subdir/new &&
- git rm a &&
+ git rm A.t &&
test_tick &&
git commit -m "again not subdir" &&
git branch sub &&
@@ -134,7 +153,7 @@ test_expect_success 'more setup' '
git add subdir/new &&
test_tick &&
git commit -m "subdir on master" subdir/new &&
- git rm a &&
+ git rm A.t &&
test_tick &&
git commit -m "again subdir on master" &&
git merge branch
@@ -143,11 +162,12 @@ test_expect_success 'more setup' '
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 ls-files -s | sed \"s- -&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)"'
+ git diff --exit-code HEAD directorymoved:newsubdir
+'
test_expect_success 'stops when msg filter fails' '
old=$(git rev-parse HEAD) &&
@@ -162,10 +182,11 @@ test_expect_success 'author information is preserved' '
test_tick &&
GIT_AUTHOR_NAME="B V Uips" git commit -m bvuips &&
git branch preserved-author &&
- git filter-branch -f --msg-filter "cat; \
+ (sane_unset GIT_AUTHOR_NAME &&
+ git filter-branch -f --msg-filter "cat; \
test \$GIT_COMMIT != $(git rev-parse master) || \
echo Hallo" \
- preserved-author &&
+ preserved-author) &&
test 1 = $(git rev-list --author="B V Uips" preserved-author | wc -l)
'
@@ -272,6 +293,19 @@ test_expect_success 'Tag name filtering strips gpg signature' '
test_cmp expect actual
'
+test_expect_success GPG 'Filtering retains message of gpg signed commit' '
+ mkdir gpg &&
+ touch gpg/foo &&
+ git add gpg &&
+ test_tick &&
+ git commit -S -m "Adding gpg" &&
+
+ git log -1 --format="%s" > expect &&
+ git filter-branch -f --msg-filter "cat" &&
+ git log -1 --format="%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 &&
@@ -282,12 +316,31 @@ test_expect_success 'Tag name filtering allows slashes in tag names' '
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 &&
+ test_commit to_remove &&
+ git filter-branch -f --index-filter "git update-index --remove to_remove.t" --prune-empty HEAD &&
git rev-list HEAD > actual &&
test_cmp expect actual
'
+test_expect_success 'prune empty collapsed merges' '
+ test_config merge.ff false &&
+ git rev-list HEAD >expect &&
+ test_commit to_remove_2 &&
+ git reset --hard HEAD^ &&
+ test_merge non-ff to_remove_2 &&
+ git filter-branch -f --index-filter "git update-index --remove to_remove_2.t" --prune-empty HEAD &&
+ git rev-list HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'prune empty works even without index/tree filters' '
+ git rev-list HEAD >expect &&
+ git commit --allow-empty -m empty &&
+ git filter-branch -f --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 &&
@@ -306,6 +359,24 @@ test_expect_success '--remap-to-ancestor with filename filters' '
test $orig_invariant = $(git rev-parse invariant)
'
+test_expect_success 'automatic remapping to ancestor with filename filters' '
+ git checkout master &&
+ git reset --hard A &&
+ test_commit add-foo2 foo 1 &&
+ git branch moved-foo2 &&
+ test_commit add-bar2 bar a &&
+ git branch invariant2 &&
+ orig_invariant=$(git rev-parse invariant2) &&
+ git branch moved-bar2 &&
+ test_commit change-foo2 foo 2 &&
+ git filter-branch -f \
+ moved-foo2 moved-bar2 A..master \
+ -- -- foo &&
+ test $(git rev-parse moved-foo2) = $(git rev-parse moved-bar2) &&
+ test $(git rev-parse moved-foo2) = $(git rev-parse master^) &&
+ test $orig_invariant = $(git rev-parse invariant2)
+'
+
test_expect_success 'setup submodule' '
rm -fr ?* .git &&
git init &&
@@ -324,7 +395,7 @@ test_expect_success 'setup submodule' '
git branch original HEAD
'
-orig_head=`git show-ref --hash --head 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 && {
@@ -333,7 +404,7 @@ test_expect_success 'rewrite submodule with another content' '
mkdir submod &&
: > submod/file
} || :" HEAD &&
- test $orig_head != `git show-ref --hash --head HEAD`
+ test $orig_head != $(git show-ref --hash --head HEAD)
'
test_expect_success 'replace submodule revision' '
@@ -342,7 +413,24 @@ test_expect_success 'replace submodule revision' '
"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 $orig_head != $(git show-ref --hash --head HEAD)
+'
+
+test_expect_success 'filter commit message without trailing newline' '
+ git reset --hard original &&
+ commit=$(printf "no newline" | git commit-tree HEAD^{tree}) &&
+ git update-ref refs/heads/no-newline $commit &&
+ git filter-branch -f refs/heads/no-newline &&
+ echo $commit >expect &&
+ git rev-parse refs/heads/no-newline >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'tree-filter deals with object name vs pathname ambiguity' '
+ test_when_finished "git reset --hard original" &&
+ ambiguous=$(git rev-list -1 HEAD) &&
+ git filter-branch --tree-filter "mv file.t $ambiguous" HEAD^.. &&
+ git show HEAD:$ambiguous
'
test_done
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index ac943f5..f9b7d79 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -8,6 +8,7 @@ test_description='git tag
Tests for operations with tags.'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
# creating and listing lightweight tags:
@@ -22,8 +23,8 @@ test_expect_success 'listing all tags in an empty tree should succeed' '
'
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 $(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' \
@@ -50,7 +51,19 @@ test_expect_success 'creating a tag using default HEAD should succeed' '
echo foo >foo &&
git add foo &&
git commit -m Foo &&
- git tag mytag
+ git tag mytag &&
+ test_must_fail git reflog exists refs/tags/mytag
+'
+
+test_expect_success 'creating a tag with --create-reflog should create reflog' '
+ test_when_finished "git tag -d tag_with_reflog" &&
+ git tag --create-reflog tag_with_reflog &&
+ git reflog exists refs/tags/tag_with_reflog
+'
+
+test_expect_success '--create-reflog does not create reflog on failure' '
+ test_must_fail git tag --create-reflog mytag &&
+ test_must_fail git reflog exists refs/tags/mytag
'
test_expect_success 'listing all tags if one exists should succeed' '
@@ -59,8 +72,8 @@ test_expect_success 'listing all tags if one exists should succeed' '
'
test_expect_success 'listing all tags if one exists should output that tag' '
- test `git tag -l` = mytag &&
- test `git tag` = mytag
+ test $(git tag -l) = mytag &&
+ test $(git tag) = mytag
'
# pattern matching:
@@ -70,7 +83,7 @@ test_expect_success 'listing a tag using a matching pattern should succeed' \
test_expect_success \
'listing a tag using a matching pattern should output that tag' \
- 'test `git tag -l mytag` = mytag'
+ 'test $(git tag -l mytag) = mytag'
# todo: git tag -l now returns always zero, when fixed, change this test
test_expect_success \
@@ -79,7 +92,7 @@ test_expect_success \
test_expect_success \
'listing tags using a non-matching pattern should output nothing' \
- 'test `git tag -l xxx | wc -l` -eq 0'
+ 'test $(git tag -l xxx | wc -l) -eq 0'
# special cases for creating tags:
@@ -89,13 +102,13 @@ test_expect_success \
test_expect_success \
'trying to create a tag with a non-valid name should fail' '
- test `git tag -l | wc -l` -eq 1 &&
+ 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 $(git tag -l | wc -l) -eq 1
'
test_expect_success 'creating a tag using HEAD directly should succeed' '
@@ -103,6 +116,18 @@ test_expect_success 'creating a tag using HEAD directly should succeed' '
tag_exists myhead
'
+test_expect_success '--force can create a tag with the name of one existing' '
+ tag_exists mytag &&
+ git tag --force mytag &&
+ tag_exists mytag'
+
+test_expect_success '--force is moot with a non-existing tag name' '
+ git tag newtag >expect &&
+ git tag --force forcetag >actual &&
+ test_cmp expect actual
+'
+git tag -d newtag forcetag
+
# deleting tags:
test_expect_success 'trying to delete an unknown tag should fail' '
@@ -257,6 +282,55 @@ test_expect_success \
test_cmp expect actual
'
+test_expect_success 'tag -l can accept multiple patterns' '
+ git tag -l "v1*" "v0*" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'listing tags in column' '
+ COLUMNS=40 git tag -l --column=row >actual &&
+ cat >expected <<\EOF &&
+a1 aa1 cba t210 t211
+v0.2.1 v1.0 v1.0.1 v1.1.3
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'listing tags in column with column.*' '
+ git config column.tag row &&
+ git config column.ui dense &&
+ COLUMNS=40 git tag -l >actual &&
+ git config --unset column.ui &&
+ git config --unset column.tag &&
+ cat >expected <<\EOF &&
+a1 aa1 cba t210 t211
+v0.2.1 v1.0 v1.0.1 v1.1.3
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'listing tag with -n --column should fail' '
+ test_must_fail git tag --column -n
+'
+
+test_expect_success 'listing tags -n in column with column.ui ignored' '
+ git config column.ui "row dense" &&
+ COLUMNS=40 git tag -l -n >actual &&
+ git config --unset column.ui &&
+ cat >expected <<\EOF &&
+a1 Foo
+aa1 Foo
+cba Foo
+t210 Foo
+t211 Foo
+v0.2.1 Foo
+v1.0 Foo
+v1.0.1 Foo
+v1.1.3 Foo
+EOF
+ test_cmp expected actual
+'
+
# creating and verifying lightweight tags:
test_expect_success \
@@ -580,23 +654,18 @@ test_expect_success \
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
+test_expect_success 'annotations for blobs are empty' '
+ blob=$(git hash-object -w --stdin <<-\EOF
+ Blob paragraph 1.
+
+ Blob paragraph 2.
+ EOF
+ ) &&
+ git tag tag-blob $blob &&
+ echo "tag-blob " >expect &&
+ git tag -n1 -l tag-blob >actual &&
+ test_cmp expect actual
+'
# trying to verify annotated non-signed tags:
@@ -620,16 +689,6 @@ test_expect_success GPG \
# 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
@@ -716,6 +775,47 @@ test_expect_success GPG '-s implies annotated tag' '
test_cmp expect actual
'
+get_tag_header forcesignannotated-implied-sign $commit commit $time >expect
+echo "A message" >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'git tag -s implied if configured with tag.forcesignannotated' \
+ 'test_config tag.forcesignannotated true &&
+ git tag -m "A message" forcesignannotated-implied-sign &&
+ get_tag_msg forcesignannotated-implied-sign >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG \
+ 'lightweight with no message when configured with tag.forcesignannotated' \
+ 'test_config tag.forcesignannotated true &&
+ git tag forcesignannotated-lightweight &&
+ tag_exists forcesignannotated-lightweight &&
+ test_must_fail git tag -v forcesignannotated-no-message
+'
+
+get_tag_header forcesignannotated-annotate $commit commit $time >expect
+echo "A message" >>expect
+test_expect_success GPG \
+ 'git tag -a disable configured tag.forcesignannotated' \
+ 'test_config tag.forcesignannotated true &&
+ git tag -a -m "A message" forcesignannotated-annotate &&
+ get_tag_msg forcesignannotated-annotate >actual &&
+ test_cmp expect actual &&
+ test_must_fail git tag -v forcesignannotated-annotate
+'
+
+get_tag_header forcesignannotated-disabled $commit commit $time >expect
+echo "A message" >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'git tag --sign enable GPG sign' \
+ 'test_config tag.forcesignannotated false &&
+ git tag --sign -m "A message" forcesignannotated-disabled &&
+ get_tag_msg forcesignannotated-disabled >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 &&
@@ -1030,6 +1130,75 @@ test_expect_success GPG \
test_cmp expect actual
'
+# usage with rfc1991 signatures
+get_tag_header rfc1991-signed-tag $commit commit $time >expect
+echo "RFC1991 signed tag" >>expect
+echo '-----BEGIN PGP MESSAGE-----' >>expect
+test_expect_success GPG,RFC1991 \
+ 'creating a signed tag with rfc1991' '
+ echo "rfc1991" >gpghome/gpg.conf &&
+ git tag -s -m "RFC1991 signed tag" rfc1991-signed-tag $commit &&
+ get_tag_msg rfc1991-signed-tag >actual &&
+ test_cmp expect actual
+'
+
+cat >fakeeditor <<'EOF'
+#!/bin/sh
+cp "$1" actual
+EOF
+chmod +x fakeeditor
+
+test_expect_success GPG,RFC1991 \
+ 'reediting a signed tag body omits signature' '
+ echo "rfc1991" >gpghome/gpg.conf &&
+ echo "RFC1991 signed tag" >expect &&
+ GIT_EDITOR=./fakeeditor git tag -f -s rfc1991-signed-tag $commit &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG,RFC1991 \
+ 'verifying rfc1991 signature' '
+ echo "rfc1991" >gpghome/gpg.conf &&
+ git tag -v rfc1991-signed-tag
+'
+
+test_expect_success GPG,RFC1991 \
+ 'list tag with rfc1991 signature' '
+ echo "rfc1991" >gpghome/gpg.conf &&
+ echo "rfc1991-signed-tag RFC1991 signed tag" >expect &&
+ git tag -l -n1 rfc1991-signed-tag >actual &&
+ test_cmp expect actual &&
+ git tag -l -n2 rfc1991-signed-tag >actual &&
+ test_cmp expect actual &&
+ git tag -l -n999 rfc1991-signed-tag >actual &&
+ test_cmp expect actual
+'
+
+rm -f gpghome/gpg.conf
+
+test_expect_success GPG,RFC1991 \
+ 'verifying rfc1991 signature without --rfc1991' '
+ git tag -v rfc1991-signed-tag
+'
+
+test_expect_success GPG,RFC1991 \
+ 'list tag with rfc1991 signature without --rfc1991' '
+ echo "rfc1991-signed-tag RFC1991 signed tag" >expect &&
+ git tag -l -n1 rfc1991-signed-tag >actual &&
+ test_cmp expect actual &&
+ git tag -l -n2 rfc1991-signed-tag >actual &&
+ test_cmp expect actual &&
+ git tag -l -n999 rfc1991-signed-tag >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG,RFC1991 \
+ 'reediting a signed tag body omits signature' '
+ echo "RFC1991 signed tag" >expect &&
+ GIT_EDITOR=./fakeeditor git tag -f -s rfc1991-signed-tag $commit &&
+ test_cmp expect actual
+'
+
# try to sign with bad user.signingkey
git config user.signingkey BobTheMouse
test_expect_success GPG \
@@ -1051,13 +1220,22 @@ test_expect_success \
test_expect_success \
'message in editor has initial comment' '
- GIT_EDITOR=cat git tag -a initial-comment > actual
+ ! (GIT_EDITOR=cat git tag -a initial-comment > actual)
+'
+
+test_expect_success 'message in editor has initial comment: first line' '
# check the first line --- should be empty
- first=$(sed -e 1q <actual) &&
- test -z "$first" &&
+ echo >first.expect &&
+ sed -e 1q <actual >first.actual &&
+ test_i18ncmp first.expect first.actual
+'
+
+test_expect_success \
+ 'message in editor has initial comment: remainder' '
# remove commented lines from the remainder -- should be empty
- rest=$(sed -e 1d -e '/^#/d' <actual) &&
- test -z "$rest"
+ >rest.expect &&
+ sed -e 1d -e "/^#/d" <actual >rest.actual &&
+ test_cmp rest.expect rest.actual
'
get_tag_header reuse $commit commit $time >expect
@@ -1097,7 +1275,7 @@ 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 commit -m second &&
git tag v2.0
'
@@ -1122,18 +1300,18 @@ v2.0
EOF
test_expect_success 'checking that first commit is in all tags (hash)' "
- git tag -l --contains $hash1 v* >actual
+ 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
+ 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
+ git tag -l --contains HEAD~2 v* >actual &&
test_cmp expected actual
"
@@ -1142,7 +1320,7 @@ v2.0
EOF
test_expect_success 'checking that second commit only has one tag' "
- git tag -l --contains $hash2 v* >actual
+ git tag -l --contains $hash2 v* >actual &&
test_cmp expected actual
"
@@ -1151,7 +1329,7 @@ cat > expected <<EOF
EOF
test_expect_success 'checking that third commit has no tags' "
- git tag -l --contains $hash3 v* >actual
+ git tag -l --contains $hash3 v* >actual &&
test_cmp expected actual
"
@@ -1161,7 +1339,7 @@ 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 commit foo -m fourth &&
git tag v3.0
'
@@ -1172,7 +1350,7 @@ v3.0
EOF
test_expect_success 'checking that branch head only has one tag' "
- git tag -l --contains $hash4 v* >actual
+ git tag -l --contains $hash4 v* >actual &&
test_cmp expected actual
"
@@ -1186,7 +1364,7 @@ v4.0
EOF
test_expect_success 'checking that original branch head has one tag now' "
- git tag -l --contains $hash3 v* >actual
+ git tag -l --contains $hash3 v* >actual &&
test_cmp expected actual
"
@@ -1201,19 +1379,232 @@ v4.0
EOF
test_expect_success 'checking that initial commit is in all tags' "
- git tag -l --contains $hash1 v* >actual
+ 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 -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
'
+# check points-at
+
+test_expect_success '--points-at cannot be used in non-list mode' '
+ test_must_fail git tag --points-at=v4.0 foo
+'
+
+test_expect_success '--points-at finds lightweight tags' '
+ echo v4.0 >expect &&
+ git tag --points-at v4.0 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--points-at finds annotated tags of commits' '
+ git tag -m "v4.0, annotated" annotated-v4.0 v4.0 &&
+ echo annotated-v4.0 >expect &&
+ git tag -l --points-at v4.0 "annotated*" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--points-at finds annotated tags of tags' '
+ git tag -m "describing the v4.0 tag object" \
+ annotated-again-v4.0 annotated-v4.0 &&
+ cat >expect <<-\EOF &&
+ annotated-again-v4.0
+ annotated-v4.0
+ EOF
+ git tag --points-at=annotated-v4.0 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'multiple --points-at are OR-ed together' '
+ cat >expect <<-\EOF &&
+ v2.0
+ v3.0
+ EOF
+ git tag --points-at=v2.0 --points-at=v3.0 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'lexical sort' '
+ git tag foo1.3 &&
+ git tag foo1.6 &&
+ git tag foo1.10 &&
+ git tag -l --sort=refname "foo*" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.10
+ foo1.3
+ foo1.6
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'version sort' '
+ git tag -l --sort=version:refname "foo*" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.3
+ foo1.6
+ foo1.10
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'reverse version sort' '
+ git tag -l --sort=-version:refname "foo*" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.10
+ foo1.6
+ foo1.3
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'reverse lexical sort' '
+ git tag -l --sort=-refname "foo*" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.6
+ foo1.3
+ foo1.10
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'configured lexical sort' '
+ git config tag.sort "v:refname" &&
+ git tag -l "foo*" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.3
+ foo1.6
+ foo1.10
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'option override configured sort' '
+ git tag -l --sort=-refname "foo*" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.6
+ foo1.3
+ foo1.10
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'invalid sort parameter on command line' '
+ test_must_fail git tag -l --sort=notvalid "foo*" >actual
+'
+
+test_expect_success 'invalid sort parameter in configuratoin' '
+ git config tag.sort "v:notvalid" &&
+ test_must_fail git tag -l "foo*"
+'
+
+test_expect_success 'version sort with prerelease reordering' '
+ git config --unset tag.sort &&
+ git config versionsort.prereleaseSuffix -rc &&
+ git tag foo1.6-rc1 &&
+ git tag foo1.6-rc2 &&
+ git tag -l --sort=version:refname "foo*" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.3
+ foo1.6-rc1
+ foo1.6-rc2
+ foo1.6
+ foo1.10
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'reverse version sort with prerelease reordering' '
+ git tag -l --sort=-version:refname "foo*" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.10
+ foo1.6
+ foo1.6-rc2
+ foo1.6-rc1
+ foo1.3
+ EOF
+ test_cmp expect actual
+'
+
+run_with_limited_stack () {
+ (ulimit -s 128 && "$@")
+}
+
+test_lazy_prereq ULIMIT_STACK_SIZE 'run_with_limited_stack true'
+
+# we require ulimit, this excludes Windows
+test_expect_success ULIMIT_STACK_SIZE '--contains works in a deep repo' '
+ >expect &&
+ i=1 &&
+ while test $i -lt 8000
+ do
+ echo "commit refs/heads/master
+committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200
+data <<EOF
+commit #$i
+EOF"
+ test $i = 1 && echo "from refs/heads/master^0"
+ i=$(($i + 1))
+ done | git fast-import &&
+ git checkout master &&
+ git tag far-far-away HEAD^ &&
+ run_with_limited_stack git tag --contains HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--format should list tags as per format given' '
+ cat >expect <<-\EOF &&
+ refname : refs/tags/foo1.10
+ refname : refs/tags/foo1.3
+ refname : refs/tags/foo1.6
+ refname : refs/tags/foo1.6-rc1
+ refname : refs/tags/foo1.6-rc2
+ EOF
+ git tag -l --format="refname : %(refname)" "foo*" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup --merged test tags' '
+ git tag mergetest-1 HEAD~2 &&
+ git tag mergetest-2 HEAD~1 &&
+ git tag mergetest-3 HEAD
+'
+
+test_expect_success '--merged cannot be used in non-list mode' '
+ test_must_fail git tag --merged=mergetest-2 foo
+'
+
+test_expect_success '--merged shows merged tags' '
+ cat >expect <<-\EOF &&
+ mergetest-1
+ mergetest-2
+ EOF
+ git tag -l --merged=mergetest-2 mergetest-* >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--no-merged show unmerged tags' '
+ cat >expect <<-\EOF &&
+ mergetest-3
+ EOF
+ git tag -l --no-merged=mergetest-2 mergetest-* >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'ambiguous branch/tags not marked' '
+ git tag ambiguous &&
+ git branch ambiguous &&
+ echo ambiguous >expect &&
+ git tag -l ambiguous >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t7004/pubring.gpg b/t/t7004/pubring.gpg
deleted file mode 100644
index 83855fa..0000000
--- a/t/t7004/pubring.gpg
+++ /dev/null
Binary files differ
diff --git a/t/t7004/random_seed b/t/t7004/random_seed
deleted file mode 100644
index 8fed133..0000000
--- a/t/t7004/random_seed
+++ /dev/null
Binary files differ
diff --git a/t/t7004/secring.gpg b/t/t7004/secring.gpg
deleted file mode 100644
index d831cd9..0000000
--- a/t/t7004/secring.gpg
+++ /dev/null
Binary files differ
diff --git a/t/t7004/trustdb.gpg b/t/t7004/trustdb.gpg
deleted file mode 100644
index abace96..0000000
--- a/t/t7004/trustdb.gpg
+++ /dev/null
Binary files differ
diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh
index 26ddf9d..1b530b5 100755
--- a/t/t7005-editor.sh
+++ b/t/t7005-editor.sh
@@ -111,13 +111,13 @@ do
'
done
-if ! echo 'echo space > "$1"' > "e space.sh"
+if echo 'echo space > "$1"' > "e space.sh"
then
- skip_all="Skipping; FS does not support spaces in filenames"
- test_done
+ # FS supports spaces in filenames
+ test_set_prereq SPACES_IN_FILENAMES
fi
-test_expect_success 'editor with a space' '
+test_expect_success SPACES_IN_FILENAMES 'editor with a space' '
chmod a+x "e space.sh" &&
GIT_EDITOR="./e\ space.sh" git commit --amend &&
@@ -126,7 +126,7 @@ test_expect_success 'editor with a space' '
'
unset GIT_EDITOR
-test_expect_success 'core.editor with a space' '
+test_expect_success SPACES_IN_FILENAMES 'core.editor with a space' '
git config core.editor \"./e\ space.sh\" &&
git commit --amend &&
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
index 71d3cef..e4fc5c8 100755
--- a/t/t7006-pager.sh
+++ b/t/t7006-pager.sh
@@ -4,45 +4,11 @@ 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_DIRECTORY"/lib-terminal.sh
test_expect_success 'setup' '
- unset GIT_PAGER GIT_PAGER_IN_USE;
- test_might_fail git config --unset core.pager &&
+ sane_unset GIT_PAGER GIT_PAGER_IN_USE &&
+ test_unconfig core.pager &&
PAGER="cat >paginated.out" &&
export PAGER &&
@@ -51,61 +17,110 @@ test_expect_success 'setup' '
'
test_expect_success TTY 'some commands use a pager' '
- rm -f paginated.out ||
- cleanup_fail &&
-
+ rm -f paginated.out &&
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_expect_failure TTY 'pager runs from subdir' '
+ echo subdir/paginated.out >expected &&
+ mkdir -p subdir &&
+ rm -f paginated.out subdir/paginated.out &&
+ (
+ cd subdir &&
+ test_terminal git log
+ ) &&
+ {
+ ls paginated.out subdir/paginated.out ||
+ :
+ } >actual &&
+ test_cmp expected actual
+'
+test_expect_success TTY 'LESS and LV envvars are set for pagination' '
+ (
+ sane_unset LESS LV &&
+ PAGER="env >pager-env.out; wc" &&
+ export PAGER &&
+
+ test_terminal git log
+ ) &&
+ grep ^LESS= pager-env.out &&
+ grep ^LV= pager-env.out
+'
+
+test_expect_success TTY 'some commands do not use a pager' '
+ rm -f paginated.out &&
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 &&
-
+ rm -f paginated.out &&
git log | cat &&
! test -e paginated.out
'
test_expect_success 'no pager when stdout is a regular file' '
- rm -f paginated.out ||
- cleanup_fail &&
-
+ rm -f paginated.out &&
git log >file &&
! test -e paginated.out
'
test_expect_success TTY 'git --paginate rev-list uses a pager' '
- rm -f paginated.out ||
- cleanup_fail &&
-
+ rm -f paginated.out &&
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 &&
-
+ rm -f file paginated.out &&
git --paginate log | cat &&
! test -e paginated.out
'
test_expect_success TTY 'no pager with --no-pager' '
- rm -f paginated.out ||
- cleanup_fail &&
-
+ rm -f paginated.out &&
test_terminal git --no-pager log &&
! test -e paginated.out
'
+test_expect_success TTY 'configuration can disable pager' '
+ rm -f paginated.out &&
+ test_unconfig pager.grep &&
+ test_terminal git grep initial &&
+ test -e paginated.out &&
+
+ rm -f paginated.out &&
+ test_config pager.grep false &&
+ test_terminal git grep initial &&
+ ! test -e paginated.out
+'
+
+test_expect_success TTY 'git config uses a pager if configured to' '
+ rm -f paginated.out &&
+ test_config pager.config true &&
+ test_terminal git config --list &&
+ test -e paginated.out
+'
+
+test_expect_success TTY 'configuration can enable pager (from subdir)' '
+ rm -f paginated.out &&
+ mkdir -p subdir &&
+ test_config pager.bundle true &&
+
+ git bundle create test.bundle --all &&
+ rm -f paginated.out subdir/paginated.out &&
+ (
+ cd subdir &&
+ test_terminal git bundle unbundle ../test.bundle
+ ) &&
+ {
+ test -e paginated.out ||
+ test -e subdir/paginated.out
+ }
+'
+
# A colored commit log will begin with an appropriate ANSI escape
# for the first color; the text "commit" comes later.
colorful() {
@@ -114,9 +129,7 @@ colorful() {
}
test_expect_success 'tests can detect color' '
- rm -f colorful.log colorless.log ||
- cleanup_fail &&
-
+ rm -f colorful.log colorless.log &&
git log --no-color >colorless.log &&
git log --color >colorful.log &&
! colorful colorless.log &&
@@ -125,31 +138,29 @@ test_expect_success 'tests can detect color' '
test_expect_success 'no color when stdout is a regular file' '
rm -f colorless.log &&
- git config color.ui auto ||
- cleanup_fail &&
-
+ test_config color.ui auto &&
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
- ) &&
+ test_config color.ui auto &&
+ test_terminal env TERM=vt100 git log &&
colorful paginated.out
'
+test_expect_success TTY 'colors are suppressed by color.pager' '
+ rm -f paginated.out &&
+ test_config color.ui auto &&
+ test_config color.pager false &&
+ test_terminal env TERM=vt100 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 &&
-
+ test_config color.ui auto &&
(
TERM=vt100 &&
GIT_PAGER_IN_USE=true &&
@@ -159,10 +170,12 @@ test_expect_success 'color when writing to a file intended for a pager' '
colorful colorful.log
'
-if test_have_prereq SIMPLEPAGER && test_have_prereq TTY
-then
- test_set_prereq SIMPLEPAGERTTY
-fi
+test_expect_success TTY 'colors are sent to pager for external commands' '
+ test_config alias.externallog "!git log" &&
+ test_config color.ui auto &&
+ test_terminal env TERM=vt100 git -p externallog &&
+ colorful paginated.out
+'
# Use this helper to make it easy for the caller of your
# terminal-using function to specify whether it should fail.
@@ -199,12 +212,10 @@ parse_args() {
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 &&
-
+ $test_expectation SIMPLEPAGER,TTY "$cmd - default pager is used by default" "
+ sane_unset PAGER GIT_PAGER &&
+ test_unconfig core.pager &&
+ rm -f default_pager_used &&
cat >\$less <<-\EOF &&
#!/bin/sh
wc >default_pager_used
@@ -223,11 +234,9 @@ 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 &&
-
+ sane_unset GIT_PAGER &&
+ test_unconfig core.pager &&
+ rm -f PAGER_used &&
PAGER='wc >PAGER_used' &&
export PAGER &&
$full_command &&
@@ -251,13 +260,11 @@ 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 &&
-
+ sane_unset GIT_PAGER &&
+ rm -f core.pager_used &&
PAGER=wc &&
export PAGER &&
- git config core.pager 'wc >core.pager_used' &&
+ test_config core.pager 'wc >core.pager_used' &&
$full_command &&
${if_local_config}test -e core.pager_used
"
@@ -279,15 +286,13 @@ test_pager_subdir_helper() {
parse_args "$@"
$test_expectation TTY "$cmd - core.pager $used_if_wanted from subdirectory" "
- unset GIT_PAGER;
+ sane_unset GIT_PAGER &&
rm -f core.pager_used &&
- rm -fr sub ||
- cleanup_fail &&
-
+ rm -fr sub &&
PAGER=wc &&
stampname=\$(pwd)/core.pager_used &&
export PAGER stampname &&
- git config core.pager 'wc >\"\$stampname\"' &&
+ test_config core.pager 'wc >\"\$stampname\"' &&
mkdir sub &&
(
cd sub &&
@@ -301,10 +306,8 @@ 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 &&
+ rm -f GIT_PAGER_used &&
+ test_config core.pager wc &&
GIT_PAGER='wc >GIT_PAGER_used' &&
export GIT_PAGER &&
$full_command &&
@@ -316,9 +319,7 @@ test_doesnt_paginate() {
parse_args "$@"
$test_expectation TTY "no pager for '$cmd'" "
- rm -f GIT_PAGER_used ||
- cleanup_fail &&
-
+ rm -f GIT_PAGER_used &&
GIT_PAGER='wc >GIT_PAGER_used' &&
export GIT_PAGER &&
$full_command &&
@@ -369,4 +370,90 @@ test_GIT_PAGER_overrides expect_success test_must_fail 'git -p'
test_doesnt_paginate expect_failure test_must_fail 'git -p nonsense'
+test_pager_choices 'git shortlog'
+test_expect_success 'setup: configure shortlog not to paginate' '
+ git config pager.shortlog false
+'
+test_doesnt_paginate expect_success 'git shortlog'
+test_no_local_config_subdir expect_success 'git shortlog'
+test_default_pager expect_success 'git -p shortlog'
+test_core_pager_subdir expect_success 'git -p shortlog'
+
+test_core_pager_subdir expect_success test_must_fail \
+ 'git -p apply </dev/null'
+
+test_expect_success TTY 'command-specific pager' '
+ sane_unset PAGER GIT_PAGER &&
+ echo "foo:initial" >expect &&
+ >actual &&
+ test_unconfig core.pager &&
+ test_config pager.log "sed s/^/foo:/ >actual" &&
+ test_terminal git log --format=%s -1 &&
+ test_cmp expect actual
+'
+
+test_expect_success TTY 'command-specific pager overrides core.pager' '
+ sane_unset PAGER GIT_PAGER &&
+ echo "foo:initial" >expect &&
+ >actual &&
+ test_config core.pager "exit 1" &&
+ test_config pager.log "sed s/^/foo:/ >actual" &&
+ test_terminal git log --format=%s -1 &&
+ test_cmp expect actual
+'
+
+test_expect_success TTY 'command-specific pager overridden by environment' '
+ GIT_PAGER="sed s/^/foo:/ >actual" && export GIT_PAGER &&
+ >actual &&
+ echo "foo:initial" >expect &&
+ test_config pager.log "exit 1" &&
+ test_terminal git log --format=%s -1 &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup external command' '
+ cat >git-external <<-\EOF &&
+ #!/bin/sh
+ git "$@"
+ EOF
+ chmod +x git-external
+'
+
+test_expect_success TTY 'command-specific pager works for external commands' '
+ sane_unset PAGER GIT_PAGER &&
+ echo "foo:initial" >expect &&
+ >actual &&
+ test_config pager.external "sed s/^/foo:/ >actual" &&
+ test_terminal git --exec-path="$(pwd)" external log --format=%s -1 &&
+ test_cmp expect actual
+'
+
+test_expect_success TTY 'sub-commands of externals use their own pager' '
+ sane_unset PAGER GIT_PAGER &&
+ echo "foo:initial" >expect &&
+ >actual &&
+ test_config pager.log "sed s/^/foo:/ >actual" &&
+ test_terminal git --exec-path=. external log --format=%s -1 &&
+ test_cmp expect actual
+'
+
+test_expect_success TTY 'external command pagers override sub-commands' '
+ sane_unset PAGER GIT_PAGER &&
+ >expect &&
+ >actual &&
+ test_config pager.external false &&
+ test_config pager.log "sed s/^/log:/ >actual" &&
+ test_terminal git --exec-path=. external log --format=%s -1 &&
+ test_cmp expect actual
+'
+
+test_expect_success 'command with underscores does not complain' '
+ write_script git-under_score <<-\EOF &&
+ echo ok
+ EOF
+ git --exec-path=. under_score >actual 2>&1 &&
+ echo ok >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t7006/test-terminal.perl b/t/t7006/test-terminal.perl
deleted file mode 100755
index 73ff809..0000000
--- a/t/t7006/test-terminal.perl
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/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
index cce222f..42d3db6 100755
--- a/t/t7007-show.sh
+++ b/t/t7007-show.sh
@@ -17,4 +17,115 @@ test_expect_success 'showing a tag that point at a missing object' '
test_must_fail git --no-pager show foo-tag
'
+test_expect_success 'set up a bit of history' '
+ test_commit main1 &&
+ test_commit main2 &&
+ test_commit main3 &&
+ git tag -m "annotated tag" annotated &&
+ git checkout -b side HEAD^^ &&
+ test_commit side2 &&
+ test_commit side3 &&
+ test_merge merge main3
+'
+
+test_expect_success 'showing two commits' '
+ cat >expect <<-EOF &&
+ commit $(git rev-parse main2)
+ commit $(git rev-parse main3)
+ EOF
+ git show main2 main3 >actual &&
+ grep ^commit actual >actual.filtered &&
+ test_cmp expect actual.filtered
+'
+
+test_expect_success 'showing a range walks (linear)' '
+ cat >expect <<-EOF &&
+ commit $(git rev-parse main3)
+ commit $(git rev-parse main2)
+ EOF
+ git show main1..main3 >actual &&
+ grep ^commit actual >actual.filtered &&
+ test_cmp expect actual.filtered
+'
+
+test_expect_success 'showing a range walks (Y shape, ^ first)' '
+ cat >expect <<-EOF &&
+ commit $(git rev-parse main3)
+ commit $(git rev-parse main2)
+ EOF
+ git show ^side3 main3 >actual &&
+ grep ^commit actual >actual.filtered &&
+ test_cmp expect actual.filtered
+'
+
+test_expect_success 'showing a range walks (Y shape, ^ last)' '
+ cat >expect <<-EOF &&
+ commit $(git rev-parse main3)
+ commit $(git rev-parse main2)
+ EOF
+ git show main3 ^side3 >actual &&
+ grep ^commit actual >actual.filtered &&
+ test_cmp expect actual.filtered
+'
+
+test_expect_success 'showing with -N walks' '
+ cat >expect <<-EOF &&
+ commit $(git rev-parse main3)
+ commit $(git rev-parse main2)
+ EOF
+ git show -2 main3 >actual &&
+ grep ^commit actual >actual.filtered &&
+ test_cmp expect actual.filtered
+'
+
+test_expect_success 'showing annotated tag' '
+ cat >expect <<-EOF &&
+ tag annotated
+ commit $(git rev-parse annotated^{commit})
+ EOF
+ git show annotated >actual &&
+ grep -E "^(commit|tag)" actual >actual.filtered &&
+ test_cmp expect actual.filtered
+'
+
+test_expect_success 'showing annotated tag plus commit' '
+ cat >expect <<-EOF &&
+ tag annotated
+ commit $(git rev-parse annotated^{commit})
+ commit $(git rev-parse side3)
+ EOF
+ git show annotated side3 >actual &&
+ grep -E "^(commit|tag)" actual >actual.filtered &&
+ test_cmp expect actual.filtered
+'
+
+test_expect_success 'showing range' '
+ cat >expect <<-EOF &&
+ commit $(git rev-parse main3)
+ commit $(git rev-parse main2)
+ EOF
+ git show ^side3 annotated >actual &&
+ grep -E "^(commit|tag)" actual >actual.filtered &&
+ test_cmp expect actual.filtered
+'
+
+test_expect_success '-s suppresses diff' '
+ cat >expect <<-\EOF &&
+ merge
+ main3
+ EOF
+ git show -s --format=%s merge main3 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--quiet suppresses diff' '
+ echo main3 >expect &&
+ git show --quiet --format=%s main3 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'show --graph is forbidden' '
+ test_must_fail git show --graph HEAD
+'
+
test_done
diff --git a/t/t7008-grep-binary.sh b/t/t7008-grep-binary.sh
index eb8ca88..9c9c378 100755
--- a/t/t7008-grep-binary.sh
+++ b/t/t7008-grep-binary.sh
@@ -5,7 +5,7 @@ test_description='git grep in binary files'
. ./test-lib.sh
test_expect_success 'setup' "
- printf 'binary\000file\n' >a &&
+ echo 'binaryQfile' | q_to_nul >a &&
git add a &&
git commit -m.
"
@@ -61,7 +61,7 @@ test_expect_success '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' '
+test_expect_success 'git grep ile a' '
git grep ile a
'
@@ -70,33 +70,111 @@ test_expect_failure 'git grep .fi a' '
'
test_expect_success 'git grep -F y<NUL>f a' "
- printf 'y\000f' >f &&
+ printf 'yQf' | q_to_nul >f &&
git grep -f f -F a
"
test_expect_success 'git grep -F y<NUL>x a' "
- printf 'y\000x' >f &&
+ printf 'yQx' | q_to_nul >f &&
test_must_fail git grep -f f -F a
"
test_expect_success 'git grep -Fi Y<NUL>f a' "
- printf 'Y\000f' >f &&
+ printf 'YQf' | q_to_nul >f &&
git grep -f f -Fi a
"
-test_expect_failure 'git grep -Fi Y<NUL>x a' "
- printf 'Y\000x' >f &&
+test_expect_success 'git grep -Fi Y<NUL>x a' "
+ printf 'YQx' | q_to_nul >f &&
test_must_fail git grep -f f -Fi a
"
test_expect_success 'git grep y<NUL>f a' "
- printf 'y\000f' >f &&
+ printf 'yQf' | q_to_nul >f &&
git grep -f f a
"
-test_expect_failure 'git grep y<NUL>x a' "
- printf 'y\000x' >f &&
+test_expect_success 'git grep y<NUL>x a' "
+ printf 'yQx' | q_to_nul >f &&
test_must_fail git grep -f f a
"
+test_expect_success 'grep respects binary diff attribute' '
+ echo text >t &&
+ git add t &&
+ echo t:text >expect &&
+ git grep text t >actual &&
+ test_cmp expect actual &&
+ echo "t -diff" >.gitattributes &&
+ echo "Binary file t matches" >expect &&
+ git grep text t >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'grep --cached respects binary diff attribute' '
+ git grep --cached text t >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'grep --cached respects binary diff attribute (2)' '
+ git add .gitattributes &&
+ rm .gitattributes &&
+ git grep --cached text t >actual &&
+ test_when_finished "git rm --cached .gitattributes" &&
+ test_when_finished "git checkout .gitattributes" &&
+ test_cmp expect actual
+'
+
+test_expect_success 'grep revision respects binary diff attribute' '
+ git commit -m new &&
+ echo "Binary file HEAD:t matches" >expect &&
+ git grep text HEAD -- t >actual &&
+ test_when_finished "git reset HEAD^" &&
+ test_cmp expect actual
+'
+
+test_expect_success 'grep respects not-binary diff attribute' '
+ echo binQary | q_to_nul >b &&
+ git add b &&
+ echo "Binary file b matches" >expect &&
+ git grep bin b >actual &&
+ test_cmp expect actual &&
+ echo "b diff" >.gitattributes &&
+ echo "b:binQary" >expect &&
+ git grep bin b >actual.raw &&
+ nul_to_q <actual.raw >actual &&
+ test_cmp expect actual
+'
+
+cat >nul_to_q_textconv <<'EOF'
+#!/bin/sh
+"$PERL_PATH" -pe 'y/\000/Q/' < "$1"
+EOF
+chmod +x nul_to_q_textconv
+
+test_expect_success 'setup textconv filters' '
+ echo a diff=foo >.gitattributes &&
+ git config diff.foo.textconv "\"$(pwd)\""/nul_to_q_textconv
+'
+
+test_expect_success 'grep does not honor textconv' '
+ test_must_fail git grep Qfile
+'
+
+test_expect_success 'grep --textconv honors textconv' '
+ echo "a:binaryQfile" >expect &&
+ git grep --textconv Qfile >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'grep --no-textconv does not honor textconv' '
+ test_must_fail git grep --no-textconv Qfile
+'
+
+test_expect_success 'grep --textconv blob honors textconv' '
+ echo "HEAD:a:binaryQfile" >expect &&
+ git grep --textconv Qfile HEAD:a >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t7009-filter-branch-null-sha1.sh b/t/t7009-filter-branch-null-sha1.sh
new file mode 100755
index 0000000..c27f90f
--- /dev/null
+++ b/t/t7009-filter-branch-null-sha1.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='filter-branch removal of trees with null sha1'
+. ./test-lib.sh
+
+test_expect_success 'setup: base commits' '
+ test_commit one &&
+ test_commit two &&
+ test_commit three
+'
+
+test_expect_success 'setup: a commit with a bogus null sha1 in the tree' '
+ {
+ git ls-tree HEAD &&
+ printf "160000 commit $_z40\\tbroken\\n"
+ } >broken-tree &&
+ echo "add broken entry" >msg &&
+
+ tree=$(git mktree <broken-tree) &&
+ test_tick &&
+ commit=$(git commit-tree $tree -p HEAD <msg) &&
+ git update-ref HEAD "$commit"
+'
+
+# we have to make one more commit on top removing the broken
+# entry, since otherwise our index does not match HEAD (and filter-branch will
+# complain). We could make the index match HEAD, but doing so would involve
+# writing a null sha1 into the index.
+test_expect_success 'setup: bring HEAD and index in sync' '
+ test_tick &&
+ git commit -a -m "back to normal"
+'
+
+test_expect_success 'filter commands are still checked' '
+ test_must_fail git filter-branch \
+ --force --prune-empty \
+ --index-filter "git rm --cached --ignore-unmatch three.t"
+'
+
+test_expect_success 'removing the broken entry works' '
+ echo three >expect &&
+ git filter-branch \
+ --force --prune-empty \
+ --index-filter "git rm --cached --ignore-unmatch broken" &&
+ git log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t7011-skip-worktree-reading.sh b/t/t7011-skip-worktree-reading.sh
index bb4066f..88d60c1 100755
--- a/t/t7011-skip-worktree-reading.sh
+++ b/t/t7011-skip-worktree-reading.sh
@@ -24,7 +24,7 @@ H sub/2
EOF
NULL_SHA1=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
-ZERO_SHA0=0000000000000000000000000000000000000000
+
setup_absent() {
test -f 1 && rm 1
git update-index --remove 1 &&
@@ -91,12 +91,12 @@ test_expect_success 'update-index --remove' '
test_cmp expected 1
'
-test_expect_success 'ls-files --delete' '
+test_expect_success 'ls-files --deleted' '
setup_absent &&
test -z "$(git ls-files -d)"
'
-test_expect_success 'ls-files --delete' '
+test_expect_success 'ls-files --deleted' '
setup_dirty &&
test -z "$(git ls-files -d)"
'
@@ -120,7 +120,7 @@ test_expect_success 'grep with skip-worktree file' '
test "$(git grep --no-ext-grep test)" = "1:test"
'
-echo ":000000 100644 $ZERO_SHA0 $NULL_SHA1 A 1" > expected
+echo ":000000 100644 $_z40 $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 &&
diff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh
index 582d0b5..9ceaa40 100755
--- a/t/t7012-skip-worktree-writing.sh
+++ b/t/t7012-skip-worktree-writing.sh
@@ -54,7 +54,7 @@ test_expect_success 'read-tree removes worktree, dirty case' '
'
NULL_SHA1=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
-ZERO_SHA0=0000000000000000000000000000000000000000
+
setup_absent() {
test -f 1 && rm 1
git update-index --remove 1 &&
@@ -127,13 +127,13 @@ EOF
test_expect_success 'git-clean, absent case' '
setup_absent &&
git clean -n > result &&
- test_cmp expected result
+ test_i18ncmp expected result
'
test_expect_success 'git-clean, dirty case' '
setup_dirty &&
git clean -n > result &&
- test_cmp expected result
+ test_i18ncmp expected result
'
#TODO test_expect_failure 'git-apply adds file' false
diff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh
new file mode 100755
index 0000000..07079a4
--- /dev/null
+++ b/t/t7030-verify-tag.sh
@@ -0,0 +1,128 @@
+#!/bin/sh
+
+test_description='signed tag tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success GPG 'create signed tags' '
+ echo 1 >file && git add file &&
+ test_tick && git commit -m initial &&
+ git tag -s -m initial initial &&
+ git branch side &&
+
+ echo 2 >file && test_tick && git commit -a -m second &&
+ git tag -s -m second second &&
+
+ git checkout side &&
+ echo 3 >elif && git add elif &&
+ test_tick && git commit -m "third on side" &&
+
+ git checkout master &&
+ test_tick && git merge -S side &&
+ git tag -s -m merge merge &&
+
+ echo 4 >file && test_tick && git commit -a -S -m "fourth unsigned" &&
+ git tag -a -m fourth-unsigned fourth-unsigned &&
+
+ test_tick && git commit --amend -S -m "fourth signed" &&
+ git tag -s -m fourth fourth-signed &&
+
+ echo 5 >file && test_tick && git commit -a -m "fifth" &&
+ git tag fifth-unsigned &&
+
+ git config commit.gpgsign true &&
+ echo 6 >file && test_tick && git commit -a -m "sixth" &&
+ git tag -a -m sixth sixth-unsigned &&
+
+ test_tick && git rebase -f HEAD^^ && git tag -s -m 6th sixth-signed HEAD^ &&
+ git tag -m seventh -s seventh-signed &&
+
+ echo 8 >file && test_tick && git commit -a -m eighth &&
+ git tag -uB7227189 -m eighth eighth-signed-alt
+'
+
+test_expect_success GPG 'verify and show signatures' '
+ (
+ for tag in initial second merge fourth-signed sixth-signed seventh-signed
+ do
+ git verify-tag $tag 2>actual &&
+ grep "Good signature from" actual &&
+ ! grep "BAD signature from" actual &&
+ echo $tag OK || exit 1
+ done
+ ) &&
+ (
+ for tag in fourth-unsigned fifth-unsigned sixth-unsigned
+ do
+ test_must_fail git verify-tag $tag 2>actual &&
+ ! grep "Good signature from" actual &&
+ ! grep "BAD signature from" actual &&
+ echo $tag OK || exit 1
+ done
+ ) &&
+ (
+ for tag in eighth-signed-alt
+ do
+ git verify-tag $tag 2>actual &&
+ grep "Good signature from" actual &&
+ ! grep "BAD signature from" actual &&
+ grep "not certified" actual &&
+ echo $tag OK || exit 1
+ done
+ )
+'
+
+test_expect_success GPG 'detect fudged signature' '
+ git cat-file tag seventh-signed >raw &&
+ sed -e "s/seventh/7th forged/" raw >forged1 &&
+ git hash-object -w -t tag forged1 >forged1.tag &&
+ test_must_fail git verify-tag $(cat forged1.tag) 2>actual1 &&
+ grep "BAD signature from" actual1 &&
+ ! grep "Good signature from" actual1
+'
+
+test_expect_success GPG 'verify signatures with --raw' '
+ (
+ for tag in initial second merge fourth-signed sixth-signed seventh-signed
+ do
+ git verify-tag --raw $tag 2>actual &&
+ grep "GOODSIG" actual &&
+ ! grep "BADSIG" actual &&
+ echo $tag OK || exit 1
+ done
+ ) &&
+ (
+ for tag in fourth-unsigned fifth-unsigned sixth-unsigned
+ do
+ test_must_fail git verify-tag --raw $tag 2>actual &&
+ ! grep "GOODSIG" actual &&
+ ! grep "BADSIG" actual &&
+ echo $tag OK || exit 1
+ done
+ ) &&
+ (
+ for tag in eighth-signed-alt
+ do
+ git verify-tag --raw $tag 2>actual &&
+ grep "GOODSIG" actual &&
+ ! grep "BADSIG" actual &&
+ grep "TRUST_UNDEFINED" actual &&
+ echo $tag OK || exit 1
+ done
+ )
+'
+
+test_expect_success GPG 'verify multiple tags' '
+ tags="fourth-signed sixth-signed seventh-signed" &&
+ for i in $tags
+ do
+ git verify-tag -v --raw $i || return 1
+ done >expect.stdout 2>expect.stderr.1 &&
+ grep "^.GNUPG:." <expect.stderr.1 >expect.stderr &&
+ git verify-tag -v --raw $tags >actual.stdout 2>actual.stderr.1 &&
+ grep "^.GNUPG:." <actual.stderr.1 >actual.stderr &&
+ test_cmp expect.stdout actual.stdout &&
+ test_cmp expect.stderr actual.stderr
+'
+
+test_done
diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh
index fcac472..44bf1d8 100755
--- a/t/t7060-wtstatus.sh
+++ b/t/t7060-wtstatus.sh
@@ -5,6 +5,7 @@ test_description='basic work tree status reporting'
. ./test-lib.sh
test_expect_success setup '
+ git config --global advice.statusuoption false &&
test_commit A &&
test_commit B oneside added &&
git checkout A^0 &&
@@ -28,17 +29,19 @@ test_expect_success 'Report new path with conflict' '
test_cmp expect actual
'
-cat >expect <<EOF
-# On branch side
-# Unmerged paths:
-# (use "git add/rm <file>..." as appropriate to mark resolution)
-#
-# deleted by us: foo
-#
+test_expect_success 'M/D conflict does not segfault' '
+ cat >expect <<EOF &&
+On branch side
+You have unmerged paths.
+ (fix conflicts and run "git commit")
+
+Unmerged paths:
+ (use "git add/rm <file>..." as appropriate to mark resolution)
+
+ deleted by us: foo
+
no changes added to commit (use "git add" and/or "git commit -a")
EOF
-
-test_expect_success 'M/D conflict does not segfault' '
mkdir mdconflict &&
(
cd mdconflict &&
@@ -50,10 +53,179 @@ test_expect_success 'M/D conflict does not segfault' '
git commit -m delete &&
test_must_fail git merge master &&
test_must_fail git commit --dry-run >../actual &&
- test_cmp ../expect ../actual &&
+ test_i18ncmp ../expect ../actual &&
git status >../actual &&
- test_cmp ../expect ../actual
+ test_i18ncmp ../expect ../actual
)
'
+test_expect_success 'rename & unmerged setup' '
+ git rm -f -r . &&
+ cat "$TEST_DIRECTORY/README" >ONE &&
+ git add ONE &&
+ test_tick &&
+ git commit -m "One commit with ONE" &&
+
+ echo Modified >TWO &&
+ cat ONE >>TWO &&
+ cat ONE >>THREE &&
+ git add TWO THREE &&
+ sha1=$(git rev-parse :ONE) &&
+ git rm --cached ONE &&
+ (
+ echo "100644 $sha1 1 ONE" &&
+ echo "100644 $sha1 2 ONE" &&
+ echo "100644 $sha1 3 ONE"
+ ) | git update-index --index-info &&
+ echo Further >>THREE
+'
+
+test_expect_success 'rename & unmerged status' '
+ git status -suno >actual &&
+ cat >expect <<-EOF &&
+ UU ONE
+ AM THREE
+ A TWO
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'git diff-index --cached shows 2 added + 1 unmerged' '
+ cat >expected <<-EOF &&
+ U ONE
+ A THREE
+ A TWO
+ EOF
+ git diff-index --cached --name-status HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'git diff-index --cached -M shows 2 added + 1 unmerged' '
+ cat >expected <<-EOF &&
+ U ONE
+ A THREE
+ A TWO
+ EOF
+ git diff-index --cached -M --name-status HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'git diff-index --cached -C shows 2 copies + 1 unmerged' '
+ cat >expected <<-EOF &&
+ U ONE
+ C ONE THREE
+ C ONE TWO
+ EOF
+ git diff-index --cached -C --name-status HEAD |
+ sed "s/^C[0-9]*/C/g" >actual &&
+ test_cmp expected actual
+'
+
+
+test_expect_success 'status when conflicts with add and rm advice (deleted by them)' '
+ git reset --hard &&
+ git checkout master &&
+ test_commit init main.txt init &&
+ git checkout -b second_branch &&
+ git rm main.txt &&
+ git commit -m "main.txt deleted on second_branch" &&
+ test_commit second conflict.txt second &&
+ git checkout master &&
+ test_commit on_second main.txt on_second &&
+ test_commit master conflict.txt master &&
+ test_must_fail git merge second_branch &&
+ cat >expected <<\EOF &&
+On branch master
+You have unmerged paths.
+ (fix conflicts and run "git commit")
+
+Unmerged paths:
+ (use "git add/rm <file>..." as appropriate to mark resolution)
+
+ both added: conflict.txt
+ deleted by them: main.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'prepare for conflicts' '
+ git reset --hard &&
+ git checkout -b conflict &&
+ test_commit one main.txt one &&
+ git branch conflict_second &&
+ git mv main.txt sub_master.txt &&
+ git commit -m "main.txt renamed in sub_master.txt" &&
+ git checkout conflict_second &&
+ git mv main.txt sub_second.txt &&
+ git commit -m "main.txt renamed in sub_second.txt"
+'
+
+
+test_expect_success 'status when conflicts with add and rm advice (both deleted)' '
+ test_must_fail git merge conflict &&
+ cat >expected <<\EOF &&
+On branch conflict_second
+You have unmerged paths.
+ (fix conflicts and run "git commit")
+
+Unmerged paths:
+ (use "git add/rm <file>..." as appropriate to mark resolution)
+
+ both deleted: main.txt
+ added by them: sub_master.txt
+ added by us: sub_second.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status when conflicts with only rm advice (both deleted)' '
+ git reset --hard conflict_second &&
+ test_must_fail git merge conflict &&
+ git add sub_master.txt &&
+ git add sub_second.txt &&
+ cat >expected <<\EOF &&
+On branch conflict_second
+You have unmerged paths.
+ (fix conflicts and run "git commit")
+
+Changes to be committed:
+
+ new file: sub_master.txt
+
+Unmerged paths:
+ (use "git rm <file>..." to mark resolution)
+
+ both deleted: main.txt
+
+Untracked files not listed (use -u option to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual &&
+ git reset --hard &&
+ git checkout master
+'
+
+test_expect_success 'status --branch with detached HEAD' '
+ git reset --hard &&
+ git checkout master^0 &&
+ git status --branch --porcelain >actual &&
+ cat >expected <<-EOF &&
+ ## HEAD (no branch)
+ ?? .gitconfig
+ ?? actual
+ ?? expect
+ ?? expected
+ ?? mdconflict/
+ EOF
+ test_i18ncmp expected actual
+'
+
test_done
diff --git a/t/t7061-wtstatus-ignore.sh b/t/t7061-wtstatus-ignore.sh
new file mode 100755
index 0000000..cdc0747
--- /dev/null
+++ b/t/t7061-wtstatus-ignore.sh
@@ -0,0 +1,274 @@
+#!/bin/sh
+
+test_description='git-status ignored files'
+
+. ./test-lib.sh
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+?? untracked/
+EOF
+
+test_expect_success 'status untracked directory with --ignored' '
+ echo "ignored" >.gitignore &&
+ mkdir untracked &&
+ : >untracked/ignored &&
+ : >untracked/uncommitted &&
+ git status --porcelain --ignored >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'same with gitignore starting with BOM' '
+ printf "\357\273\277ignored\n" >.gitignore &&
+ mkdir -p untracked &&
+ : >untracked/ignored &&
+ : >untracked/uncommitted &&
+ git status --porcelain --ignored >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+?? untracked/uncommitted
+!! untracked/ignored
+EOF
+
+test_expect_success 'status untracked directory with --ignored -u' '
+ git status --porcelain --ignored -u >actual &&
+ test_cmp expected actual
+'
+cat >expected <<\EOF
+?? untracked/uncommitted
+!! untracked/ignored
+EOF
+
+test_expect_success 'status prefixed untracked directory with --ignored' '
+ git status --porcelain --ignored untracked/ >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? untracked/uncommitted
+!! untracked/ignored
+EOF
+
+test_expect_success 'status prefixed untracked sub-directory with --ignored -u' '
+ git status --porcelain --ignored -u untracked/ >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+!! ignored/
+EOF
+
+test_expect_success 'status ignored directory with --ignore' '
+ rm -rf untracked &&
+ mkdir ignored &&
+ : >ignored/uncommitted &&
+ git status --porcelain --ignored >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+!! ignored/uncommitted
+EOF
+
+test_expect_success 'status ignored directory with --ignore -u' '
+ git status --porcelain --ignored -u >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+EOF
+
+test_expect_success 'status empty untracked directory with --ignore' '
+ rm -rf ignored &&
+ mkdir untracked-ignored &&
+ mkdir untracked-ignored/test &&
+ git status --porcelain --ignored >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+EOF
+
+test_expect_success 'status empty untracked directory with --ignore -u' '
+ git status --porcelain --ignored -u >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+!! untracked-ignored/
+EOF
+
+test_expect_success 'status untracked directory with ignored files with --ignore' '
+ : >untracked-ignored/ignored &&
+ : >untracked-ignored/test/ignored &&
+ git status --porcelain --ignored >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+!! untracked-ignored/ignored
+!! untracked-ignored/test/ignored
+EOF
+
+test_expect_success 'status untracked directory with ignored files with --ignore -u' '
+ git status --porcelain --ignored -u >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+EOF
+
+test_expect_success 'status ignored tracked directory with --ignore' '
+ rm -rf untracked-ignored &&
+ mkdir tracked &&
+ : >tracked/committed &&
+ git add tracked/committed &&
+ git commit -m. &&
+ echo "tracked" >.gitignore &&
+ git status --porcelain --ignored >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+EOF
+
+test_expect_success 'status ignored tracked directory with --ignore -u' '
+ git status --porcelain --ignored -u >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+EOF
+
+test_expect_success 'status ignored tracked directory and ignored file with --ignore' '
+ echo "committed" >>.gitignore &&
+ git status --porcelain --ignored >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+EOF
+
+test_expect_success 'status ignored tracked directory and ignored file with --ignore -u' '
+ git status --porcelain --ignored -u >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+!! tracked/uncommitted
+EOF
+
+test_expect_success 'status ignored tracked directory and uncommitted file with --ignore' '
+ echo "tracked" >.gitignore &&
+ : >tracked/uncommitted &&
+ git status --porcelain --ignored >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+!! tracked/uncommitted
+EOF
+
+test_expect_success 'status ignored tracked directory and uncommitted file with --ignore -u' '
+ git status --porcelain --ignored -u >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+!! tracked/ignored/
+EOF
+
+test_expect_success 'status ignored tracked directory with uncommitted file in untracked subdir with --ignore' '
+ rm -rf tracked/uncommitted &&
+ mkdir tracked/ignored &&
+ : >tracked/ignored/uncommitted &&
+ git status --porcelain --ignored >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+!! tracked/ignored/uncommitted
+EOF
+
+test_expect_success 'status ignored tracked directory with uncommitted file in untracked subdir with --ignore -u' '
+ git status --porcelain --ignored -u >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+!! tracked/ignored/uncommitted
+EOF
+
+test_expect_success 'status ignored tracked directory with uncommitted file in tracked subdir with --ignore' '
+ : >tracked/ignored/committed &&
+ git add -f tracked/ignored/committed &&
+ git commit -m. &&
+ git status --porcelain --ignored >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+!! tracked/ignored/uncommitted
+EOF
+
+test_expect_success 'status ignored tracked directory with uncommitted file in tracked subdir with --ignore -u' '
+ git status --porcelain --ignored -u >actual &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t7062-wtstatus-ignorecase.sh b/t/t7062-wtstatus-ignorecase.sh
new file mode 100755
index 0000000..73709db
--- /dev/null
+++ b/t/t7062-wtstatus-ignorecase.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+test_description='git-status with core.ignorecase=true'
+
+. ./test-lib.sh
+
+test_expect_success 'status with hash collisions' '
+ # note: "V/", "V/XQANY/" and "WURZAUP/" produce the same hash code
+ # in name-hash.c::hash_name
+ mkdir V &&
+ mkdir V/XQANY &&
+ mkdir WURZAUP &&
+ touch V/XQANY/test &&
+ git config core.ignorecase true &&
+ git add . &&
+ # test is successful if git status completes (no endless loop)
+ git status
+'
+
+test_done
diff --git a/t/t7063-status-untracked-cache.sh b/t/t7063-status-untracked-cache.sh
new file mode 100755
index 0000000..a971884
--- /dev/null
+++ b/t/t7063-status-untracked-cache.sh
@@ -0,0 +1,649 @@
+#!/bin/sh
+
+test_description='test untracked cache'
+
+. ./test-lib.sh
+
+avoid_racy() {
+ sleep 1
+}
+
+test_lazy_prereq UNTRACKED_CACHE '
+ { git update-index --test-untracked-cache; ret=$?; } &&
+ test $ret -ne 1
+'
+
+if ! test_have_prereq UNTRACKED_CACHE; then
+ skip_all='This system does not support untracked cache'
+ test_done
+fi
+
+test_expect_success 'core.untrackedCache is unset' '
+ test_must_fail git config --get core.untrackedCache
+'
+
+test_expect_success 'setup' '
+ git init worktree &&
+ cd worktree &&
+ mkdir done dtwo dthree &&
+ touch one two three done/one dtwo/two dthree/three &&
+ git add one two done/one &&
+ : >.git/info/exclude &&
+ git update-index --untracked-cache
+'
+
+test_expect_success 'untracked cache is empty' '
+ test-dump-untracked-cache >../actual &&
+ cat >../expect-empty <<EOF &&
+info/exclude 0000000000000000000000000000000000000000
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+EOF
+ test_cmp ../expect-empty ../actual
+'
+
+cat >../status.expect <<EOF &&
+A done/one
+A one
+A two
+?? dthree/
+?? dtwo/
+?? three
+EOF
+
+cat >../dump.expect <<EOF &&
+info/exclude e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ 0000000000000000000000000000000000000000 recurse valid
+dthree/
+dtwo/
+three
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+three
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+
+test_expect_success 'status first time (empty cache)' '
+ avoid_racy &&
+ : >../trace &&
+ GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ git status --porcelain >../actual &&
+ test_cmp ../status.expect ../actual &&
+ cat >../trace.expect <<EOF &&
+node creation: 3
+gitignore invalidation: 1
+directory invalidation: 0
+opendir: 4
+EOF
+ test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'untracked cache after first status' '
+ test-dump-untracked-cache >../actual &&
+ test_cmp ../dump.expect ../actual
+'
+
+test_expect_success 'status second time (fully populated cache)' '
+ avoid_racy &&
+ : >../trace &&
+ GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ git status --porcelain >../actual &&
+ test_cmp ../status.expect ../actual &&
+ cat >../trace.expect <<EOF &&
+node creation: 0
+gitignore invalidation: 0
+directory invalidation: 0
+opendir: 0
+EOF
+ test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'untracked cache after second status' '
+ test-dump-untracked-cache >../actual &&
+ test_cmp ../dump.expect ../actual
+'
+
+test_expect_success 'modify in root directory, one dir invalidation' '
+ avoid_racy &&
+ : >four &&
+ : >../trace &&
+ GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ git status --porcelain >../actual &&
+ cat >../status.expect <<EOF &&
+A done/one
+A one
+A two
+?? dthree/
+?? dtwo/
+?? four
+?? three
+EOF
+ test_cmp ../status.expect ../actual &&
+ cat >../trace.expect <<EOF &&
+node creation: 0
+gitignore invalidation: 0
+directory invalidation: 1
+opendir: 1
+EOF
+ test_cmp ../trace.expect ../trace
+
+'
+
+test_expect_success 'verify untracked cache dump' '
+ test-dump-untracked-cache >../actual &&
+ cat >../expect <<EOF &&
+info/exclude e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ 0000000000000000000000000000000000000000 recurse valid
+dthree/
+dtwo/
+four
+three
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+three
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+ test_cmp ../expect ../actual
+'
+
+test_expect_success 'new .gitignore invalidates recursively' '
+ avoid_racy &&
+ echo four >.gitignore &&
+ : >../trace &&
+ GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ git status --porcelain >../actual &&
+ cat >../status.expect <<EOF &&
+A done/one
+A one
+A two
+?? .gitignore
+?? dthree/
+?? dtwo/
+?? three
+EOF
+ test_cmp ../status.expect ../actual &&
+ cat >../trace.expect <<EOF &&
+node creation: 0
+gitignore invalidation: 1
+directory invalidation: 1
+opendir: 4
+EOF
+ test_cmp ../trace.expect ../trace
+
+'
+
+test_expect_success 'verify untracked cache dump' '
+ test-dump-untracked-cache >../actual &&
+ cat >../expect <<EOF &&
+info/exclude e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+.gitignore
+dthree/
+dtwo/
+three
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+three
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+ test_cmp ../expect ../actual
+'
+
+test_expect_success 'new info/exclude invalidates everything' '
+ avoid_racy &&
+ echo three >>.git/info/exclude &&
+ : >../trace &&
+ GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ git status --porcelain >../actual &&
+ cat >../status.expect <<EOF &&
+A done/one
+A one
+A two
+?? .gitignore
+?? dtwo/
+EOF
+ test_cmp ../status.expect ../actual &&
+ cat >../trace.expect <<EOF &&
+node creation: 0
+gitignore invalidation: 1
+directory invalidation: 0
+opendir: 4
+EOF
+ test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'verify untracked cache dump' '
+ test-dump-untracked-cache >../actual &&
+ cat >../expect <<EOF &&
+info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+.gitignore
+dtwo/
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+ test_cmp ../expect ../actual
+'
+
+test_expect_success 'move two from tracked to untracked' '
+ git rm --cached two &&
+ test-dump-untracked-cache >../actual &&
+ cat >../expect <<EOF &&
+info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+ test_cmp ../expect ../actual
+'
+
+test_expect_success 'status after the move' '
+ : >../trace &&
+ GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ git status --porcelain >../actual &&
+ cat >../status.expect <<EOF &&
+A done/one
+A one
+?? .gitignore
+?? dtwo/
+?? two
+EOF
+ test_cmp ../status.expect ../actual &&
+ cat >../trace.expect <<EOF &&
+node creation: 0
+gitignore invalidation: 0
+directory invalidation: 0
+opendir: 1
+EOF
+ test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'verify untracked cache dump' '
+ test-dump-untracked-cache >../actual &&
+ cat >../expect <<EOF &&
+info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+.gitignore
+dtwo/
+two
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+ test_cmp ../expect ../actual
+'
+
+test_expect_success 'move two from untracked to tracked' '
+ git add two &&
+ test-dump-untracked-cache >../actual &&
+ cat >../expect <<EOF &&
+info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+ test_cmp ../expect ../actual
+'
+
+test_expect_success 'status after the move' '
+ : >../trace &&
+ GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ git status --porcelain >../actual &&
+ cat >../status.expect <<EOF &&
+A done/one
+A one
+A two
+?? .gitignore
+?? dtwo/
+EOF
+ test_cmp ../status.expect ../actual &&
+ cat >../trace.expect <<EOF &&
+node creation: 0
+gitignore invalidation: 0
+directory invalidation: 0
+opendir: 1
+EOF
+ test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'verify untracked cache dump' '
+ test-dump-untracked-cache >../actual &&
+ cat >../expect <<EOF &&
+info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+.gitignore
+dtwo/
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+ test_cmp ../expect ../actual
+'
+
+test_expect_success 'set up for sparse checkout testing' '
+ echo two >done/.gitignore &&
+ echo three >>done/.gitignore &&
+ echo two >done/two &&
+ git add -f done/two done/.gitignore &&
+ git commit -m "first commit"
+'
+
+test_expect_success 'status after commit' '
+ : >../trace &&
+ GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ git status --porcelain >../actual &&
+ cat >../status.expect <<EOF &&
+?? .gitignore
+?? dtwo/
+EOF
+ test_cmp ../status.expect ../actual &&
+ cat >../trace.expect <<EOF &&
+node creation: 0
+gitignore invalidation: 0
+directory invalidation: 0
+opendir: 2
+EOF
+ test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'untracked cache correct after commit' '
+ test-dump-untracked-cache >../actual &&
+ cat >../expect <<EOF &&
+info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+.gitignore
+dtwo/
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+ test_cmp ../expect ../actual
+'
+
+test_expect_success 'set up sparse checkout' '
+ echo "done/[a-z]*" >.git/info/sparse-checkout &&
+ test_config core.sparsecheckout true &&
+ git checkout master &&
+ git update-index --force-untracked-cache &&
+ git status --porcelain >/dev/null && # prime the cache
+ test_path_is_missing done/.gitignore &&
+ test_path_is_file done/one
+'
+
+test_expect_success 'create/modify files, some of which are gitignored' '
+ echo two bis >done/two &&
+ echo three >done/three && # three is gitignored
+ echo four >done/four && # four is gitignored at a higher level
+ echo five >done/five && # five is not gitignored
+ echo test >base && #we need to ensure that the root dir is touched
+ rm base
+'
+
+test_expect_success 'test sparse status with untracked cache' '
+ : >../trace &&
+ avoid_racy &&
+ GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ git status --porcelain >../status.actual &&
+ cat >../status.expect <<EOF &&
+ M done/two
+?? .gitignore
+?? done/five
+?? dtwo/
+EOF
+ test_cmp ../status.expect ../status.actual &&
+ cat >../trace.expect <<EOF &&
+node creation: 0
+gitignore invalidation: 1
+directory invalidation: 2
+opendir: 2
+EOF
+ test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'untracked cache correct after status' '
+ test-dump-untracked-cache >../actual &&
+ cat >../expect <<EOF &&
+info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+.gitignore
+dtwo/
+/done/ 1946f0437f90c5005533cbe1736a6451ca301714 recurse valid
+five
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+ test_cmp ../expect ../actual
+'
+
+test_expect_success 'test sparse status again with untracked cache' '
+ avoid_racy &&
+ : >../trace &&
+ GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ git status --porcelain >../status.actual &&
+ cat >../status.expect <<EOF &&
+ M done/two
+?? .gitignore
+?? done/five
+?? dtwo/
+EOF
+ test_cmp ../status.expect ../status.actual &&
+ cat >../trace.expect <<EOF &&
+node creation: 0
+gitignore invalidation: 0
+directory invalidation: 0
+opendir: 0
+EOF
+ test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'set up for test of subdir and sparse checkouts' '
+ mkdir done/sub &&
+ mkdir done/sub/sub &&
+ echo "sub" > done/sub/sub/file
+'
+
+test_expect_success 'test sparse status with untracked cache and subdir' '
+ avoid_racy &&
+ : >../trace &&
+ GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ git status --porcelain >../status.actual &&
+ cat >../status.expect <<EOF &&
+ M done/two
+?? .gitignore
+?? done/five
+?? done/sub/
+?? dtwo/
+EOF
+ test_cmp ../status.expect ../status.actual &&
+ cat >../trace.expect <<EOF &&
+node creation: 2
+gitignore invalidation: 0
+directory invalidation: 1
+opendir: 3
+EOF
+ test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'verify untracked cache dump (sparse/subdirs)' '
+ test-dump-untracked-cache >../actual &&
+ cat >../expect-from-test-dump <<EOF &&
+info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+.gitignore
+dtwo/
+/done/ 1946f0437f90c5005533cbe1736a6451ca301714 recurse valid
+five
+sub/
+/done/sub/ 0000000000000000000000000000000000000000 recurse check_only valid
+sub/
+/done/sub/sub/ 0000000000000000000000000000000000000000 recurse check_only valid
+file
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+ test_cmp ../expect-from-test-dump ../actual
+'
+
+test_expect_success 'test sparse status again with untracked cache and subdir' '
+ avoid_racy &&
+ : >../trace &&
+ GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ git status --porcelain >../status.actual &&
+ test_cmp ../status.expect ../status.actual &&
+ cat >../trace.expect <<EOF &&
+node creation: 0
+gitignore invalidation: 0
+directory invalidation: 0
+opendir: 0
+EOF
+ test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'move entry in subdir from untracked to cached' '
+ git add dtwo/two &&
+ git status --porcelain >../status.actual &&
+ cat >../status.expect <<EOF &&
+ M done/two
+A dtwo/two
+?? .gitignore
+?? done/five
+?? done/sub/
+EOF
+ test_cmp ../status.expect ../status.actual
+'
+
+test_expect_success 'move entry in subdir from cached to untracked' '
+ git rm --cached dtwo/two &&
+ git status --porcelain >../status.actual &&
+ cat >../status.expect <<EOF &&
+ M done/two
+?? .gitignore
+?? done/five
+?? done/sub/
+?? dtwo/
+EOF
+ test_cmp ../status.expect ../status.actual
+'
+
+test_expect_success '--no-untracked-cache removes the cache' '
+ git update-index --no-untracked-cache &&
+ test-dump-untracked-cache >../actual &&
+ echo "no untracked cache" >../expect-no-uc &&
+ test_cmp ../expect-no-uc ../actual
+'
+
+test_expect_success 'git status does not change anything' '
+ git status &&
+ test-dump-untracked-cache >../actual &&
+ test_cmp ../expect-no-uc ../actual
+'
+
+test_expect_success 'setting core.untrackedCache to true and using git status creates the cache' '
+ git config core.untrackedCache true &&
+ test-dump-untracked-cache >../actual &&
+ test_cmp ../expect-no-uc ../actual &&
+ git status &&
+ test-dump-untracked-cache >../actual &&
+ test_cmp ../expect-from-test-dump ../actual
+'
+
+test_expect_success 'using --no-untracked-cache does not fail when core.untrackedCache is true' '
+ git update-index --no-untracked-cache &&
+ test-dump-untracked-cache >../actual &&
+ test_cmp ../expect-no-uc ../actual &&
+ git update-index --untracked-cache &&
+ test-dump-untracked-cache >../actual &&
+ test_cmp ../expect-empty ../actual
+'
+
+test_expect_success 'setting core.untrackedCache to false and using git status removes the cache' '
+ git config core.untrackedCache false &&
+ test-dump-untracked-cache >../actual &&
+ test_cmp ../expect-empty ../actual &&
+ git status &&
+ test-dump-untracked-cache >../actual &&
+ test_cmp ../expect-no-uc ../actual
+'
+
+test_expect_success 'using --untracked-cache does not fail when core.untrackedCache is false' '
+ git update-index --untracked-cache &&
+ test-dump-untracked-cache >../actual &&
+ test_cmp ../expect-empty ../actual
+'
+
+test_expect_success 'setting core.untrackedCache to keep' '
+ git config core.untrackedCache keep &&
+ git update-index --untracked-cache &&
+ test-dump-untracked-cache >../actual &&
+ test_cmp ../expect-empty ../actual &&
+ git status &&
+ test-dump-untracked-cache >../actual &&
+ test_cmp ../expect-from-test-dump ../actual &&
+ git update-index --no-untracked-cache &&
+ test-dump-untracked-cache >../actual &&
+ test_cmp ../expect-no-uc ../actual &&
+ git update-index --force-untracked-cache &&
+ test-dump-untracked-cache >../actual &&
+ test_cmp ../expect-empty ../actual &&
+ git status &&
+ test-dump-untracked-cache >../actual &&
+ test_cmp ../expect-from-test-dump ../actual
+'
+
+test_expect_success 'test ident field is working' '
+ mkdir ../other_worktree &&
+ cp -R done dthree dtwo four three ../other_worktree &&
+ GIT_WORK_TREE=../other_worktree git status 2>../err &&
+ echo "warning: Untracked cache is disabled on this system or location." >../expect &&
+ test_cmp ../expect ../err
+'
+
+test_done
diff --git a/t/t7101-reset.sh b/t/t7101-reset-empty-subdirs.sh
index 96e163f..96e163f 100755
--- a/t/t7101-reset.sh
+++ b/t/t7101-reset-empty-subdirs.sh
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index b8cf260..98bcfe2 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -9,6 +9,22 @@ Documented tests for git reset'
. ./test-lib.sh
+commit_msg () {
+ # String "modify 2nd file (changed)" partly in German
+ # (translated with Google Translate),
+ # encoded in UTF-8, used as a commit log message below.
+ msg="modify 2nd file (ge\303\244ndert)\n"
+ if test -n "$1"
+ then
+ printf "$msg" | iconv -f utf-8 -t "$1"
+ else
+ printf "$msg"
+ fi
+}
+
+# Tested non-UTF-8 encoding
+test_encoding="ISO8859-1"
+
test_expect_success 'creating initial files and commits' '
test_tick &&
echo "1st file" >first &&
@@ -28,7 +44,10 @@ test_expect_success 'creating initial files and commits' '
echo "1st line 2nd file" >secondfile &&
echo "2nd line 2nd file" >>secondfile &&
- git commit -a -m "modify 2nd file"
+ # "git commit -m" would break MinGW, as Windows refuse to pass
+ # $test_encoding encoded parameter to git.
+ commit_msg $test_encoding | git -c "i18n.commitEncoding=$test_encoding" commit -a -F - &&
+ head5=$(git rev-parse --verify HEAD)
'
# git log --pretty=oneline # to see those SHA1 involved
@@ -43,6 +62,20 @@ check_changes () {
done | test_cmp .cat_expect -
}
+test_expect_success 'reset --hard message' '
+ hex=$(git log -1 --format="%h") &&
+ git reset --hard > .actual &&
+ echo HEAD is now at $hex $(commit_msg) > .expected &&
+ test_cmp .expected .actual
+'
+
+test_expect_success 'reset --hard message (ISO8859-1 logoutputencoding)' '
+ hex=$(git log -1 --format="%h") &&
+ git -c "i18n.logOutputEncoding=$test_encoding" reset --hard > .actual &&
+ echo HEAD is now at $hex $(commit_msg $test_encoding) > .expected &&
+ test_cmp .expected .actual
+'
+
>.diff_expect
>.cached_expect
cat >.cat_expect <<EOF
@@ -56,7 +89,7 @@ test_expect_success 'giving a non existing revision should fail' '
test_must_fail git reset --mixed aaaaaa &&
test_must_fail git reset --soft aaaaaa &&
test_must_fail git reset --hard aaaaaa &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+ check_changes $head5
'
test_expect_success 'reset --soft with unmerged index should fail' '
@@ -74,7 +107,7 @@ test_expect_success \
test_must_fail git reset --hard -- first &&
test_must_fail git reset --soft HEAD^ -- first &&
test_must_fail git reset --hard HEAD^ -- first &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+ check_changes $head5
'
test_expect_success 'giving unrecognized options should fail' '
@@ -86,7 +119,7 @@ test_expect_success 'giving unrecognized options should fail' '
test_must_fail git reset --soft -o &&
test_must_fail git reset --hard --other &&
test_must_fail git reset --hard -o &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+ check_changes $head5
'
test_expect_success \
@@ -110,7 +143,7 @@ test_expect_success \
git checkout master &&
git branch -D branch1 branch2 &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+ check_changes $head5
'
test_expect_success \
@@ -133,27 +166,27 @@ test_expect_success \
git checkout master &&
git branch -D branch3 branch4 &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+ check_changes $head5
'
test_expect_success \
'resetting to HEAD with no changes should succeed and do nothing' '
git reset --hard &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+ check_changes $head5 &&
git reset --hard HEAD &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+ check_changes $head5 &&
git reset --soft &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+ check_changes $head5 &&
git reset --soft HEAD &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+ check_changes $head5 &&
git reset --mixed &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+ check_changes $head5 &&
git reset --mixed HEAD &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+ check_changes $head5 &&
git reset &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+ check_changes $head5 &&
git reset HEAD &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+ check_changes $head5
'
>.diff_expect
@@ -176,7 +209,7 @@ test_expect_success '--soft reset only should show changes in diff --cached' '
git reset --soft HEAD^ &&
check_changes d1a4bc3abce4829628ae2dcb0d60ef3d1a78b1c4 &&
test "$(git rev-parse ORIG_HEAD)" = \
- 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+ $head5
'
>.diff_expect
@@ -191,9 +224,10 @@ test_expect_success \
'changing files and redo the last commit should succeed' '
echo "3rd line 2nd file" >>secondfile &&
git commit -a -C ORIG_HEAD &&
- check_changes 3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d &&
+ head4=$(git rev-parse --verify HEAD) &&
+ check_changes $head4 &&
test "$(git rev-parse ORIG_HEAD)" = \
- 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+ $head5
'
>.diff_expect
@@ -210,7 +244,7 @@ test_expect_success \
git reset --hard HEAD~2 &&
check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e &&
test "$(git rev-parse ORIG_HEAD)" = \
- 3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d
+ $head4
'
>.diff_expect
@@ -302,8 +336,10 @@ test_expect_success 'redoing the last two commits should succeed' '
echo "1st line 2nd file" >secondfile &&
echo "2nd line 2nd file" >>secondfile &&
- git commit -a -m "modify 2nd file" &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+ # "git commit -m" would break MinGW, as Windows refuse to pass
+ # $test_encoding encoded parameter to git.
+ commit_msg $test_encoding | git -c "i18n.commitEncoding=$test_encoding" commit -a -F - &&
+ check_changes $head5
'
>.diff_expect
@@ -325,10 +361,11 @@ test_expect_success '--hard reset to HEAD should clear a failed merge' '
git checkout branch2 &&
echo "3rd line in branch2" >>secondfile &&
git commit -a -m "change in branch2" &&
+ head3=$(git rev-parse --verify HEAD) &&
test_must_fail git pull . branch1 &&
git reset --hard &&
- check_changes 77abb337073fb4369a7ad69ff6f5ec0e4d6b54bb
+ check_changes $head3
'
>.diff_expect
@@ -341,15 +378,15 @@ EOF
test_expect_success \
'--hard reset to ORIG_HEAD should clear a fast-forward merge' '
git reset --hard HEAD^ &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+ check_changes $head5 &&
git pull . branch1 &&
git reset --hard ORIG_HEAD &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+ check_changes $head5 &&
git checkout master &&
git branch -D branch1 branch2 &&
- check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+ check_changes $head5
'
cat > expect << EOF
@@ -388,7 +425,8 @@ test_expect_success 'test --mixed <paths>' '
echo 4 > file4 &&
echo 5 > file1 &&
git add file1 file3 file4 &&
- test_must_fail git reset HEAD -- file1 file2 file3 &&
+ git reset HEAD -- file1 file2 file3 &&
+ test_must_fail git diff --quiet &&
git diff > output &&
test_cmp output expect &&
git diff --cached > output &&
@@ -402,7 +440,8 @@ test_expect_success 'test resetting the index at give paths' '
>sub/file2 &&
git update-index --add sub/file1 sub/file2 &&
T=$(git write-tree) &&
- test_must_fail git reset HEAD sub/file2 &&
+ git reset HEAD sub/file2 &&
+ test_must_fail git diff --quiet &&
U=$(git write-tree) &&
echo "$T" &&
echo "$U" &&
@@ -426,7 +465,23 @@ EOF
test_expect_success '--mixed refreshes the index' '
echo 123 >> file2 &&
git reset --mixed HEAD > output &&
- test_cmp expect output
+ test_i18ncmp expect output
+'
+
+test_expect_success 'resetting specific path that is unmerged' '
+ git rm --cached file2 &&
+ F1=$(git rev-parse HEAD:file1) &&
+ F2=$(git rev-parse HEAD:file2) &&
+ F3=$(git rev-parse HEAD:secondfile) &&
+ {
+ echo "100644 $F1 1 file2" &&
+ echo "100644 $F2 2 file2" &&
+ echo "100644 $F3 3 file2"
+ } | git update-index --index-info &&
+ git ls-files -u &&
+ git reset HEAD file2 &&
+ test_must_fail git diff --quiet &&
+ git diff-index --exit-code --cached HEAD
'
test_expect_success 'disambiguation (1)' '
@@ -434,10 +489,11 @@ test_expect_success 'disambiguation (1)' '
git reset --hard &&
>secondfile &&
git add secondfile &&
- test_must_fail git reset secondfile &&
+ git reset secondfile &&
+ test_must_fail git diff --quiet -- secondfile &&
test -z "$(git diff --cached --name-only)" &&
test -f secondfile &&
- test ! -s secondfile
+ test_must_be_empty secondfile
'
@@ -459,7 +515,8 @@ test_expect_success 'disambiguation (3)' '
>secondfile &&
git add secondfile &&
rm -f secondfile &&
- test_must_fail git reset HEAD secondfile &&
+ git reset HEAD secondfile &&
+ test_must_fail git diff --quiet &&
test -z "$(git diff --cached --name-only)" &&
test ! -f secondfile
@@ -471,9 +528,44 @@ test_expect_success 'disambiguation (4)' '
>secondfile &&
git add secondfile &&
rm -f secondfile &&
- test_must_fail git reset -- secondfile &&
+ git reset -- secondfile &&
+ test_must_fail git diff --quiet &&
test -z "$(git diff --cached --name-only)" &&
test ! -f secondfile
'
+test_expect_success 'reset with paths accepts tree' '
+ # for simpler tests, drop last commit containing added files
+ git reset --hard HEAD^ &&
+ git reset HEAD^^{tree} -- . &&
+ git diff --cached HEAD^ --exit-code &&
+ git diff HEAD --exit-code
+'
+
+test_expect_success 'reset -N keeps removed files as intent-to-add' '
+ echo new-file >new-file &&
+ git add new-file &&
+ git reset -N HEAD &&
+
+ tree=$(git write-tree) &&
+ git ls-tree $tree new-file >actual &&
+ >expect &&
+ test_cmp expect actual &&
+
+ git diff --name-only >actual &&
+ echo new-file >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'reset --mixed sets up work tree' '
+ git init mixed_worktree &&
+ (
+ cd mixed_worktree &&
+ test_commit dummy
+ ) &&
+ : >expect &&
+ git --git-dir=mixed_worktree/.git --work-tree=mixed_worktree reset >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t7103-reset-bare.sh b/t/t7103-reset-bare.sh
index 1eef93c..afe36a5 100755
--- a/t/t7103-reset-bare.sh
+++ b/t/t7103-reset-bare.sh
@@ -63,7 +63,7 @@ test_expect_success '"mixed" reset is not allowed in bare' '
test_expect_success '"soft" reset is allowed in bare' '
git reset --soft HEAD^ &&
- test "`git show --pretty=format:%s | head -n 1`" = "one"
+ test "$(git show --pretty=format:%s | head -n 1)" = "one"
'
test_done
diff --git a/t/t7104-reset.sh b/t/t7104-reset-hard.sh
index f136ee7..16faa07 100755
--- a/t/t7104-reset.sh
+++ b/t/t7104-reset-hard.sh
@@ -33,7 +33,7 @@ test_expect_success 'reset --hard should restore unmerged ones' '
'
-test_expect_success 'reset --hard did not corrupt index nor cached-tree' '
+test_expect_success 'reset --hard did not corrupt index or cached-tree' '
T=$(git write-tree) &&
rm -f .git/index &&
diff --git a/t/t7105-reset-patch.sh b/t/t7105-reset-patch.sh
index c1f4fc3..98b7d7b 100755
--- a/t/t7105-reset-patch.sh
+++ b/t/t7105-reset-patch.sh
@@ -3,7 +3,7 @@
test_description='git reset --patch'
. ./lib-patch-mode.sh
-test_expect_success 'setup' '
+test_expect_success PERL 'setup' '
mkdir dir &&
echo parent > dir/foo &&
echo dummy > bar &&
@@ -17,23 +17,25 @@ test_expect_success 'setup' '
# 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
+test_expect_success PERL '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 &&
+test_expect_success PERL 'git reset -p' '
+ (echo n; echo y) | git reset -p >output &&
verify_state dir/foo work head &&
- verify_saved_state bar
+ verify_saved_state bar &&
+ test_i18ngrep "Unstage" output
'
-test_expect_success 'git reset -p HEAD^' '
- (echo n; echo y) | git reset -p HEAD^ &&
+test_expect_success PERL 'git reset -p HEAD^' '
+ (echo n; echo y) | git reset -p HEAD^ >output &&
verify_state dir/foo work parent &&
- verify_saved_state bar
+ verify_saved_state bar &&
+ test_i18ngrep "Apply" output
'
# The idea in the rest is that bar sorts first, so we always say 'y'
@@ -41,27 +43,27 @@ test_expect_success 'git reset -p HEAD^' '
# 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
+test_expect_success PERL '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
+test_expect_success PERL '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' '
+test_expect_success PERL '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' '
+test_expect_success PERL 'none of this moved HEAD' '
verify_saved_head
'
diff --git a/t/t7106-reset-unborn-branch.sh b/t/t7106-reset-unborn-branch.sh
new file mode 100755
index 0000000..0f95f00
--- /dev/null
+++ b/t/t7106-reset-unborn-branch.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='git reset should work on unborn branch'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo a >a &&
+ echo b >b
+'
+
+test_expect_success 'reset' '
+ git add a b &&
+ git reset &&
+
+ >expect &&
+ git ls-files >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'reset HEAD' '
+ rm .git/index &&
+ git add a b &&
+ test_must_fail git reset HEAD
+'
+
+test_expect_success 'reset $file' '
+ rm .git/index &&
+ git add a b &&
+ git reset a &&
+
+ echo b >expect &&
+ git ls-files >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success PERL 'reset -p' '
+ rm .git/index &&
+ git add a &&
+ echo y >yes &&
+ git reset -p <yes >output &&
+
+ >expect &&
+ git ls-files >actual &&
+ test_cmp expect actual &&
+ test_i18ngrep "Unstage" output
+'
+
+test_expect_success 'reset --soft is a no-op' '
+ rm .git/index &&
+ git add a &&
+ git reset --soft &&
+
+ echo a >expect &&
+ git ls-files >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'reset --hard' '
+ rm .git/index &&
+ git add a &&
+ test_when_finished "echo a >a" &&
+ git reset --hard &&
+
+ >expect &&
+ git ls-files >actual &&
+ test_cmp expect actual &&
+ test_path_is_missing a
+'
+
+test_done
diff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh
index 70cdd8e..a82a07a 100755
--- a/t/t7110-reset-merge.sh
+++ b/t/t7110-reset-merge.sh
@@ -237,7 +237,7 @@ 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_i18ngrep "middle of a merge" err.log
'
# The next test will test the following:
@@ -263,7 +263,7 @@ 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_i18ngrep "middle of a merge" err.log
'
test_expect_success '--merge is ok with added/deleted merge' '
@@ -289,7 +289,7 @@ test_expect_success '--keep fails with added/deleted merge' '
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_i18ngrep "middle of a merge" err.log
'
test_done
diff --git a/t/t7112-reset-submodule.sh b/t/t7112-reset-submodule.sh
new file mode 100755
index 0000000..2eda6ad
--- /dev/null
+++ b/t/t7112-reset-submodule.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+test_description='reset can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+test_submodule_switch "git reset --keep"
+
+test_submodule_switch "git reset --merge"
+
+test_submodule_forced_switch "git reset --hard"
+
+test_done
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 1337fa5..8859236 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -88,14 +88,10 @@ 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
+ cp same kept &&
git checkout side >messages &&
- test_cmp same kept
- (cat > messages.expect <<EOF
-M same
-EOF
-) &&
- touch messages.expect &&
+ test_cmp same kept &&
+ printf "M\t%s\n" same >messages.expect &&
test_cmp messages.expect messages
'
@@ -109,10 +105,7 @@ test_expect_success "checkout -m with dirty tree" '
test "$(git symbolic-ref HEAD)" = "refs/heads/side" &&
- (cat >expect.messages <<EOF
-M one
-EOF
-) &&
+ printf "M\t%s\n" one >expect.messages &&
test_cmp expect.messages messages &&
fill "M one" "A three" "D two" >expect.master &&
@@ -223,13 +216,30 @@ test_expect_success 'checkout --merge --conflict=diff3 <branch>' '
test_cmp two expect
'
+test_expect_success 'switch to another branch while carrying a deletion' '
+
+ git checkout -f master && git reset --hard && git clean -f &&
+ git rm two &&
+
+ test_must_fail git checkout simple 2>errs &&
+ test_i18ngrep overwritten errs &&
+
+ git checkout --merge simple 2>errs &&
+ test_i18ngrep ! overwritten errs &&
+ git ls-files -u &&
+ test_must_fail git cat-file -t :0:two &&
+ test "$(git cat-file -t :1:two)" = blob &&
+ test "$(git cat-file -t :2:two)" = blob &&
+ test_must_fail git cat-file -t :3:two
+'
+
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) &&
+ test_i18ngrep "HEAD is now at 7329388" messages &&
+ test_line_count = 1 messages &&
H=$(git rev-parse --verify HEAD) &&
M=$(git show-ref -s --verify refs/heads/master) &&
test "z$H" = "z$M" &&
@@ -246,8 +256,8 @@ 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) &&
+ test_i18ngrep "HEAD is now at 7329388" messages &&
+ test_line_count -gt 1 messages &&
H=$(git rev-parse --verify HEAD) &&
M=$(git show-ref -s --verify refs/heads/master) &&
test "z$H" = "z$M" &&
@@ -392,12 +402,12 @@ test_expect_success \
test_expect_success \
'checkout w/autosetupmerge=always sets up tracking' '
+ test_when_finished git config branch.autosetupmerge false &&
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 "$(git config branch.track2.merge)"'
test_expect_success 'checkout w/--track from non-branch HEAD fails' '
git checkout master^0 &&
@@ -408,6 +418,15 @@ test_expect_success 'checkout w/--track from non-branch HEAD fails' '
test "z$(git rev-parse master^0)" = "z$(git rev-parse HEAD)"
'
+test_expect_success 'checkout w/--track from tag fails' '
+ git checkout master^0 &&
+ test_must_fail git symbolic-ref HEAD &&
+ test_must_fail git checkout --track -b track frotz &&
+ 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 &&
@@ -422,8 +441,8 @@ test_expect_success 'detach a symbolic link HEAD' '
test_expect_success \
'checkout with --track fakes a sensible -b <name>' '
+ git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" &&
git update-ref refs/remotes/origin/koala/bear renamer &&
- git update-ref refs/new/koala/bear renamer &&
git checkout --track origin/koala/bear &&
test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
@@ -439,12 +458,6 @@ test_expect_success \
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)"
'
@@ -571,7 +584,7 @@ test_expect_success 'checkout --conflict=merge, overriding config' '
'
test_expect_success 'checkout --conflict=diff3' '
- git config --unset merge.conflictstyle
+ test_unconfig merge.conflictstyle &&
setup_conflicting_index &&
echo "none of the above" >sample &&
echo ourside >expect &&
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index 7d8ed68..b89fd2a 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -110,7 +110,7 @@ test_expect_success 'git clean with prefix' '
'
-test_expect_success 'git clean with relative prefix' '
+test_expect_success C_LOCALE_OUTPUT 'git clean with relative prefix' '
mkdir -p build docs &&
touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
@@ -119,13 +119,10 @@ test_expect_success 'git clean with relative prefix' '
git clean -n ../src |
sed -n -e "s|^Would remove ||p"
) &&
- test "$would_clean" = ../src/part3.c || {
- echo "OOps <$would_clean>"
- false
- }
+ verbose test "$would_clean" = ../src/part3.c
'
-test_expect_success 'git clean with absolute path' '
+test_expect_success C_LOCALE_OUTPUT 'git clean with absolute path' '
mkdir -p build docs &&
touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
@@ -134,10 +131,7 @@ test_expect_success 'git clean with absolute path' '
git clean -n "$(pwd)/../src" |
sed -n -e "s|^Would remove ||p"
) &&
- test "$would_clean" = ../src/part3.c || {
- echo "OOps <$would_clean>"
- false
- }
+ verbose test "$would_clean" = ../src/part3.c
'
test_expect_success 'git clean with out of work tree relative path' '
@@ -179,11 +173,11 @@ test_expect_success 'git clean -d with prefix and path' '
'
-test_expect_success 'git clean symbolic link' '
+test_expect_success SYMLINKS '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
+ ln -s docs/manual.txt src/part4.c &&
git clean &&
test -f Makefile &&
test -f README &&
@@ -298,6 +292,23 @@ test_expect_success 'git clean -d -x' '
'
+test_expect_success 'git clean -d -x with ignored tracked directory' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git clean -d -x -e src &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test -f src/part3.c &&
+ test ! -d docs &&
+ test ! -f obj.o &&
+ test ! -d build
+
+'
+
test_expect_success 'git clean -X' '
mkdir -p build docs &&
@@ -332,6 +343,23 @@ test_expect_success 'git clean -d -X' '
'
+test_expect_success 'git clean -d -X with ignored tracked directory' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git clean -d -X -e src &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test ! -f obj.o &&
+ test ! -d build
+
+'
+
test_expect_success 'clean.requireForce defaults to true' '
git config --unset clean.requireForce &&
@@ -377,7 +405,7 @@ test_expect_success 'clean.requireForce and -f' '
'
-test_expect_success 'core.excludesfile' '
+test_expect_success C_LOCALE_OUTPUT 'core.excludesfile' '
echo excludes >excludes &&
echo included >included &&
@@ -388,54 +416,241 @@ test_expect_success 'core.excludesfile' '
'
-test_expect_success 'removal failure' '
+test_expect_success SANITY 'removal failure' '
mkdir foo &&
touch foo/bar &&
+ test_when_finished "chmod 755 foo" &&
(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 &&
+ rm -fr foo bar baz &&
+ mkdir -p foo bar baz/boo &&
(
cd foo &&
git init &&
- >hello.world
- git add . &&
- git commit -a -m nested
+ test_commit nested hello.world
) &&
(
cd bar &&
>goodbye.people
) &&
+ (
+ cd baz/boo &&
+ git init &&
+ test_commit deeply.nested deeper.world
+ ) &&
git clean -f -d &&
test -f foo/.git/index &&
test -f foo/hello.world &&
+ test -f baz/boo/.git/index &&
+ test -f baz/boo/deeper.world &&
! test -d bar
'
+test_expect_success 'should clean things that almost look like git but are not' '
+ rm -fr almost_git almost_bare_git almost_submodule &&
+ mkdir -p almost_git/.git/objects &&
+ mkdir -p almost_git/.git/refs &&
+ cat >almost_git/.git/HEAD <<-\EOF &&
+ garbage
+ EOF
+ cp -r almost_git/.git/ almost_bare_git &&
+ mkdir almost_submodule/ &&
+ cat >almost_submodule/.git <<-\EOF &&
+ garbage
+ EOF
+ test_when_finished "rm -rf almost_*" &&
+ git clean -f -d &&
+ test_path_is_missing almost_git &&
+ test_path_is_missing almost_bare_git &&
+ test_path_is_missing almost_submodule
+'
+
+test_expect_success 'should not clean submodules' '
+ rm -fr repo to_clean sub1 sub2 &&
+ mkdir repo to_clean &&
+ (
+ cd repo &&
+ git init &&
+ test_commit msg hello.world
+ ) &&
+ git submodule add ./repo/.git sub1 &&
+ git commit -m "sub1" &&
+ git branch before_sub2 &&
+ git submodule add ./repo/.git sub2 &&
+ git commit -m "sub2" &&
+ git checkout before_sub2 &&
+ >to_clean/should_clean.this &&
+ git clean -f -d &&
+ test_path_is_file repo/.git/index &&
+ test_path_is_file repo/hello.world &&
+ test_path_is_file sub1/.git &&
+ test_path_is_file sub1/hello.world &&
+ test_path_is_file sub2/.git &&
+ test_path_is_file sub2/hello.world &&
+ test_path_is_missing to_clean
+'
+
+test_expect_success POSIXPERM,SANITY 'should avoid cleaning possible submodules' '
+ rm -fr to_clean possible_sub1 &&
+ mkdir to_clean possible_sub1 &&
+ test_when_finished "rm -rf possible_sub*" &&
+ echo "gitdir: foo" >possible_sub1/.git &&
+ >possible_sub1/hello.world &&
+ chmod 0 possible_sub1/.git &&
+ >to_clean/should_clean.this &&
+ git clean -f -d &&
+ test_path_is_file possible_sub1/.git &&
+ test_path_is_file possible_sub1/hello.world &&
+ test_path_is_missing to_clean
+'
+
+test_expect_success 'nested (empty) git should be kept' '
+ rm -fr empty_repo to_clean &&
+ git init empty_repo &&
+ mkdir to_clean &&
+ >to_clean/should_clean.this &&
+ git clean -f -d &&
+ test_path_is_file empty_repo/.git/HEAD &&
+ test_path_is_missing to_clean
+'
+
+test_expect_success 'nested bare repositories should be cleaned' '
+ rm -fr bare1 bare2 subdir &&
+ git init --bare bare1 &&
+ git clone --local --bare . bare2 &&
+ mkdir subdir &&
+ cp -r bare2 subdir/bare3 &&
+ git clean -f -d &&
+ test_path_is_missing bare1 &&
+ test_path_is_missing bare2 &&
+ test_path_is_missing subdir
+'
+
+test_expect_failure 'nested (empty) bare repositories should be cleaned even when in .git' '
+ rm -fr strange_bare &&
+ mkdir strange_bare &&
+ git init --bare strange_bare/.git &&
+ git clean -f -d &&
+ test_path_is_missing strange_bare
+'
+
+test_expect_failure 'nested (non-empty) bare repositories should be cleaned even when in .git' '
+ rm -fr strange_bare &&
+ mkdir strange_bare &&
+ git clone --local --bare . strange_bare/.git &&
+ git clean -f -d &&
+ test_path_is_missing strange_bare
+'
+
+test_expect_success 'giving path in nested git work tree will remove it' '
+ rm -fr repo &&
+ mkdir repo &&
+ (
+ cd repo &&
+ git init &&
+ mkdir -p bar/baz &&
+ test_commit msg bar/baz/hello.world
+ ) &&
+ git clean -f -d repo/bar/baz &&
+ test_path_is_file repo/.git/HEAD &&
+ test_path_is_dir repo/bar/ &&
+ test_path_is_missing repo/bar/baz
+'
+
+test_expect_success 'giving path to nested .git will not remove it' '
+ rm -fr repo &&
+ mkdir repo untracked &&
+ (
+ cd repo &&
+ git init &&
+ test_commit msg hello.world
+ ) &&
+ git clean -f -d repo/.git &&
+ test_path_is_file repo/.git/HEAD &&
+ test_path_is_dir repo/.git/refs &&
+ test_path_is_dir repo/.git/objects &&
+ test_path_is_dir untracked/
+'
+
+test_expect_success 'giving path to nested .git/ will remove contents' '
+ rm -fr repo untracked &&
+ mkdir repo untracked &&
+ (
+ cd repo &&
+ git init &&
+ test_commit msg hello.world
+ ) &&
+ git clean -f -d repo/.git/ &&
+ test_path_is_dir repo/.git &&
+ test_dir_is_empty repo/.git &&
+ test_path_is_dir untracked/
+'
+
test_expect_success 'force removal of nested git work tree' '
- rm -fr foo bar &&
- mkdir foo bar &&
+ rm -fr foo bar baz &&
+ mkdir -p foo bar baz/boo &&
(
cd foo &&
git init &&
- >hello.world
- git add . &&
- git commit -a -m nested
+ test_commit nested hello.world
) &&
(
cd bar &&
>goodbye.people
) &&
+ (
+ cd baz/boo &&
+ git init &&
+ test_commit deeply.nested deeper.world
+ ) &&
git clean -f -f -d &&
! test -d foo &&
- ! test -d bar
+ ! test -d bar &&
+ ! test -d baz
+'
+
+test_expect_success 'git clean -e' '
+ rm -fr repo &&
+ mkdir repo &&
+ (
+ cd repo &&
+ git init &&
+ touch known 1 2 3 &&
+ git add known &&
+ git clean -f -e 1 -e 2 &&
+ test -e 1 &&
+ test -e 2 &&
+ ! (test -e 3) &&
+ test -e known
+ )
+'
+
+test_expect_success SANITY 'git clean -d with an unreadable empty directory' '
+ mkdir foo &&
+ chmod a= foo &&
+ git clean -dfx foo &&
+ ! test -d foo
+'
+
+test_expect_success 'git clean -d respects pathspecs (dir is prefix of pathspec)' '
+ mkdir -p foo &&
+ mkdir -p foobar &&
+ git clean -df foobar &&
+ test_path_is_dir foo &&
+ test_path_is_missing foobar
+'
+
+test_expect_success 'git clean -d respects pathspecs (pathspec is prefix of dir)' '
+ mkdir -p foo &&
+ mkdir -p foobar &&
+ git clean -df foo &&
+ test_path_is_missing foo &&
+ test_path_is_dir foobar
'
test_done
diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh
new file mode 100755
index 0000000..3ae394e
--- /dev/null
+++ b/t/t7301-clean-interactive.sh
@@ -0,0 +1,475 @@
+#!/bin/sh
+
+test_description='git clean -i basic tests'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+ mkdir -p src &&
+ touch src/part1.c Makefile &&
+ echo build >.gitignore &&
+ echo \*.o >>.gitignore &&
+ git add . &&
+ git commit -m setup &&
+ touch src/part2.c README &&
+ git add .
+
+'
+
+test_expect_success 'git clean -i (c: clean hotkey)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ echo c | git clean -i &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test ! -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -i (cl: clean prefix)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ echo cl | git clean -i &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test ! -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -i (quit)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ echo quit | git clean -i &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -i (Ctrl+D)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ echo "\04" | git clean -i &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (filter all)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo f; echo "*"; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (filter patterns)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo f; echo "part3.* *.out"; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test ! -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (filter patterns 2)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo f; echo "* !*.out"; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - all)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo s; echo "*"; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test ! -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - none)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo s; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - number)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo s; echo 3; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - number 2)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo s; echo 2 3; echo 5; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - number 3)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo s; echo 3,4 5; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - filenames)' '
+
+ mkdir -p build docs &&
+ touch a.out foo.txt bar.txt baz.txt &&
+ (echo s; echo a.out fo ba bar; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test ! -f a.out &&
+ test ! -f foo.txt &&
+ test ! -f bar.txt &&
+ test -f baz.txt &&
+ rm baz.txt
+
+'
+
+test_expect_success 'git clean -id (select - range)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo s; echo 1,3-4; echo 2; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test ! -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - range 2)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo s; echo 4- 1; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test ! -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (inverse select)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo s; echo "*"; echo -5- 1 -2; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (ask)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (ask - Ctrl+D)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo a; echo Y; echo no; echo yes; echo "\04") | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id with prefix and path (filter)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (cd build/ && \
+ (echo f; echo "docs"; echo "*.h"; echo ; echo c) | \
+ git clean -id ..) &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id with prefix and path (select by name)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (cd build/ && \
+ (echo s; echo "../docs/"; echo "../src/part3.c"; \
+ echo "../src/part4.c"; echo; echo c) | \
+ git clean -id ..) &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id with prefix and path (ask)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (cd build/ && \
+ (echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \
+ git clean -id ..) &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_done
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 9bda970..3570f7b 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -11,6 +11,10 @@ subcommands of git submodule.
. ./test-lib.sh
+test_expect_success 'submodule deinit works on empty repository' '
+ git submodule deinit --all
+'
+
test_expect_success 'setup - initial commit' '
>t &&
git add t &&
@@ -18,6 +22,32 @@ test_expect_success 'setup - initial commit' '
git branch initial
'
+test_expect_success 'submodule init aborts on missing .gitmodules file' '
+ test_when_finished "git update-index --remove sub" &&
+ git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub &&
+ # missing the .gitmodules file here
+ test_must_fail git submodule init 2>actual &&
+ test_i18ngrep "No url found for submodule path" actual
+'
+
+test_expect_success 'submodule update aborts on missing .gitmodules file' '
+ test_when_finished "git update-index --remove sub" &&
+ git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub &&
+ # missing the .gitmodules file here
+ git submodule update sub 2>actual &&
+ test_i18ngrep "Submodule path .sub. not initialized" actual
+'
+
+test_expect_success 'configuration parsing' '
+ test_when_finished "rm -f .gitmodules" &&
+ cat >.gitmodules <<-\EOF &&
+ [submodule "s"]
+ path
+ ignore
+ EOF
+ test_must_fail git status
+'
+
test_expect_success 'setup - repository in init subdirectory' '
mkdir init &&
(
@@ -47,8 +77,10 @@ test_expect_success 'setup - repository to add submodules to' '
'
# The 'submodule add' tests need some repository to add as a submodule.
-# The trash directory is a good one as any.
-submodurl=$TRASH_DIRECTORY
+# The trash directory is a good one as any. We need to canonicalize
+# the name, though, as some tests compare it to the absolute path git
+# generates, which will expand symbolic links.
+submodurl=$(pwd -P)
listbranches() {
git for-each-ref --format='%(refname)' 'refs/heads/*'
@@ -75,7 +107,17 @@ test_expect_success 'submodule add' '
(
cd addtest &&
- git submodule add "$submodurl" submod &&
+ git submodule add -q "$submodurl" submod >actual &&
+ test_must_be_empty actual &&
+ echo "gitdir: ../.git/modules/submod" >expect &&
+ test_cmp expect submod/.git &&
+ (
+ cd submod &&
+ git config core.worktree >actual &&
+ echo "../../../submod" >expect &&
+ test_cmp expect actual &&
+ rm -f actual expect
+ ) &&
git submodule init
) &&
@@ -99,7 +141,7 @@ test_expect_success 'submodule add to .gitignored path fails' '
git add --force .gitignore &&
git commit -m"Ignore everything" &&
! git submodule add "$submodurl" submod >actual 2>&1 &&
- test_cmp expect actual
+ test_i18ncmp expect actual
)
'
@@ -121,6 +163,7 @@ test_expect_success 'submodule add --branch' '
(
cd addtest &&
git submodule add -b initial "$submodurl" submod-branch &&
+ test "initial" = "$(git config -f .gitmodules submodule.submod-branch.branch)" &&
git submodule init
) &&
@@ -148,6 +191,23 @@ test_expect_success 'submodule add with ./ in path' '
test_cmp empty untracked
'
+test_expect_success 'submodule add with /././ in path' '
+ echo "refs/heads/master" >expect &&
+ >empty &&
+
+ (
+ cd addtest &&
+ git submodule add "$submodurl" dotslashdotsubmod/././frotz/./ &&
+ git submodule init
+ ) &&
+
+ rm -f heads head untracked &&
+ inspect addtest/dotslashdotsubmod/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 &&
@@ -199,9 +259,34 @@ test_expect_success 'submodule add with ./, /.. and // in path' '
test_cmp empty untracked
'
+test_expect_success 'submodule add in subdirectory' '
+ echo "refs/heads/master" >expect &&
+ >empty &&
+
+ mkdir addtest/sub &&
+ (
+ cd addtest/sub &&
+ git submodule add "$submodurl" ../realsubmod3 &&
+ git submodule init
+ ) &&
+
+ rm -f heads head untracked &&
+ inspect addtest/realsubmod3 ../.. &&
+ test_cmp expect heads &&
+ test_cmp expect head &&
+ test_cmp empty untracked
+'
+
+test_expect_success 'submodule add in subdirectory with relative path should fail' '
+ (
+ cd addtest/sub &&
+ test_must_fail git submodule add ../../ submod3 2>../../output.err
+ ) &&
+ test_i18ngrep toplevel output.err
+'
+
test_expect_success 'setup - add an example entry to .gitmodules' '
- GIT_CONFIG=.gitmodules \
- git config submodule.example.url git://example.com/init.git
+ git config --file=.gitmodules submodule.example.url git://example.com/init.git
'
test_expect_success 'status should fail for unmapped paths' '
@@ -215,14 +300,14 @@ test_expect_success 'setup - map path in .gitmodules' '
path = init
EOF
- GIT_CONFIG=.gitmodules git config submodule.example.path init &&
+ git config --file=.gitmodules 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_line_count = 1 lines
'
test_expect_success 'setup - fetch commit name from submodule' '
@@ -246,6 +331,27 @@ test_expect_success 'init should register submodule url in .git/config' '
test_cmp expect url
'
+test_failure_with_unknown_submodule () {
+ test_must_fail git submodule $1 no-such-submodule 2>output.err &&
+ grep "^error: .*no-such-submodule" output.err
+}
+
+test_expect_success 'init should fail with unknown submodule' '
+ test_failure_with_unknown_submodule init
+'
+
+test_expect_success 'update should fail with unknown submodule' '
+ test_failure_with_unknown_submodule update
+'
+
+test_expect_success 'status should fail with unknown submodule' '
+ test_failure_with_unknown_submodule status
+'
+
+test_expect_success 'sync should fail with unknown submodule' '
+ test_failure_with_unknown_submodule sync
+'
+
test_expect_success 'update should fail when path is used by a file' '
echo hello >expect &&
@@ -273,7 +379,8 @@ test_expect_success 'update should work when path is an empty dir' '
echo "$rev1" >expect &&
mkdir init &&
- git submodule update &&
+ git submodule update -q >update.out &&
+ test_must_be_empty update.out &&
inspect init &&
test_cmp expect head-sha1
@@ -284,6 +391,26 @@ test_expect_success 'status should be "up-to-date" after update' '
grep "^ $rev1" list
'
+test_expect_success 'status "up-to-date" from subdirectory' '
+ mkdir -p sub &&
+ (
+ cd sub &&
+ git submodule status >../list
+ ) &&
+ grep "^ $rev1" list &&
+ grep "\\.\\./init" list
+'
+
+test_expect_success 'status "up-to-date" from subdirectory with path' '
+ mkdir -p sub &&
+ (
+ cd sub &&
+ git submodule status ../init >../list
+ ) &&
+ grep "^ $rev1" list &&
+ grep "\\.\\./init" list
+'
+
test_expect_success 'status should be "modified" after submodule commit' '
(
cd init &&
@@ -355,13 +482,32 @@ test_expect_success 'update --init' '
git config --remove-section submodule.example &&
test_must_fail git config submodule.example.url &&
- git submodule update init > update.out &&
+ git submodule update init 2> update.out &&
cat update.out &&
- grep "not initialized" update.out &&
- ! test -d init/.git &&
+ test_i18ngrep "not initialized" update.out &&
+ test_must_fail git rev-parse --resolve-git-dir init/.git &&
git submodule update --init init &&
- test -d init/.git
+ git rev-parse --resolve-git-dir init/.git
+'
+
+test_expect_success 'update --init from subdirectory' '
+ mv init init2 &&
+ git config -f .gitmodules submodule.example.url "$(pwd)/init2" &&
+ git config --remove-section submodule.example &&
+ test_must_fail git config submodule.example.url &&
+
+ mkdir -p sub &&
+ (
+ cd sub &&
+ git submodule update ../init 2>update.out &&
+ cat update.out &&
+ test_i18ngrep "not initialized" update.out &&
+ test_must_fail git rev-parse --resolve-git-dir ../init/.git &&
+
+ git submodule update --init ../init
+ ) &&
+ git rev-parse --resolve-git-dir init/.git
'
test_expect_success 'do not add files from a submodule' '
@@ -371,7 +517,7 @@ test_expect_success 'do not add files from a submodule' '
'
-test_expect_success 'gracefully add submodule with a trailing slash' '
+test_expect_success 'gracefully add/reset submodule with a trailing slash' '
git reset --hard &&
git commit -m "commit subproject" init &&
@@ -385,7 +531,9 @@ test_expect_success 'gracefully add submodule with a trailing slash' '
git add init/ &&
test_must_fail git diff --exit-code --cached init &&
test $commit = $(git ls-files --stage |
- sed -n "s/^160000 \([^ ]*\).*/\1/p")
+ sed -n "s/^160000 \([^ ]*\).*/\1/p") &&
+ git reset init/ &&
+ git diff --exit-code --cached init
'
@@ -404,27 +552,569 @@ test_expect_success 'moving to a commit without submodule does not leave empty d
git checkout second
'
-test_expect_success 'submodule <invalid-path> warns' '
-
- git submodule no-such-submodule 2> output.err &&
- grep "^error: .*no-such-submodule" output.err
-
+test_expect_success 'submodule <invalid-subcommand> fails' '
+ test_must_fail git submodule no-such-subcommand
'
test_expect_success 'add submodules without specifying an explicit path' '
mkdir repo &&
- cd repo &&
- git init &&
- echo r >r &&
- git add r &&
- git commit -m "repo commit 1" &&
- cd .. &&
+ (
+ cd repo &&
+ git init &&
+ echo r >r &&
+ git add r &&
+ git commit -m "repo commit 1"
+ ) &&
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
+ (
+ 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_expect_success 'add should fail when path is used by a file' '
+ (
+ cd addtest &&
+ touch file &&
+ test_must_fail git submodule add "$submodurl/repo" file
+ )
+'
+
+test_expect_success 'add should fail when path is used by an existing directory' '
+ (
+ cd addtest &&
+ mkdir empty-dir &&
+ test_must_fail git submodule add "$submodurl/repo" empty-dir
+ )
+'
+
+test_expect_success 'use superproject as upstream when path is relative and no url is set there' '
+ (
+ cd addtest &&
+ git submodule add ../repo relative &&
+ test "$(git config -f .gitmodules submodule.relative.url)" = ../repo &&
+ git submodule sync relative &&
+ test "$(git config submodule.relative.url)" = "$submodurl/repo"
+ )
+'
+
+test_expect_success 'set up for relative path tests' '
+ mkdir reltest &&
+ (
+ cd reltest &&
+ git init &&
+ mkdir sub &&
+ (
+ cd sub &&
+ git init &&
+ test_commit foo
+ ) &&
+ git add sub &&
+ git config -f .gitmodules submodule.sub.path sub &&
+ git config -f .gitmodules submodule.sub.url ../subrepo &&
+ cp .git/config pristine-.git-config &&
+ cp .gitmodules pristine-.gitmodules
+ )
+'
+
+test_expect_success '../subrepo works with URL - ssh://hostname/repo' '
+ (
+ cd reltest &&
+ cp pristine-.git-config .git/config &&
+ cp pristine-.gitmodules .gitmodules &&
+ git config remote.origin.url ssh://hostname/repo &&
+ git submodule init &&
+ test "$(git config submodule.sub.url)" = ssh://hostname/subrepo
+ )
+'
+
+test_expect_success '../subrepo works with port-qualified URL - ssh://hostname:22/repo' '
+ (
+ cd reltest &&
+ cp pristine-.git-config .git/config &&
+ cp pristine-.gitmodules .gitmodules &&
+ git config remote.origin.url ssh://hostname:22/repo &&
+ git submodule init &&
+ test "$(git config submodule.sub.url)" = ssh://hostname:22/subrepo
+ )
+'
+
+# About the choice of the path in the next test:
+# - double-slash side-steps path mangling issues on Windows
+# - it is still an absolute local path
+# - there cannot be a server with a blank in its name just in case the
+# path is used erroneously to access a //server/share style path
+test_expect_success '../subrepo path works with local path - //somewhere else/repo' '
+ (
+ cd reltest &&
+ cp pristine-.git-config .git/config &&
+ cp pristine-.gitmodules .gitmodules &&
+ git config remote.origin.url "//somewhere else/repo" &&
+ git submodule init &&
+ test "$(git config submodule.sub.url)" = "//somewhere else/subrepo"
+ )
+'
+
+test_expect_success '../subrepo works with file URL - file:///tmp/repo' '
+ (
+ cd reltest &&
+ cp pristine-.git-config .git/config &&
+ cp pristine-.gitmodules .gitmodules &&
+ git config remote.origin.url file:///tmp/repo &&
+ git submodule init &&
+ test "$(git config submodule.sub.url)" = file:///tmp/subrepo
+ )
+'
+
+test_expect_success '../subrepo works with helper URL- helper:://hostname/repo' '
+ (
+ cd reltest &&
+ cp pristine-.git-config .git/config &&
+ cp pristine-.gitmodules .gitmodules &&
+ git config remote.origin.url helper:://hostname/repo &&
+ git submodule init &&
+ test "$(git config submodule.sub.url)" = helper:://hostname/subrepo
+ )
+'
+
+test_expect_success '../subrepo works with scp-style URL - user@host:repo' '
+ (
+ cd reltest &&
+ cp pristine-.git-config .git/config &&
+ git config remote.origin.url user@host:repo &&
+ git submodule init &&
+ test "$(git config submodule.sub.url)" = user@host:subrepo
+ )
+'
+
+test_expect_success '../subrepo works with scp-style URL - user@host:path/to/repo' '
+ (
+ cd reltest &&
+ cp pristine-.git-config .git/config &&
+ cp pristine-.gitmodules .gitmodules &&
+ git config remote.origin.url user@host:path/to/repo &&
+ git submodule init &&
+ test "$(git config submodule.sub.url)" = user@host:path/to/subrepo
+ )
+'
+
+test_expect_success '../subrepo works with relative local path - foo' '
+ (
+ cd reltest &&
+ cp pristine-.git-config .git/config &&
+ cp pristine-.gitmodules .gitmodules &&
+ git config remote.origin.url foo &&
+ # actual: fails with an error
+ git submodule init &&
+ test "$(git config submodule.sub.url)" = subrepo
+ )
+'
+
+test_expect_success '../subrepo works with relative local path - foo/bar' '
+ (
+ cd reltest &&
+ cp pristine-.git-config .git/config &&
+ cp pristine-.gitmodules .gitmodules &&
+ git config remote.origin.url foo/bar &&
+ git submodule init &&
+ test "$(git config submodule.sub.url)" = foo/subrepo
+ )
+'
+
+test_expect_success '../subrepo works with relative local path - ./foo' '
+ (
+ cd reltest &&
+ cp pristine-.git-config .git/config &&
+ cp pristine-.gitmodules .gitmodules &&
+ git config remote.origin.url ./foo &&
+ git submodule init &&
+ test "$(git config submodule.sub.url)" = subrepo
+ )
+'
+
+test_expect_success '../subrepo works with relative local path - ./foo/bar' '
+ (
+ cd reltest &&
+ cp pristine-.git-config .git/config &&
+ cp pristine-.gitmodules .gitmodules &&
+ git config remote.origin.url ./foo/bar &&
+ git submodule init &&
+ test "$(git config submodule.sub.url)" = foo/subrepo
+ )
+'
+
+test_expect_success '../subrepo works with relative local path - ../foo' '
+ (
+ cd reltest &&
+ cp pristine-.git-config .git/config &&
+ cp pristine-.gitmodules .gitmodules &&
+ git config remote.origin.url ../foo &&
+ git submodule init &&
+ test "$(git config submodule.sub.url)" = ../subrepo
+ )
+'
+
+test_expect_success '../subrepo works with relative local path - ../foo/bar' '
+ (
+ cd reltest &&
+ cp pristine-.git-config .git/config &&
+ cp pristine-.gitmodules .gitmodules &&
+ git config remote.origin.url ../foo/bar &&
+ git submodule init &&
+ test "$(git config submodule.sub.url)" = ../foo/subrepo
+ )
+'
+
+test_expect_success '../bar/a/b/c works with relative local path - ../foo/bar.git' '
+ (
+ cd reltest &&
+ cp pristine-.git-config .git/config &&
+ cp pristine-.gitmodules .gitmodules &&
+ mkdir -p a/b/c &&
+ (cd a/b/c; git init) &&
+ git config remote.origin.url ../foo/bar.git &&
+ git submodule add ../bar/a/b/c ./a/b/c &&
+ git submodule init &&
+ test "$(git config submodule.a/b/c.url)" = ../foo/bar/a/b/c
+ )
+'
+
+test_expect_success 'moving the superproject does not break submodules' '
+ (
+ cd addtest &&
+ git submodule status >expect
+ ) &&
+ mv addtest addtest2 &&
+ (
+ cd addtest2 &&
+ git submodule status >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'submodule add --name allows to replace a submodule with another at the same path' '
+ (
+ cd addtest2 &&
+ (
+ cd repo &&
+ echo "$submodurl/repo" >expect &&
+ git config remote.origin.url >actual &&
+ test_cmp expect actual &&
+ echo "gitdir: ../.git/modules/repo" >expect &&
+ test_cmp expect .git
+ ) &&
+ rm -rf repo &&
+ git rm repo &&
+ git submodule add -q --name repo_new "$submodurl/bare.git" repo >actual &&
+ test_must_be_empty actual &&
+ echo "gitdir: ../.git/modules/submod" >expect &&
+ test_cmp expect submod/.git &&
+ (
+ cd repo &&
+ echo "$submodurl/bare.git" >expect &&
+ git config remote.origin.url >actual &&
+ test_cmp expect actual &&
+ echo "gitdir: ../.git/modules/repo_new" >expect &&
+ test_cmp expect .git
+ ) &&
+ echo "repo" >expect &&
+ test_must_fail git config -f .gitmodules submodule.repo.path &&
+ git config -f .gitmodules submodule.repo_new.path >actual &&
+ test_cmp expect actual&&
+ echo "$submodurl/repo" >expect &&
+ test_must_fail git config -f .gitmodules submodule.repo.url &&
+ echo "$submodurl/bare.git" >expect &&
+ git config -f .gitmodules submodule.repo_new.url >actual &&
+ test_cmp expect actual &&
+ echo "$submodurl/repo" >expect &&
+ git config submodule.repo.url >actual &&
+ test_cmp expect actual &&
+ echo "$submodurl/bare.git" >expect &&
+ git config submodule.repo_new.url >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'recursive relative submodules stay relative' '
+ test_when_finished "rm -rf super clone2 subsub sub3" &&
+ mkdir subsub &&
+ (
+ cd subsub &&
+ git init &&
+ >t &&
+ git add t &&
+ git commit -m "initial commit"
+ ) &&
+ mkdir sub3 &&
+ (
+ cd sub3 &&
+ git init &&
+ >t &&
+ git add t &&
+ git commit -m "initial commit" &&
+ git submodule add ../subsub dirdir/subsub &&
+ git commit -m "add submodule subsub"
+ ) &&
+ mkdir super &&
+ (
+ cd super &&
+ git init &&
+ >t &&
+ git add t &&
+ git commit -m "initial commit" &&
+ git submodule add ../sub3 &&
+ git commit -m "add submodule sub"
+ ) &&
+ git clone super clone2 &&
+ (
+ cd clone2 &&
+ git submodule update --init --recursive &&
+ echo "gitdir: ../.git/modules/sub3" >./sub3/.git_expect &&
+ echo "gitdir: ../../../.git/modules/sub3/modules/dirdir/subsub" >./sub3/dirdir/subsub/.git_expect
+ ) &&
+ test_cmp clone2/sub3/.git_expect clone2/sub3/.git &&
+ test_cmp clone2/sub3/dirdir/subsub/.git_expect clone2/sub3/dirdir/subsub/.git
+'
+
+test_expect_success 'submodule add with an existing name fails unless forced' '
+ (
+ cd addtest2 &&
+ rm -rf repo &&
+ git rm repo &&
+ test_must_fail git submodule add -q --name repo_new "$submodurl/repo.git" repo &&
+ test ! -d repo &&
+ test_must_fail git config -f .gitmodules submodule.repo_new.path &&
+ test_must_fail git config -f .gitmodules submodule.repo_new.url &&
+ echo "$submodurl/bare.git" >expect &&
+ git config submodule.repo_new.url >actual &&
+ test_cmp expect actual &&
+ git submodule add -f -q --name repo_new "$submodurl/repo.git" repo &&
+ test -d repo &&
+ echo "repo" >expect &&
+ git config -f .gitmodules submodule.repo_new.path >actual &&
+ test_cmp expect actual&&
+ echo "$submodurl/repo.git" >expect &&
+ git config -f .gitmodules submodule.repo_new.url >actual &&
+ test_cmp expect actual &&
+ echo "$submodurl/repo.git" >expect &&
+ git config submodule.repo_new.url >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'set up a second submodule' '
+ git submodule add ./init2 example2 &&
+ git commit -m "submodule example2 added"
+'
+
+test_expect_success 'submodule deinit works on repository without submodules' '
+ test_when_finished "rm -rf newdirectory" &&
+ mkdir newdirectory &&
+ (
+ cd newdirectory &&
+ git init &&
+ >file &&
+ git add file &&
+ git commit -m "repo should not be empty" &&
+ git submodule deinit . &&
+ git submodule deinit --all
+ )
+'
+
+test_expect_success 'submodule deinit should remove the whole submodule section from .git/config' '
+ git config submodule.example.foo bar &&
+ git config submodule.example2.frotz nitfol &&
+ git submodule deinit init &&
+ test -z "$(git config --get-regexp "submodule\.example\.")" &&
+ test -n "$(git config --get-regexp "submodule\.example2\.")" &&
+ test -f example2/.git &&
+ rmdir init
+'
+
+test_expect_success 'submodule deinit from subdirectory' '
+ git submodule update --init &&
+ git config submodule.example.foo bar &&
+ mkdir -p sub &&
+ (
+ cd sub &&
+ git submodule deinit ../init >../output
+ ) &&
+ grep "\\.\\./init" output &&
+ test -z "$(git config --get-regexp "submodule\.example\.")" &&
+ test -n "$(git config --get-regexp "submodule\.example2\.")" &&
+ test -f example2/.git &&
+ rmdir init
+'
+
+test_expect_success 'submodule deinit . deinits all initialized submodules' '
+ git submodule update --init &&
+ git config submodule.example.foo bar &&
+ git config submodule.example2.frotz nitfol &&
+ test_must_fail git submodule deinit &&
+ git submodule deinit . >actual &&
+ test -z "$(git config --get-regexp "submodule\.example\.")" &&
+ test -z "$(git config --get-regexp "submodule\.example2\.")" &&
+ test_i18ngrep "Cleared directory .init" actual &&
+ test_i18ngrep "Cleared directory .example2" actual &&
+ rmdir init example2
+'
+
+test_expect_success 'submodule deinit --all deinits all initialized submodules' '
+ git submodule update --init &&
+ git config submodule.example.foo bar &&
+ git config submodule.example2.frotz nitfol &&
+ test_must_fail git submodule deinit &&
+ git submodule deinit --all >actual &&
+ test -z "$(git config --get-regexp "submodule\.example\.")" &&
+ test -z "$(git config --get-regexp "submodule\.example2\.")" &&
+ test_i18ngrep "Cleared directory .init" actual &&
+ test_i18ngrep "Cleared directory .example2" actual &&
+ rmdir init example2
+'
+
+test_expect_success 'submodule deinit deinits a submodule when its work tree is missing or empty' '
+ git submodule update --init &&
+ rm -rf init example2/* example2/.git &&
+ git submodule deinit init example2 >actual &&
+ test -z "$(git config --get-regexp "submodule\.example\.")" &&
+ test -z "$(git config --get-regexp "submodule\.example2\.")" &&
+ test_i18ngrep ! "Cleared directory .init" actual &&
+ test_i18ngrep "Cleared directory .example2" actual &&
+ rmdir init
+'
+
+test_expect_success 'submodule deinit fails when the submodule contains modifications unless forced' '
+ git submodule update --init &&
+ echo X >>init/s &&
+ test_must_fail git submodule deinit init &&
+ test -n "$(git config --get-regexp "submodule\.example\.")" &&
+ test -f example2/.git &&
+ git submodule deinit -f init >actual &&
+ test -z "$(git config --get-regexp "submodule\.example\.")" &&
+ test_i18ngrep "Cleared directory .init" actual &&
+ rmdir init
+'
+
+test_expect_success 'submodule deinit fails when the submodule contains untracked files unless forced' '
+ git submodule update --init &&
+ echo X >>init/untracked &&
+ test_must_fail git submodule deinit init &&
+ test -n "$(git config --get-regexp "submodule\.example\.")" &&
+ test -f example2/.git &&
+ git submodule deinit -f init >actual &&
+ test -z "$(git config --get-regexp "submodule\.example\.")" &&
+ test_i18ngrep "Cleared directory .init" actual &&
+ rmdir init
+'
+
+test_expect_success 'submodule deinit fails when the submodule HEAD does not match unless forced' '
+ git submodule update --init &&
+ (
+ cd init &&
+ git checkout HEAD^
+ ) &&
+ test_must_fail git submodule deinit init &&
+ test -n "$(git config --get-regexp "submodule\.example\.")" &&
+ test -f example2/.git &&
+ git submodule deinit -f init >actual &&
+ test -z "$(git config --get-regexp "submodule\.example\.")" &&
+ test_i18ngrep "Cleared directory .init" actual &&
+ rmdir init
+'
+
+test_expect_success 'submodule deinit is silent when used on an uninitialized submodule' '
+ git submodule update --init &&
+ git submodule deinit init >actual &&
+ test_i18ngrep "Submodule .example. (.*) unregistered for path .init" actual &&
+ test_i18ngrep "Cleared directory .init" actual &&
+ git submodule deinit init >actual &&
+ test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+ test_i18ngrep "Cleared directory .init" actual &&
+ git submodule deinit . >actual &&
+ test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+ test_i18ngrep "Submodule .example2. (.*) unregistered for path .example2" actual &&
+ test_i18ngrep "Cleared directory .init" actual &&
+ git submodule deinit . >actual &&
+ test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+ test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
+ test_i18ngrep "Cleared directory .init" actual &&
+ git submodule deinit --all >actual &&
+ test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+ test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
+ test_i18ngrep "Cleared directory .init" actual &&
+ rmdir init example2
+'
+
+test_expect_success 'submodule deinit fails when submodule has a .git directory even when forced' '
+ git submodule update --init &&
+ (
+ cd init &&
+ rm .git &&
+ cp -R ../.git/modules/example .git &&
+ GIT_WORK_TREE=. git config --unset core.worktree
+ ) &&
+ test_must_fail git submodule deinit init &&
+ test_must_fail git submodule deinit -f init &&
+ test -d init/.git &&
+ test -n "$(git config --get-regexp "submodule\.example\.")"
+'
+
+test_expect_success 'submodule with UTF-8 name' '
+ svname=$(printf "\303\245 \303\244\303\266") &&
+ mkdir "$svname" &&
+ (
+ cd "$svname" &&
+ git init &&
+ >sub &&
+ git add sub &&
+ git commit -m "init sub"
+ ) &&
+ git submodule add ./"$svname" &&
+ git submodule >&2 &&
+ test -n "$(git submodule | grep "$svname")"
+'
+
+test_expect_success 'submodule add clone shallow submodule' '
+ mkdir super &&
+ pwd=$(pwd) &&
+ (
+ cd super &&
+ git init &&
+ git submodule add --depth=1 file://"$pwd"/example2 submodule &&
+ (
+ cd submodule &&
+ test 1 = $(git log --oneline | wc -l)
+ )
+ )
'
+test_expect_success 'submodule helper list is not confused by common prefixes' '
+ mkdir -p dir1/b &&
+ (
+ cd dir1/b &&
+ git init &&
+ echo hi >testfile2 &&
+ git add . &&
+ git commit -m "test1"
+ ) &&
+ mkdir -p dir2/b &&
+ (
+ cd dir2/b &&
+ git init &&
+ echo hello >testfile1 &&
+ git add . &&
+ git commit -m "test2"
+ ) &&
+ git submodule add /dir1/b dir1/b &&
+ git submodule add /dir2/b dir2/b &&
+ git commit -m "first submodule commit" &&
+ git submodule--helper list dir1/b |cut -c51- >actual &&
+ echo "dir1/b" >expect &&
+ test_cmp expect actual
+'
+
+
test_done
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index cee319d..366746f 100755
--- a/t/t7401-submodule-summary.sh
+++ b/t/t7401-submodule-summary.sh
@@ -37,11 +37,48 @@ head1=$(add_file sm1 foo1 foo2)
test_expect_success 'added submodule' "
git add sm1 &&
git submodule summary >actual &&
- diff actual - <<-EOF
+ cat >expected <<-EOF &&
* sm1 0000000...$head1 (2):
> Add foo2
EOF
+ test_cmp expected actual
+"
+
+test_expect_success 'added submodule (subdirectory)' "
+ mkdir sub &&
+ (
+ cd sub &&
+ git submodule summary >../actual
+ ) &&
+ cat >expected <<-EOF &&
+* ../sm1 0000000...$head1 (2):
+ > Add foo2
+
+EOF
+ test_cmp expected actual
+"
+
+test_expect_success 'added submodule (subdirectory only)' "
+ (
+ cd sub &&
+ git submodule summary . >../actual
+ ) &&
+ >expected &&
+ test_cmp expected actual
+"
+
+test_expect_success 'added submodule (subdirectory with explicit path)' "
+ (
+ cd sub &&
+ git submodule summary ../sm1 >../actual
+ ) &&
+ cat >expected <<-EOF &&
+* ../sm1 0000000...$head1 (2):
+ > Add foo2
+
+EOF
+ test_cmp expected actual
"
commit_file sm1 &&
@@ -49,43 +86,65 @@ head2=$(add_file sm1 foo3)
test_expect_success 'modified submodule(forward)' "
git submodule summary >actual &&
- diff actual - <<-EOF
+ cat >expected <<-EOF &&
* sm1 $head1...$head2 (1):
> Add foo3
EOF
+ test_cmp expected actual
"
test_expect_success 'modified submodule(forward), --files' "
git submodule summary --files >actual &&
- diff actual - <<-EOF
+ cat >expected <<-EOF &&
+* sm1 $head1...$head2 (1):
+ > Add foo3
+
+EOF
+ test_cmp expected actual
+"
+
+test_expect_success 'no ignore=all setting has any effect' "
+ git config -f .gitmodules submodule.sm1.path sm1 &&
+ git config -f .gitmodules submodule.sm1.ignore all &&
+ git config submodule.sm1.ignore all &&
+ git config diff.ignoreSubmodules all &&
+ git submodule summary >actual &&
+ cat >expected <<-EOF &&
* sm1 $head1...$head2 (1):
> Add foo3
EOF
+ test_cmp expected actual &&
+ git config --unset diff.ignoreSubmodules &&
+ git config --remove-section submodule.sm1 &&
+ git config -f .gitmodules --remove-section submodule.sm1
"
+
commit_file sm1 &&
-cd sm1 &&
-git reset --hard HEAD~2 >/dev/null &&
-head3=$(git rev-parse --verify HEAD | cut -c1-7) &&
-cd ..
+head3=$(
+ cd sm1 &&
+ git reset --hard HEAD~2 >/dev/null &&
+ git rev-parse --verify HEAD | cut -c1-7
+)
test_expect_success 'modified submodule(backward)' "
- git submodule summary >actual &&
- diff actual - <<-EOF
+ git submodule summary >actual &&
+ cat >expected <<-EOF &&
* sm1 $head2...$head3 (2):
< Add foo3
< Add foo2
EOF
+ test_cmp expected actual
"
head4=$(add_file sm1 foo4 foo5) &&
head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD)
test_expect_success 'modified submodule(backward and forward)' "
- git submodule summary >actual &&
- diff actual - <<-EOF
+ git submodule summary >actual &&
+ cat >expected <<-EOF &&
* sm1 $head2...$head4 (4):
> Add foo5
> Add foo4
@@ -93,17 +152,19 @@ test_expect_success 'modified submodule(backward and forward)' "
< Add foo2
EOF
+ test_cmp expected actual
"
test_expect_success '--summary-limit' "
- git submodule summary -n 3 >actual &&
- diff actual - <<-EOF
+ git submodule summary -n 3 >actual &&
+ cat >expected <<-EOF &&
* sm1 $head2...$head4 (4):
> Add foo5
> Add foo4
< Add foo3
EOF
+ test_cmp expected actual
"
commit_file sm1 &&
@@ -115,63 +176,69 @@ rm -f sm1 &&
mv sm1-bak sm1
test_expect_success 'typechanged submodule(submodule->blob), --cached' "
- git submodule summary --cached >actual &&
- diff actual - <<-EOF
+ git submodule summary --cached >actual &&
+ cat >expected <<-EOF &&
* sm1 $head4(submodule)->$head5(blob) (3):
< Add foo5
EOF
+ test_i18ncmp actual expected
"
test_expect_success 'typechanged submodule(submodule->blob), --files' "
- git submodule summary --files >actual &&
- diff actual - <<-EOF
+ git submodule summary --files >actual &&
+ cat >expected <<-EOF &&
* sm1 $head5(blob)->$head4(submodule) (3):
> Add foo5
EOF
+ test_i18ncmp actual expected
"
rm -rf sm1 &&
git checkout-index sm1
test_expect_success 'typechanged submodule(submodule->blob)' "
- git submodule summary >actual &&
- diff actual - <<-EOF
+ git submodule summary >actual &&
+ cat >expected <<-EOF &&
* sm1 $head4(submodule)->$head5(blob):
EOF
+ test_i18ncmp actual expected
"
rm -f sm1 &&
test_create_repo sm1 &&
head6=$(add_file sm1 foo6 foo7)
test_expect_success 'nonexistent commit' "
- git submodule summary >actual &&
- diff actual - <<-EOF
+ git submodule summary >actual &&
+ cat >expected <<-EOF &&
* sm1 $head4...$head6:
Warn: sm1 doesn't contain commit $head4_full
EOF
+ test_i18ncmp actual expected
"
commit_file
test_expect_success 'typechanged submodule(blob->submodule)' "
- git submodule summary >actual &&
- diff actual - <<-EOF
+ git submodule summary >actual &&
+ cat >expected <<-EOF &&
* sm1 $head5(blob)->$head6(submodule) (2):
> Add foo7
EOF
+ test_i18ncmp expected actual
"
commit_file sm1 &&
rm -rf sm1
test_expect_success 'deleted submodule' "
- git submodule summary >actual &&
- diff actual - <<-EOF
+ git submodule summary >actual &&
+ cat >expected <<-EOF &&
* sm1 $head6...0000000:
EOF
+ test_cmp expected actual
"
test_create_repo sm2 &&
@@ -179,59 +246,60 @@ head7=$(add_file sm2 foo8 foo9) &&
git add sm2
test_expect_success 'multiple submodules' "
- git submodule summary >actual &&
- diff actual - <<-EOF
+ git submodule summary >actual &&
+ cat >expected <<-EOF &&
* sm1 $head6...0000000:
* sm2 0000000...$head7 (2):
> Add foo9
EOF
+ test_cmp expected actual
"
test_expect_success 'path filter' "
- git submodule summary sm2 >actual &&
- diff actual - <<-EOF
+ git submodule summary sm2 >actual &&
+ cat >expected <<-EOF &&
* sm2 0000000...$head7 (2):
> Add foo9
EOF
+ test_cmp expected actual
"
commit_file sm2
test_expect_success 'given commit' "
- git submodule summary HEAD^ >actual &&
- diff actual - <<-EOF
+ git submodule summary HEAD^ >actual &&
+ cat >expected <<-EOF &&
* sm1 $head6...0000000:
* sm2 0000000...$head7 (2):
> Add foo9
EOF
+ test_cmp expected actual
"
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
-#
+ git submodule summary --for-status HEAD^ >actual &&
+ test_i18ncmp actual - <<EOF
+* sm1 $head6...0000000:
+
+* sm2 0000000...$head7 (2):
+ > Add foo9
+
EOF
"
test_expect_success 'fail when using --files together with --cached' "
- test_must_fail git submodule summary --files --cached
+ test_must_fail git submodule summary --files --cached
"
test_expect_success 'should not fail in an empty repo' "
- git init xyzzy &&
- cd xyzzy &&
- git submodule summary >output 2>&1 &&
- test_cmp output /dev/null
+ git init xyzzy &&
+ cd xyzzy &&
+ git submodule summary >output 2>&1 &&
+ test_cmp output /dev/null
"
test_done
diff --git a/t/t7402-submodule-rebase.sh b/t/t7402-submodule-rebase.sh
index f919c8d..8e32f19 100755
--- a/t/t7402-submodule-rebase.sh
+++ b/t/t7402-submodule-rebase.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2008 Johannes Schindelin
#
-test_description='Test rebasing and stashing with dirty submodules'
+test_description='Test rebasing, stashing, etc. with submodules'
. ./test-lib.sh
@@ -20,7 +20,8 @@ test_expect_success setup '
echo second line >> file &&
(cd submodule && git pull) &&
test_tick &&
- git commit -m file-and-submodule -a
+ git commit -m file-and-submodule -a &&
+ git branch added-submodule
'
@@ -89,4 +90,29 @@ test_expect_success 'stash with a dirty submodule' '
'
+test_expect_success 'rebasing submodule that should conflict' '
+ git reset --hard &&
+ git checkout added-submodule &&
+ git add submodule &&
+ test_tick &&
+ git commit -m third &&
+ (
+ cd submodule &&
+ git commit --allow-empty -m extra
+ ) &&
+ git add submodule &&
+ test_tick &&
+ git commit -m fourth &&
+
+ test_must_fail git rebase --onto HEAD^^ HEAD^ HEAD^0 &&
+ git ls-files -s submodule >actual &&
+ (
+ cd submodule &&
+ echo "160000 $(git rev-parse HEAD^) 1 submodule" &&
+ echo "160000 $(git rev-parse HEAD^^) 2 submodule" &&
+ echo "160000 $(git rev-parse HEAD) 3 submodule"
+ ) >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t7403-submodule-sync.sh b/t/t7403-submodule-sync.sh
index 7538756..5503ec0 100755
--- a/t/t7403-submodule-sync.sh
+++ b/t/t7403-submodule-sync.sh
@@ -11,54 +11,340 @@ These tests exercise the "git submodule sync" subcommand.
. ./test-lib.sh
test_expect_success setup '
- echo file > file &&
+ echo file >file &&
git add file &&
test_tick &&
- git commit -m upstream
+ git commit -m upstream &&
git clone . super &&
git clone super submodule &&
- (cd super &&
- git submodule add ../submodule submodule &&
- test_tick &&
- git commit -m "submodule"
+ (
+ cd submodule &&
+ git submodule add ../submodule sub-submodule &&
+ test_tick &&
+ git commit -m "sub-submodule"
+ ) &&
+ (
+ cd super &&
+ git submodule add ../submodule submodule &&
+ test_tick &&
+ git commit -m "submodule"
) &&
git clone super super-clone &&
- (cd super-clone && git submodule update --init)
+ (
+ cd super-clone &&
+ git submodule update --init --recursive
+ ) &&
+ git clone super empty-clone &&
+ (
+ cd empty-clone &&
+ git submodule init
+ ) &&
+ git clone super top-only-clone &&
+ git clone super relative-clone &&
+ (
+ cd relative-clone &&
+ git submodule update --init --recursive
+ ) &&
+ git clone super recursive-clone &&
+ (
+ cd recursive-clone &&
+ git submodule update --init --recursive
+ )
'
test_expect_success 'change submodule' '
- (cd submodule &&
- echo second line >> file &&
- test_tick &&
- git commit -a -m "change submodule"
+ (
+ cd submodule &&
+ echo second line >>file &&
+ test_tick &&
+ git commit -a -m "change submodule"
)
'
+reset_submodule_urls () {
+ (
+ root=$(pwd) &&
+ cd super-clone/submodule &&
+ git config remote.origin.url "$root/submodule"
+ ) &&
+ (
+ root=$(pwd) &&
+ cd super-clone/submodule/sub-submodule &&
+ git config remote.origin.url "$root/submodule"
+ )
+}
+
test_expect_success 'change submodule url' '
- (cd super &&
- cd submodule &&
- git checkout master &&
- git pull
+ (
+ cd super &&
+ cd submodule &&
+ git checkout master &&
+ git pull
) &&
mv submodule moved-submodule &&
- (cd super &&
- git config -f .gitmodules submodule.submodule.url ../moved-submodule
- test_tick &&
- git commit -a -m moved-submodule
+ (
+ cd moved-submodule &&
+ git config -f .gitmodules submodule.sub-submodule.url ../moved-submodule &&
+ test_tick &&
+ git commit -a -m moved-sub-submodule
+ ) &&
+ (
+ cd super &&
+ git config -f .gitmodules submodule.submodule.url ../moved-submodule &&
+ test_tick &&
+ git commit -a -m moved-submodule
)
'
test_expect_success '"git submodule sync" should update submodule URLs' '
- (cd super-clone &&
- git pull &&
- git submodule sync
+ (
+ cd super-clone &&
+ git pull --no-recurse-submodules &&
+ git submodule sync
+ ) &&
+ test -d "$(
+ cd super-clone/submodule &&
+ git config remote.origin.url
+ )" &&
+ test ! -d "$(
+ cd super-clone/submodule/sub-submodule &&
+ git config remote.origin.url
+ )" &&
+ (
+ cd super-clone/submodule &&
+ git checkout master &&
+ git pull
+ ) &&
+ (
+ cd super-clone &&
+ test -d "$(git config submodule.submodule.url)"
+ )
+'
+
+test_expect_success '"git submodule sync --recursive" should update all submodule URLs' '
+ (
+ cd super-clone &&
+ (
+ cd submodule &&
+ git pull --no-recurse-submodules
+ ) &&
+ git submodule sync --recursive
) &&
- test -d "$(git config -f super-clone/submodule/.git/config \
- remote.origin.url)" &&
- (cd super-clone/submodule &&
- git checkout master &&
- git pull
+ test -d "$(
+ cd super-clone/submodule &&
+ git config remote.origin.url
+ )" &&
+ test -d "$(
+ cd super-clone/submodule/sub-submodule &&
+ git config remote.origin.url
+ )" &&
+ (
+ cd super-clone/submodule/sub-submodule &&
+ git checkout master &&
+ git pull
+ )
+'
+
+test_expect_success 'reset submodule URLs' '
+ reset_submodule_urls super-clone
+'
+
+test_expect_success '"git submodule sync" should update submodule URLs - subdirectory' '
+ (
+ cd super-clone &&
+ git pull --no-recurse-submodules &&
+ mkdir -p sub &&
+ cd sub &&
+ git submodule sync >../../output
+ ) &&
+ grep "\\.\\./submodule" output &&
+ test -d "$(
+ cd super-clone/submodule &&
+ git config remote.origin.url
+ )" &&
+ test ! -d "$(
+ cd super-clone/submodule/sub-submodule &&
+ git config remote.origin.url
+ )" &&
+ (
+ cd super-clone/submodule &&
+ git checkout master &&
+ git pull
+ ) &&
+ (
+ cd super-clone &&
+ test -d "$(git config submodule.submodule.url)"
+ )
+'
+
+test_expect_success '"git submodule sync --recursive" should update all submodule URLs - subdirectory' '
+ (
+ cd super-clone &&
+ (
+ cd submodule &&
+ git pull --no-recurse-submodules
+ ) &&
+ mkdir -p sub &&
+ cd sub &&
+ git submodule sync --recursive >../../output
+ ) &&
+ grep "\\.\\./submodule/sub-submodule" output &&
+ test -d "$(
+ cd super-clone/submodule &&
+ git config remote.origin.url
+ )" &&
+ test -d "$(
+ cd super-clone/submodule/sub-submodule &&
+ git config remote.origin.url
+ )" &&
+ (
+ cd super-clone/submodule/sub-submodule &&
+ git checkout master &&
+ git pull
+ )
+'
+
+test_expect_success '"git submodule sync" should update known submodule URLs' '
+ (
+ cd empty-clone &&
+ git pull &&
+ git submodule sync &&
+ test -d "$(git config submodule.submodule.url)"
)
'
+test_expect_success '"git submodule sync" should not vivify uninteresting submodule' '
+ (
+ cd top-only-clone &&
+ git pull &&
+ git submodule sync &&
+ test -z "$(git config submodule.submodule.url)" &&
+ git submodule sync submodule &&
+ test -z "$(git config submodule.submodule.url)"
+ )
+'
+
+test_expect_success '"git submodule sync" handles origin URL of the form foo' '
+ (
+ cd relative-clone &&
+ git remote set-url origin foo &&
+ git submodule sync &&
+ (
+ cd submodule &&
+ #actual fails with: "cannot strip off url foo
+ test "$(git config remote.origin.url)" = "../submodule"
+ )
+ )
+'
+
+test_expect_success '"git submodule sync" handles origin URL of the form foo/bar' '
+ (
+ cd relative-clone &&
+ git remote set-url origin foo/bar &&
+ git submodule sync &&
+ (
+ cd submodule &&
+ #actual foo/submodule
+ test "$(git config remote.origin.url)" = "../foo/submodule"
+ ) &&
+ (
+ cd submodule/sub-submodule &&
+ test "$(git config remote.origin.url)" != "../../foo/submodule"
+ )
+ )
+'
+
+test_expect_success '"git submodule sync --recursive" propagates changes in origin' '
+ (
+ cd recursive-clone &&
+ git remote set-url origin foo/bar &&
+ git submodule sync --recursive &&
+ (
+ cd submodule &&
+ #actual foo/submodule
+ test "$(git config remote.origin.url)" = "../foo/submodule"
+ ) &&
+ (
+ cd submodule/sub-submodule &&
+ test "$(git config remote.origin.url)" = "../../foo/submodule"
+ )
+ )
+'
+
+test_expect_success '"git submodule sync" handles origin URL of the form ./foo' '
+ (
+ cd relative-clone &&
+ git remote set-url origin ./foo &&
+ git submodule sync &&
+ (
+ cd submodule &&
+ #actual ./submodule
+ test "$(git config remote.origin.url)" = "../submodule"
+ )
+ )
+'
+
+test_expect_success '"git submodule sync" handles origin URL of the form ./foo/bar' '
+ (
+ cd relative-clone &&
+ git remote set-url origin ./foo/bar &&
+ git submodule sync &&
+ (
+ cd submodule &&
+ #actual ./foo/submodule
+ test "$(git config remote.origin.url)" = "../foo/submodule"
+ )
+ )
+'
+
+test_expect_success '"git submodule sync" handles origin URL of the form ../foo' '
+ (
+ cd relative-clone &&
+ git remote set-url origin ../foo &&
+ git submodule sync &&
+ (
+ cd submodule &&
+ #actual ../submodule
+ test "$(git config remote.origin.url)" = "../../submodule"
+ )
+ )
+'
+
+test_expect_success '"git submodule sync" handles origin URL of the form ../foo/bar' '
+ (
+ cd relative-clone &&
+ git remote set-url origin ../foo/bar &&
+ git submodule sync &&
+ (
+ cd submodule &&
+ #actual ../foo/submodule
+ test "$(git config remote.origin.url)" = "../../foo/submodule"
+ )
+ )
+'
+
+test_expect_success '"git submodule sync" handles origin URL of the form ../foo/bar with deeply nested submodule' '
+ (
+ cd relative-clone &&
+ git remote set-url origin ../foo/bar &&
+ mkdir -p a/b/c &&
+ (
+ cd a/b/c &&
+ git init &&
+ >.gitignore &&
+ git add .gitignore &&
+ test_tick &&
+ git commit -m "initial commit"
+ ) &&
+ git submodule add ../bar/a/b/c ./a/b/c &&
+ git submodule sync &&
+ (
+ cd a/b/c &&
+ #actual ../foo/bar/a/b/c
+ test "$(git config remote.origin.url)" = "../../../../foo/bar/a/b/c"
+ )
+ )
+'
+
+
test_done
diff --git a/t/t7405-submodule-merge.sh b/t/t7405-submodule-merge.sh
index 4a7b893..0d5b42a 100755
--- a/t/t7405-submodule-merge.sh
+++ b/t/t7405-submodule-merge.sh
@@ -54,21 +54,229 @@ test_expect_success setup '
git merge -s ours a
'
-test_expect_success 'merging with modify/modify conflict' '
+# History setup
+#
+# b
+# / \
+# init -- a d
+# \ \ /
+# g c
+#
+# a in the main repository records to sub-a in the submodule and
+# analogous b and c. d should be automatically found by merging c into
+# b in the main repository.
+test_expect_success 'setup for merge search' '
+ mkdir merge-search &&
+ (cd merge-search &&
+ git init &&
+ mkdir sub &&
+ (cd sub &&
+ git init &&
+ echo "file-a" > file-a &&
+ git add file-a &&
+ git commit -m "sub-a" &&
+ git branch sub-a) &&
+ git commit --allow-empty -m init &&
+ git branch init &&
+ git add sub &&
+ git commit -m "a" &&
+ git branch a &&
+
+ git checkout -b b &&
+ (cd sub &&
+ git checkout -b sub-b &&
+ echo "file-b" > file-b &&
+ git add file-b &&
+ git commit -m "sub-b") &&
+ git commit -a -m "b" &&
+
+ git checkout -b c a &&
+ (cd sub &&
+ git checkout -b sub-c sub-a &&
+ echo "file-c" > file-c &&
+ git add file-c &&
+ git commit -m "sub-c") &&
+ git commit -a -m "c" &&
+
+ git checkout -b d a &&
+ (cd sub &&
+ git checkout -b sub-d sub-b &&
+ git merge sub-c) &&
+ git commit -a -m "d" &&
+ git branch test b &&
+
+ git checkout -b g init &&
+ (cd sub &&
+ git checkout -b sub-g sub-c) &&
+ git add sub &&
+ git commit -a -m "g")
+'
+
+test_expect_success 'merge with one side as a fast-forward of the other' '
+ (cd merge-search &&
+ git checkout -b test-forward b &&
+ git merge d &&
+ git ls-tree test-forward sub | cut -f1 | cut -f3 -d" " > actual &&
+ (cd sub &&
+ git rev-parse sub-d > ../expect) &&
+ test_cmp actual expect)
+'
+
+test_expect_success 'merging should conflict for non fast-forward' '
+ (cd merge-search &&
+ git checkout -b test-nonforward b &&
+ (cd sub &&
+ git rev-parse sub-d > ../expect) &&
+ test_must_fail git merge c 2> actual &&
+ grep $(cat expect) actual > /dev/null &&
+ git reset --hard)
+'
+
+test_expect_success 'merging should fail for ambiguous common parent' '
+ (cd merge-search &&
+ git checkout -b test-ambiguous b &&
+ (cd sub &&
+ git checkout -b ambiguous sub-b &&
+ git merge sub-c &&
+ git rev-parse sub-d > ../expect1 &&
+ git rev-parse ambiguous > ../expect2) &&
+ test_must_fail git merge c 2> actual &&
+ grep $(cat expect1) actual > /dev/null &&
+ grep $(cat expect2) actual > /dev/null &&
+ git reset --hard)
+'
- git checkout -b test1 a &&
+# in a situation like this
+#
+# submodule tree:
+#
+# sub-a --- sub-b --- sub-d
+#
+# main tree:
+#
+# e (sub-a)
+# /
+# bb (sub-b)
+# \
+# f (sub-d)
+#
+# A merge between e and f should fail because one of the submodule
+# commits (sub-a) does not descend from the submodule merge-base (sub-b).
+#
+test_expect_success 'merging should fail for changes that are backwards' '
+ (cd merge-search &&
+ git checkout -b bb a &&
+ (cd sub &&
+ git checkout sub-b) &&
+ git commit -a -m "bb" &&
+
+ git checkout -b e bb &&
+ (cd sub &&
+ git checkout sub-a) &&
+ git commit -a -m "e" &&
+
+ git checkout -b f bb &&
+ (cd sub &&
+ git checkout sub-d) &&
+ git commit -a -m "f" &&
+
+ git checkout -b test-backward e &&
+ test_must_fail git merge f)
+'
+
+
+# Check that the conflicting submodule is detected when it is
+# in the common ancestor. status should be 'U00...00"
+test_expect_success 'git submodule status should display the merge conflict properly with merge base' '
+ (cd merge-search &&
+ cat >.gitmodules <<EOF &&
+[submodule "sub"]
+ path = sub
+ url = $TRASH_DIRECTORY/sub
+EOF
+ cat >expect <<EOF &&
+U0000000000000000000000000000000000000000 sub
+EOF
+ git submodule status > actual &&
+ test_cmp expect actual &&
+ git reset --hard)
+'
+
+# Check that the conflicting submodule is detected when it is
+# not in the common ancestor. status should be 'U00...00"
+test_expect_success 'git submodule status should display the merge conflict properly without merge-base' '
+ (cd merge-search &&
+ git checkout -b test-no-merge-base g &&
test_must_fail git merge b &&
- test -f .git/MERGE_MSG &&
- git diff &&
- test -n "$(git ls-files -u)"
+ cat >.gitmodules <<EOF &&
+[submodule "sub"]
+ path = sub
+ url = $TRASH_DIRECTORY/sub
+EOF
+ cat >expect <<EOF &&
+U0000000000000000000000000000000000000000 sub
+EOF
+ git submodule status > actual &&
+ test_cmp expect actual &&
+ git reset --hard)
'
-test_expect_success 'merging with a modify/modify conflict between merge bases' '
+test_expect_success 'merging with a modify/modify conflict between merge bases' '
git reset --hard HEAD &&
git checkout -b test2 c &&
git merge d
+'
+
+# canonical criss-cross history in top and submodule
+test_expect_success 'setup for recursive merge with submodule' '
+ mkdir merge-recursive &&
+ (cd merge-recursive &&
+ git init &&
+ mkdir sub &&
+ (cd sub &&
+ git init &&
+ test_commit a &&
+ git checkout -b sub-b master &&
+ test_commit b &&
+ git checkout -b sub-c master &&
+ test_commit c &&
+ git checkout -b sub-bc sub-b &&
+ git merge sub-c &&
+ git checkout -b sub-cb sub-c &&
+ git merge sub-b &&
+ git checkout master) &&
+ git add sub &&
+ git commit -m a &&
+ git checkout -b top-b master &&
+ (cd sub && git checkout sub-b) &&
+ git add sub &&
+ git commit -m b &&
+ git checkout -b top-c master &&
+ (cd sub && git checkout sub-c) &&
+ git add sub &&
+ git commit -m c &&
+ git checkout -b top-bc top-b &&
+ git merge -s ours --no-commit top-c &&
+ (cd sub && git checkout sub-bc) &&
+ git add sub &&
+ git commit -m bc &&
+ git checkout -b top-cb top-c &&
+ git merge -s ours --no-commit top-b &&
+ (cd sub && git checkout sub-cb) &&
+ git add sub &&
+ git commit -m cb)
+'
+# merge should leave submodule unmerged in index
+test_expect_success 'recursive merge with submodule' '
+ (cd merge-recursive &&
+ test_must_fail git merge top-bc &&
+ echo "160000 $(git rev-parse top-cb:sub) 2 sub" > expect2 &&
+ echo "160000 $(git rev-parse top-bc:sub) 3 sub" > expect3 &&
+ git ls-files -u > actual &&
+ grep "$(cat expect2)" actual > /dev/null &&
+ grep "$(cat expect3)" actual > /dev/null)
'
test_done
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 1382a8e..5f27879 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -14,8 +14,8 @@ submodule and "git submodule update --rebase/--merge" does not detach the HEAD.
compare_head()
{
- sha_master=`git rev-list --max-count=1 master`
- sha_head=`git rev-list --max-count=1 HEAD`
+ sha_master=$(git rev-list --max-count=1 master)
+ sha_head=$(git rev-list --max-count=1 HEAD)
test "$sha_master" = "$sha_head"
}
@@ -25,11 +25,12 @@ test_expect_success 'setup a submodule tree' '
echo file > file &&
git add file &&
test_tick &&
- git commit -m upstream
+ git commit -m upstream &&
git clone . super &&
git clone super submodule &&
git clone super rebasing &&
git clone super merging &&
+ git clone super none &&
(cd super &&
git submodule add ../submodule submodule &&
test_tick &&
@@ -57,6 +58,15 @@ test_expect_success 'setup a submodule tree' '
git submodule add ../merging merging &&
test_tick &&
git commit -m "rebasing"
+ ) &&
+ (cd super &&
+ git submodule add ../none none &&
+ test_tick &&
+ git commit -m "none"
+ ) &&
+ git clone . recursivesuper &&
+ ( cd recursivesuper
+ git submodule add ../super super
)
'
@@ -74,6 +84,148 @@ test_expect_success 'submodule update detaching the HEAD ' '
)
'
+test_expect_success 'submodule update from subdirectory' '
+ (cd super/submodule &&
+ git reset --hard HEAD~1
+ ) &&
+ mkdir super/sub &&
+ (cd super/sub &&
+ (cd ../submodule &&
+ compare_head
+ ) &&
+ git submodule update ../submodule &&
+ cd ../submodule &&
+ ! compare_head
+ )
+'
+
+supersha1=$(git -C super rev-parse HEAD)
+mergingsha1=$(git -C super/merging rev-parse HEAD)
+nonesha1=$(git -C super/none rev-parse HEAD)
+rebasingsha1=$(git -C super/rebasing rev-parse HEAD)
+submodulesha1=$(git -C super/submodule rev-parse HEAD)
+pwd=$(pwd)
+
+cat <<EOF >expect
+Submodule path '../super': checked out '$supersha1'
+Submodule path '../super/merging': checked out '$mergingsha1'
+Submodule path '../super/none': checked out '$nonesha1'
+Submodule path '../super/rebasing': checked out '$rebasingsha1'
+Submodule path '../super/submodule': checked out '$submodulesha1'
+EOF
+
+cat <<EOF >expect2
+Submodule 'merging' ($pwd/merging) registered for path '../super/merging'
+Submodule 'none' ($pwd/none) registered for path '../super/none'
+Submodule 'rebasing' ($pwd/rebasing) registered for path '../super/rebasing'
+Submodule 'submodule' ($pwd/submodule) registered for path '../super/submodule'
+Cloning into '$pwd/recursivesuper/super/merging'...
+done.
+Cloning into '$pwd/recursivesuper/super/none'...
+done.
+Cloning into '$pwd/recursivesuper/super/rebasing'...
+done.
+Cloning into '$pwd/recursivesuper/super/submodule'...
+done.
+EOF
+
+test_expect_success 'submodule update --init --recursive from subdirectory' '
+ git -C recursivesuper/super reset --hard HEAD^ &&
+ (cd recursivesuper &&
+ mkdir tmp &&
+ cd tmp &&
+ git submodule update --init --recursive ../super >../../actual 2>../../actual2
+ ) &&
+ test_cmp expect actual &&
+ test_cmp expect2 actual2
+'
+
+apos="'";
+test_expect_success 'submodule update does not fetch already present commits' '
+ (cd submodule &&
+ echo line3 >> file &&
+ git add file &&
+ test_tick &&
+ git commit -m "upstream line3"
+ ) &&
+ (cd super/submodule &&
+ head=$(git rev-parse --verify HEAD) &&
+ echo "Submodule path ${apos}submodule$apos: checked out $apos$head$apos" > ../../expected &&
+ git reset --hard HEAD~1
+ ) &&
+ (cd super &&
+ git submodule update > ../actual 2> ../actual.err
+ ) &&
+ test_i18ncmp expected actual &&
+ ! test -s actual.err
+'
+
+test_expect_success 'submodule update should fail due to local changes' '
+ (cd super/submodule &&
+ git reset --hard HEAD~1 &&
+ echo "local change" > file
+ ) &&
+ (cd super &&
+ (cd submodule &&
+ compare_head
+ ) &&
+ test_must_fail git submodule update submodule
+ )
+'
+test_expect_success 'submodule update should throw away changes with --force ' '
+ (cd super &&
+ (cd submodule &&
+ compare_head
+ ) &&
+ git submodule update --force submodule &&
+ cd submodule &&
+ ! compare_head
+ )
+'
+
+test_expect_success 'submodule update --force forcibly checks out submodules' '
+ (cd super &&
+ (cd submodule &&
+ rm -f file
+ ) &&
+ git submodule update --force submodule &&
+ (cd submodule &&
+ test "$(git status -s file)" = ""
+ )
+ )
+'
+
+test_expect_success 'submodule update --remote should fetch upstream changes' '
+ (cd submodule &&
+ echo line4 >> file &&
+ git add file &&
+ test_tick &&
+ git commit -m "upstream line4"
+ ) &&
+ (cd super &&
+ git submodule update --remote --force submodule &&
+ cd submodule &&
+ test "$(git log -1 --oneline)" = "$(GIT_DIR=../../submodule/.git git log -1 --oneline)"
+ )
+'
+
+test_expect_success 'local config should override .gitmodules branch' '
+ (cd submodule &&
+ git checkout -b test-branch &&
+ echo line5 >> file &&
+ git add file &&
+ test_tick &&
+ git commit -m "upstream line5" &&
+ git checkout master
+ ) &&
+ (cd super &&
+ git config submodule.submodule.branch test-branch &&
+ git submodule update --remote --force submodule &&
+ cd submodule &&
+ test "$(git log -1 --oneline)" = "$(GIT_DIR=../../submodule/.git git log -1 --oneline test-branch)"
+ )
+'
+
test_expect_success 'submodule update --rebase staying on master' '
(cd super/submodule &&
git checkout master
@@ -187,6 +339,93 @@ test_expect_success 'submodule update - checkout in .git/config' '
)
'
+test_expect_success 'submodule update - command in .git/config' '
+ (cd super &&
+ git config submodule.submodule.update "!git checkout"
+ ) &&
+ (cd super/submodule &&
+ git reset --hard HEAD^
+ ) &&
+ (cd super &&
+ (cd submodule &&
+ compare_head
+ ) &&
+ git submodule update submodule &&
+ cd submodule &&
+ ! compare_head
+ )
+'
+
+cat << EOF >expect
+Execution of 'false $submodulesha1' failed in submodule path 'submodule'
+EOF
+
+test_expect_success 'submodule update - command in .git/config catches failure' '
+ (cd super &&
+ git config submodule.submodule.update "!false"
+ ) &&
+ (cd super/submodule &&
+ git reset --hard $submodulesha1^
+ ) &&
+ (cd super &&
+ test_must_fail git submodule update submodule 2>../actual
+ ) &&
+ test_cmp actual expect
+'
+
+cat << EOF >expect
+Execution of 'false $submodulesha1' failed in submodule path '../submodule'
+EOF
+
+test_expect_success 'submodule update - command in .git/config catches failure -- subdirectory' '
+ (cd super &&
+ git config submodule.submodule.update "!false"
+ ) &&
+ (cd super/submodule &&
+ git reset --hard $submodulesha1^
+ ) &&
+ (cd super &&
+ mkdir tmp && cd tmp &&
+ test_must_fail git submodule update ../submodule 2>../../actual
+ ) &&
+ test_cmp actual expect
+'
+
+cat << EOF >expect
+Execution of 'false $submodulesha1' failed in submodule path '../super/submodule'
+Failed to recurse into submodule path '../super'
+EOF
+
+test_expect_success 'recursive submodule update - command in .git/config catches failure -- subdirectory' '
+ (cd recursivesuper &&
+ git submodule update --remote super &&
+ git add super &&
+ git commit -m "update to latest to have more than one commit in submodules"
+ ) &&
+ git -C recursivesuper/super config submodule.submodule.update "!false" &&
+ git -C recursivesuper/super/submodule reset --hard $submodulesha1^ &&
+ (cd recursivesuper &&
+ mkdir -p tmp && cd tmp &&
+ test_must_fail git submodule update --recursive ../super 2>../../actual
+ ) &&
+ test_cmp actual expect
+'
+
+test_expect_success 'submodule init does not copy command into .git/config' '
+ (cd super &&
+ H=$(git ls-files -s submodule | cut -d" " -f2) &&
+ mkdir submodule1 &&
+ git update-index --add --cacheinfo 160000 $H submodule1 &&
+ git config -f .gitmodules submodule.submodule1.path submodule1 &&
+ git config -f .gitmodules submodule.submodule1.url ../submodule &&
+ git config -f .gitmodules submodule.submodule1.update !false &&
+ git submodule init submodule1 &&
+ echo "none" >expect &&
+ git config submodule.submodule1.update >actual &&
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'submodule init picks up rebase' '
(cd super &&
git config -f .gitmodules submodule.rebasing.update rebase &&
@@ -203,4 +442,451 @@ test_expect_success 'submodule init picks up merge' '
)
'
+test_expect_success 'submodule update --merge - ignores --merge for new submodules' '
+ (cd super &&
+ rm -rf submodule &&
+ git submodule update submodule &&
+ git status -s submodule >expect &&
+ rm -rf submodule &&
+ git submodule update --merge submodule &&
+ git status -s submodule >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'submodule update --rebase - ignores --rebase for new submodules' '
+ (cd super &&
+ rm -rf submodule &&
+ git submodule update submodule &&
+ git status -s submodule >expect &&
+ rm -rf submodule &&
+ git submodule update --rebase submodule &&
+ git status -s submodule >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'submodule update ignores update=merge config for new submodules' '
+ (cd super &&
+ rm -rf submodule &&
+ git submodule update submodule &&
+ git status -s submodule >expect &&
+ rm -rf submodule &&
+ git config submodule.submodule.update merge &&
+ git submodule update submodule &&
+ git status -s submodule >actual &&
+ git config --unset submodule.submodule.update &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'submodule update ignores update=rebase config for new submodules' '
+ (cd super &&
+ rm -rf submodule &&
+ git submodule update submodule &&
+ git status -s submodule >expect &&
+ rm -rf submodule &&
+ git config submodule.submodule.update rebase &&
+ git submodule update submodule &&
+ git status -s submodule >actual &&
+ git config --unset submodule.submodule.update &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'submodule init picks up update=none' '
+ (cd super &&
+ git config -f .gitmodules submodule.none.update none &&
+ git submodule init none &&
+ test "none" = "$(git config submodule.none.update)"
+ )
+'
+
+test_expect_success 'submodule update - update=none in .git/config' '
+ (cd super &&
+ git config submodule.submodule.update none &&
+ (cd submodule &&
+ git checkout master &&
+ compare_head
+ ) &&
+ git diff --raw | grep " submodule" &&
+ git submodule update &&
+ git diff --raw | grep " submodule" &&
+ (cd submodule &&
+ compare_head
+ ) &&
+ git config --unset submodule.submodule.update &&
+ git submodule update submodule
+ )
+'
+
+test_expect_success 'submodule update - update=none in .git/config but --checkout given' '
+ (cd super &&
+ git config submodule.submodule.update none &&
+ (cd submodule &&
+ git checkout master &&
+ compare_head
+ ) &&
+ git diff --raw | grep " submodule" &&
+ git submodule update --checkout &&
+ test_must_fail git diff --raw \| grep " submodule" &&
+ (cd submodule &&
+ test_must_fail compare_head
+ ) &&
+ git config --unset submodule.submodule.update
+ )
+'
+
+test_expect_success 'submodule update --init skips submodule with update=none' '
+ (cd super &&
+ git add .gitmodules &&
+ git commit -m ".gitmodules"
+ ) &&
+ git clone super cloned &&
+ (cd cloned &&
+ git submodule update --init &&
+ test -e submodule/.git &&
+ test_must_fail test -e none/.git
+ )
+'
+
+test_expect_success 'submodule update continues after checkout error' '
+ (cd super &&
+ git reset --hard HEAD &&
+ git submodule add ../submodule submodule2 &&
+ git submodule init &&
+ git commit -am "new_submodule" &&
+ (cd submodule2 &&
+ git rev-parse --verify HEAD >../expect
+ ) &&
+ (cd submodule &&
+ test_commit "update_submodule" file
+ ) &&
+ (cd submodule2 &&
+ test_commit "update_submodule2" file
+ ) &&
+ git add submodule &&
+ git add submodule2 &&
+ git commit -m "two_new_submodule_commits" &&
+ (cd submodule &&
+ echo "" > file
+ ) &&
+ git checkout HEAD^ &&
+ test_must_fail git submodule update &&
+ (cd submodule2 &&
+ git rev-parse --verify HEAD >../actual
+ ) &&
+ test_cmp expect actual
+ )
+'
+test_expect_success 'submodule update continues after recursive checkout error' '
+ (cd super &&
+ git reset --hard HEAD &&
+ git checkout master &&
+ git submodule update &&
+ (cd submodule &&
+ git submodule add ../submodule subsubmodule &&
+ git submodule init &&
+ git commit -m "new_subsubmodule"
+ ) &&
+ git add submodule &&
+ git commit -m "update_submodule" &&
+ (cd submodule &&
+ (cd subsubmodule &&
+ test_commit "update_subsubmodule" file
+ ) &&
+ git add subsubmodule &&
+ test_commit "update_submodule_again" file &&
+ (cd subsubmodule &&
+ test_commit "update_subsubmodule_again" file
+ ) &&
+ test_commit "update_submodule_again_again" file
+ ) &&
+ (cd submodule2 &&
+ git rev-parse --verify HEAD >../expect &&
+ test_commit "update_submodule2_again" file
+ ) &&
+ git add submodule &&
+ git add submodule2 &&
+ git commit -m "new_commits" &&
+ git checkout HEAD^ &&
+ (cd submodule &&
+ git checkout HEAD^ &&
+ (cd subsubmodule &&
+ echo "" > file
+ )
+ ) &&
+ test_must_fail git submodule update --recursive &&
+ (cd submodule2 &&
+ git rev-parse --verify HEAD >../actual
+ ) &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'submodule update exit immediately in case of merge conflict' '
+ (cd super &&
+ git checkout master &&
+ git reset --hard HEAD &&
+ (cd submodule &&
+ (cd subsubmodule &&
+ git reset --hard HEAD
+ )
+ ) &&
+ git submodule update --recursive &&
+ (cd submodule &&
+ test_commit "update_submodule_2" file
+ ) &&
+ (cd submodule2 &&
+ test_commit "update_submodule2_2" file
+ ) &&
+ git add submodule &&
+ git add submodule2 &&
+ git commit -m "two_new_submodule_commits" &&
+ (cd submodule &&
+ git checkout master &&
+ test_commit "conflict" file &&
+ echo "conflict" > file
+ ) &&
+ git checkout HEAD^ &&
+ (cd submodule2 &&
+ git rev-parse --verify HEAD >../expect
+ ) &&
+ git config submodule.submodule.update merge &&
+ test_must_fail git submodule update &&
+ (cd submodule2 &&
+ git rev-parse --verify HEAD >../actual
+ ) &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'submodule update exit immediately after recursive rebase error' '
+ (cd super &&
+ git checkout master &&
+ git reset --hard HEAD &&
+ (cd submodule &&
+ git reset --hard HEAD &&
+ git submodule update --recursive
+ ) &&
+ (cd submodule &&
+ test_commit "update_submodule_3" file
+ ) &&
+ (cd submodule2 &&
+ test_commit "update_submodule2_3" file
+ ) &&
+ git add submodule &&
+ git add submodule2 &&
+ git commit -m "two_new_submodule_commits" &&
+ (cd submodule &&
+ git checkout master &&
+ test_commit "conflict2" file &&
+ echo "conflict" > file
+ ) &&
+ git checkout HEAD^ &&
+ (cd submodule2 &&
+ git rev-parse --verify HEAD >../expect
+ ) &&
+ git config submodule.submodule.update rebase &&
+ test_must_fail git submodule update &&
+ (cd submodule2 &&
+ git rev-parse --verify HEAD >../actual
+ ) &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'add different submodules to the same path' '
+ (cd super &&
+ git submodule add ../submodule s1 &&
+ test_must_fail git submodule add ../merging s1
+ )
+'
+
+test_expect_success 'submodule add places git-dir in superprojects git-dir' '
+ (cd super &&
+ mkdir deeper &&
+ git submodule add ../submodule deeper/submodule &&
+ (cd deeper/submodule &&
+ git log > ../../expected
+ ) &&
+ (cd .git/modules/deeper/submodule &&
+ git log > ../../../../actual
+ ) &&
+ test_cmp actual expected
+ )
+'
+
+test_expect_success 'submodule update places git-dir in superprojects git-dir' '
+ (cd super &&
+ git commit -m "added submodule"
+ ) &&
+ git clone super super2 &&
+ (cd super2 &&
+ git submodule init deeper/submodule &&
+ git submodule update &&
+ (cd deeper/submodule &&
+ git log > ../../expected
+ ) &&
+ (cd .git/modules/deeper/submodule &&
+ git log > ../../../../actual
+ ) &&
+ test_cmp actual expected
+ )
+'
+
+test_expect_success 'submodule add places git-dir in superprojects git-dir recursive' '
+ (cd super2 &&
+ (cd deeper/submodule &&
+ git submodule add ../submodule subsubmodule &&
+ (cd subsubmodule &&
+ git log > ../../../expected
+ ) &&
+ git commit -m "added subsubmodule" &&
+ git push origin :
+ ) &&
+ (cd .git/modules/deeper/submodule/modules/subsubmodule &&
+ git log > ../../../../../actual
+ ) &&
+ git add deeper/submodule &&
+ git commit -m "update submodule" &&
+ git push origin : &&
+ test_cmp actual expected
+ )
+'
+
+test_expect_success 'submodule update places git-dir in superprojects git-dir recursive' '
+ mkdir super_update_r &&
+ (cd super_update_r &&
+ git init --bare
+ ) &&
+ mkdir subsuper_update_r &&
+ (cd subsuper_update_r &&
+ git init --bare
+ ) &&
+ mkdir subsubsuper_update_r &&
+ (cd subsubsuper_update_r &&
+ git init --bare
+ ) &&
+ git clone subsubsuper_update_r subsubsuper_update_r2 &&
+ (cd subsubsuper_update_r2 &&
+ test_commit "update_subsubsuper" file &&
+ git push origin master
+ ) &&
+ git clone subsuper_update_r subsuper_update_r2 &&
+ (cd subsuper_update_r2 &&
+ test_commit "update_subsuper" file &&
+ git submodule add ../subsubsuper_update_r subsubmodule &&
+ git commit -am "subsubmodule" &&
+ git push origin master
+ ) &&
+ git clone super_update_r super_update_r2 &&
+ (cd super_update_r2 &&
+ test_commit "update_super" file &&
+ git submodule add ../subsuper_update_r submodule &&
+ git commit -am "submodule" &&
+ git push origin master
+ ) &&
+ rm -rf super_update_r2 &&
+ git clone super_update_r super_update_r2 &&
+ (cd super_update_r2 &&
+ git submodule update --init --recursive >actual &&
+ test_i18ngrep "Submodule path .submodule/subsubmodule.: checked out" actual &&
+ (cd submodule/subsubmodule &&
+ git log > ../../expected
+ ) &&
+ (cd .git/modules/submodule/modules/subsubmodule
+ git log > ../../../../../actual
+ )
+ test_cmp actual expected
+ )
+'
+
+test_expect_success 'submodule add properly re-creates deeper level submodules' '
+ (cd super &&
+ git reset --hard master &&
+ rm -rf deeper/ &&
+ git submodule add --force ../submodule deeper/submodule
+ )
+'
+
+test_expect_success 'submodule update properly revives a moved submodule' '
+ (cd super &&
+ H=$(git rev-parse --short HEAD) &&
+ git commit -am "pre move" &&
+ H2=$(git rev-parse --short HEAD) &&
+ git status | sed "s/$H/XXX/" >expect &&
+ H=$(cd submodule2; git rev-parse HEAD) &&
+ git rm --cached submodule2 &&
+ rm -rf submodule2 &&
+ mkdir -p "moved/sub module" &&
+ git update-index --add --cacheinfo 160000 $H "moved/sub module" &&
+ git config -f .gitmodules submodule.submodule2.path "moved/sub module"
+ git commit -am "post move" &&
+ git submodule update &&
+ git status | sed "s/$H2/XXX/" >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success SYMLINKS 'submodule update can handle symbolic links in pwd' '
+ mkdir -p linked/dir &&
+ ln -s linked/dir linkto &&
+ (cd linkto &&
+ git clone "$TRASH_DIRECTORY"/super_update_r2 super &&
+ (cd super &&
+ git submodule update --init --recursive
+ )
+ )
+'
+
+test_expect_success 'submodule update clone shallow submodule' '
+ git clone cloned super3 &&
+ pwd=$(pwd) &&
+ (cd super3 &&
+ sed -e "s#url = ../#url = file://$pwd/#" <.gitmodules >.gitmodules.tmp &&
+ mv -f .gitmodules.tmp .gitmodules &&
+ git submodule update --init --depth=3
+ (cd submodule &&
+ test 1 = $(git log --oneline | wc -l)
+ )
+)
+'
+
+test_expect_success 'submodule update --recursive drops module name before recursing' '
+ (cd super2 &&
+ (cd deeper/submodule/subsubmodule &&
+ git checkout HEAD^
+ ) &&
+ git submodule update --recursive deeper/submodule >actual &&
+ test_i18ngrep "Submodule path .deeper/submodule/subsubmodule.: checked out" actual
+ )
+'
+
+test_expect_success 'submodule update can be run in parallel' '
+ (cd super2 &&
+ GIT_TRACE=$(pwd)/trace.out git submodule update --jobs 7 &&
+ grep "7 tasks" trace.out &&
+ git config submodule.fetchJobs 8 &&
+ GIT_TRACE=$(pwd)/trace.out git submodule update &&
+ grep "8 tasks" trace.out &&
+ GIT_TRACE=$(pwd)/trace.out git submodule update --jobs 9 &&
+ grep "9 tasks" trace.out
+ )
+'
+
+test_expect_success 'git clone passes the parallel jobs config on to submodules' '
+ test_when_finished "rm -rf super4" &&
+ GIT_TRACE=$(pwd)/trace.out git clone --recurse-submodules --jobs 7 . super4 &&
+ grep "7 tasks" trace.out &&
+ rm -rf super4 &&
+ git config --global submodule.fetchJobs 8 &&
+ GIT_TRACE=$(pwd)/trace.out git clone --recurse-submodules . super4 &&
+ grep "8 tasks" trace.out &&
+ rm -rf super4 &&
+ GIT_TRACE=$(pwd)/trace.out git clone --recurse-submodules --jobs 9 . super4 &&
+ grep "9 tasks" trace.out &&
+ rm -rf super4
+'
+
test_done
diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
index db9365b..6ba5daf 100755
--- a/t/t7407-submodule-foreach.sh
+++ b/t/t7407-submodule-foreach.sh
@@ -16,7 +16,7 @@ test_expect_success 'setup a submodule tree' '
echo file > file &&
git add file &&
test_tick &&
- git commit -m upstream
+ git commit -m upstream &&
git clone . super &&
git clone super submodule &&
(
@@ -30,7 +30,7 @@ test_expect_success 'setup a submodule tree' '
submodule.sub2 submodule.foo2 &&
git config -f .gitmodules --rename-section \
submodule.sub3 submodule.foo3 &&
- git add .gitmodules
+ git add .gitmodules &&
test_tick &&
git commit -m "submodules" &&
git submodule init sub1 &&
@@ -77,7 +77,23 @@ test_expect_success 'test basic "submodule foreach" usage' '
git config foo.bar zar &&
git submodule foreach "git config --file \"\$toplevel/.git/config\" foo.bar"
) &&
- test_cmp expect actual
+ test_i18ncmp expect actual
+'
+
+cat >expect <<EOF
+Entering '../sub1'
+$pwd/clone-foo1-../sub1-$sub1sha1
+Entering '../sub3'
+$pwd/clone-foo3-../sub3-$sub3sha1
+EOF
+
+test_expect_success 'test "submodule foreach" from subdirectory' '
+ mkdir clone/sub &&
+ (
+ cd clone/sub &&
+ git submodule foreach "echo \$toplevel-\$name-\$sm_path-\$sha1" >../../actual
+ ) &&
+ test_i18ncmp expect actual
'
test_expect_success 'setup nested submodules' '
@@ -118,19 +134,19 @@ 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 &&
+ test_must_fail git rev-parse --resolve-git-dir sub1/.git &&
+ test_must_fail git rev-parse --resolve-git-dir sub2/.git &&
+ test_must_fail git rev-parse --resolve-git-dir sub3/.git &&
+ test_must_fail git rev-parse --resolve-git-dir 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 rev-parse --resolve-git-dir sub1/.git &&
+ git rev-parse --resolve-git-dir sub2/.git &&
+ git rev-parse --resolve-git-dir sub3/.git &&
+ git rev-parse --resolve-git-dir nested1/.git &&
+ test_must_fail git rev-parse --resolve-git-dir nested1/nested2/.git &&
git submodule foreach "git submodule update --init" &&
- test -d nested1/nested2/.git &&
- test ! -d nested1/nested2/nested3/.git
+ git rev-parse --resolve-git-dir nested1/nested2/.git &&
+ test_must_fail git rev-parse --resolve-git-dir nested1/nested2/nested3/.git
)
'
@@ -138,8 +154,8 @@ 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
+ git rev-parse --resolve-git-dir nested1/nested2/nested3/.git &&
+ git rev-parse --resolve-git-dir nested1/nested2/nested3/submodule/.git
)
'
@@ -158,7 +174,27 @@ test_expect_success 'test messages from "foreach --recursive"' '
cd clone2 &&
git submodule foreach --recursive "true" > ../actual
) &&
- test_cmp expect actual
+ test_i18ncmp expect actual
+'
+
+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" from subdirectory' '
+ (
+ cd clone2 &&
+ mkdir untracked &&
+ cd untracked &&
+ git submodule foreach --recursive >../../actual
+ ) &&
+ test_i18ncmp expect actual
'
cat > expect <<EOF
@@ -183,18 +219,18 @@ 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 &&
+ test_must_fail git rev-parse --resolve-git-dir sub1/.git &&
+ test_must_fail git rev-parse --resolve-git-dir sub2/.git &&
+ test_must_fail git rev-parse --resolve-git-dir sub3/.git &&
+ test_must_fail git rev-parse --resolve-git-dir 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
+ git rev-parse --resolve-git-dir sub1/.git &&
+ git rev-parse --resolve-git-dir sub2/.git &&
+ git rev-parse --resolve-git-dir sub3/.git &&
+ git rev-parse --resolve-git-dir nested1/.git &&
+ git rev-parse --resolve-git-dir nested1/nested2/.git &&
+ git rev-parse --resolve-git-dir nested1/nested2/nested3/.git &&
+ git rev-parse --resolve-git-dir nested1/nested2/nested3/submodule/.git
)
'
@@ -226,16 +262,121 @@ test_expect_success 'test "status --recursive"' '
test_cmp expect actual
'
+cat > expect <<EOF
+ $nested1sha1 nested1 (heads/master)
++$nested2sha1 nested1/nested2 (file2~1)
+ $nested3sha1 nested1/nested2/nested3 (heads/master)
+ $submodulesha1 nested1/nested2/nested3/submodule (heads/master)
+EOF
+
+test_expect_success 'ensure "status --cached --recursive" preserves the --cached flag' '
+ (
+ cd clone3 &&
+ (
+ cd nested1/nested2 &&
+ test_commit file2
+ ) &&
+ git submodule status --cached --recursive -- nested1 > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+nested2sha1=$(git -C clone3/nested1/nested2 rev-parse HEAD)
+
+cat > expect <<EOF
+ $nested1sha1 ../nested1 (heads/master)
++$nested2sha1 ../nested1/nested2 (file2)
+ $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" from sub directory' '
+ (
+ cd clone3 &&
+ mkdir tmp && cd tmp &&
+ 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
+ (
+ cd clone4 &&
+ git rev-parse --resolve-git-dir .git &&
+ git rev-parse --resolve-git-dir sub1/.git &&
+ git rev-parse --resolve-git-dir sub2/.git &&
+ git rev-parse --resolve-git-dir sub3/.git &&
+ git rev-parse --resolve-git-dir nested1/.git &&
+ git rev-parse --resolve-git-dir nested1/nested2/.git &&
+ git rev-parse --resolve-git-dir nested1/nested2/nested3/.git &&
+ git rev-parse --resolve-git-dir nested1/nested2/nested3/submodule/.git
+ )
+'
+
+test_expect_success 'test "update --recursive" with a flag with spaces' '
+ git clone super "common objects" &&
+ git clone super clone5 &&
+ (
+ cd clone5 &&
+ test_must_fail git rev-parse --resolve-git-dir d nested1/.git &&
+ git submodule update --init --recursive --reference="$(dirname "$PWD")/common objects" &&
+ git rev-parse --resolve-git-dir nested1/.git &&
+ git rev-parse --resolve-git-dir nested1/nested2/.git &&
+ git rev-parse --resolve-git-dir nested1/nested2/nested3/.git &&
+ test -f .git/modules/nested1/objects/info/alternates &&
+ test -f .git/modules/nested1/modules/nested2/objects/info/alternates &&
+ test -f .git/modules/nested1/modules/nested2/modules/nested3/objects/info/alternates
+ )
+'
+
+test_expect_success 'use "update --recursive nested1" to checkout all submodules rooted in nested1' '
+ git clone super clone6 &&
+ (
+ cd clone6 &&
+ test_must_fail git rev-parse --resolve-git-dir sub1/.git &&
+ test_must_fail git rev-parse --resolve-git-dir sub2/.git &&
+ test_must_fail git rev-parse --resolve-git-dir sub3/.git &&
+ test_must_fail git rev-parse --resolve-git-dir nested1/.git &&
+ git submodule update --init --recursive -- nested1 &&
+ test_must_fail git rev-parse --resolve-git-dir sub1/.git &&
+ test_must_fail git rev-parse --resolve-git-dir sub2/.git &&
+ test_must_fail git rev-parse --resolve-git-dir sub3/.git &&
+ git rev-parse --resolve-git-dir nested1/.git &&
+ git rev-parse --resolve-git-dir nested1/nested2/.git &&
+ git rev-parse --resolve-git-dir nested1/nested2/nested3/.git &&
+ git rev-parse --resolve-git-dir nested1/nested2/nested3/submodule/.git
+ )
+'
+
+test_expect_success 'command passed to foreach retains notion of stdin' '
+ (
+ cd super &&
+ git submodule foreach echo success >../expected &&
+ yes | git submodule foreach "read y && test \"x\$y\" = xy && echo success" >../actual
+ ) &&
+ test_cmp expected actual
+'
+
+test_expect_success 'command passed to foreach --recursive retains notion of stdin' '
+ (
+ cd clone2 &&
+ git submodule foreach --recursive echo success >../expected &&
+ yes | git submodule foreach --recursive "read y && test \"x\$y\" = xy && echo success" >../actual
+ ) &&
+ test_cmp expected actual
+'
+
+test_expect_success 'multi-argument command passed to foreach is not shell-evaluated twice' '
+ (
+ cd super &&
+ git submodule foreach "echo \\\"quoted\\\"" > ../expected &&
+ git submodule foreach echo \"quoted\" > ../actual
+ ) &&
+ test_cmp expected actual
'
test_done
diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh
index cc16d3f..eaea19b 100755
--- a/t/t7408-submodule-reference.sh
+++ b/t/t7408-submodule-reference.sh
@@ -6,7 +6,7 @@
test_description='test clone --reference'
. ./test-lib.sh
-base_dir=`pwd`
+base_dir=$(pwd)
U=$base_dir/UPLOAD_LOG
@@ -28,7 +28,7 @@ git prune'
cd "$base_dir"
-test_expect_success 'preparing supermodule' \
+test_expect_success 'preparing superproject' \
'test_create_repo super && cd super &&
echo file > file &&
git add file &&
@@ -43,7 +43,7 @@ git commit -m B-super-added'
cd "$base_dir"
test_expect_success 'after add: existence of info/alternates' \
-'test `wc -l <super/sub/.git/objects/info/alternates` = 1'
+'test_line_count = 1 super/.git/modules/sub/objects/info/alternates'
cd "$base_dir"
@@ -55,7 +55,7 @@ diff expected current'
cd "$base_dir"
-test_expect_success 'cloning supermodule' \
+test_expect_success 'cloning superproject' \
'git clone super super-clone'
cd "$base_dir"
@@ -66,7 +66,7 @@ test_expect_success 'update with reference' \
cd "$base_dir"
test_expect_success 'after update: existence of info/alternates' \
-'test `wc -l <super-clone/sub/.git/objects/info/alternates` = 1'
+'test_line_count = 1 super-clone/.git/modules/sub/objects/info/alternates'
cd "$base_dir"
diff --git a/t/t7409-submodule-detached-work-tree.sh b/t/t7409-submodule-detached-work-tree.sh
new file mode 100755
index 0000000..c207171
--- /dev/null
+++ b/t/t7409-submodule-detached-work-tree.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Daniel Graña
+#
+
+test_description='Test submodules on detached working tree
+
+This test verifies that "git submodule" initialization, update and addition works
+on detahced working trees
+'
+
+TEST_NO_CREATE_REPO=1
+. ./test-lib.sh
+
+test_expect_success 'submodule on detached working tree' '
+ git init --bare remote &&
+ test_create_repo bundle1 &&
+ (
+ cd bundle1 &&
+ test_commit "shoot" &&
+ git rev-parse --verify HEAD >../expect
+ ) &&
+ mkdir home &&
+ (
+ cd home &&
+ GIT_WORK_TREE="$(pwd)" &&
+ GIT_DIR="$(pwd)/.dotfiles" &&
+ export GIT_WORK_TREE GIT_DIR &&
+ git clone --bare ../remote .dotfiles &&
+ git submodule add ../bundle1 .vim/bundle/sogood &&
+ test_commit "sogood" &&
+ (
+ unset GIT_WORK_TREE GIT_DIR &&
+ cd .vim/bundle/sogood &&
+ git rev-parse --verify HEAD >actual &&
+ test_cmp ../../../../expect actual
+ ) &&
+ git push origin master
+ ) &&
+ mkdir home2 &&
+ (
+ cd home2 &&
+ git clone --bare ../remote .dotfiles &&
+ GIT_WORK_TREE="$(pwd)" &&
+ GIT_DIR="$(pwd)/.dotfiles" &&
+ export GIT_WORK_TREE GIT_DIR &&
+ git checkout master &&
+ git submodule update --init &&
+ (
+ unset GIT_WORK_TREE GIT_DIR &&
+ cd .vim/bundle/sogood &&
+ git rev-parse --verify HEAD >actual &&
+ test_cmp ../../../../expect actual
+ )
+ )
+'
+
+test_expect_success 'submodule on detached working pointed by core.worktree' '
+ mkdir home3 &&
+ (
+ cd home3 &&
+ GIT_DIR="$(pwd)/.dotfiles" &&
+ export GIT_DIR &&
+ git clone --bare ../remote "$GIT_DIR" &&
+ git config core.bare false &&
+ git config core.worktree .. &&
+ git checkout master &&
+ git submodule add ../bundle1 .vim/bundle/dupe &&
+ test_commit "dupe" &&
+ git push origin master
+ ) &&
+ (
+ cd home &&
+ GIT_DIR="$(pwd)/.dotfiles" &&
+ export GIT_DIR &&
+ git config core.bare false &&
+ git config core.worktree .. &&
+ git pull &&
+ git submodule update --init &&
+ test -f .vim/bundle/dupe/shoot.t
+ )
+'
+
+test_done
diff --git a/t/t7410-submodule-checkout-to.sh b/t/t7410-submodule-checkout-to.sh
new file mode 100755
index 0000000..1acef32
--- /dev/null
+++ b/t/t7410-submodule-checkout-to.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='Combination of submodules and multiple workdirs'
+
+. ./test-lib.sh
+
+base_path=$(pwd -P)
+
+test_expect_success 'setup: make origin' \
+ 'mkdir -p origin/sub && ( cd origin/sub && git init &&
+ echo file1 >file1 &&
+ git add file1 &&
+ git commit -m file1 ) &&
+ mkdir -p origin/main && ( cd origin/main && git init &&
+ git submodule add ../sub &&
+ git commit -m "add sub" ) &&
+ ( cd origin/sub &&
+ echo file1updated >file1 &&
+ git add file1 &&
+ git commit -m "file1 updated" ) &&
+ ( cd origin/main/sub && git pull ) &&
+ ( cd origin/main &&
+ git add sub &&
+ git commit -m "sub updated" )'
+
+test_expect_success 'setup: clone' \
+ 'mkdir clone && ( cd clone &&
+ git clone --recursive "$base_path/origin/main")'
+
+rev1_hash_main=$(git --git-dir=origin/main/.git show --pretty=format:%h -q "HEAD~1")
+rev1_hash_sub=$(git --git-dir=origin/sub/.git show --pretty=format:%h -q "HEAD~1")
+
+test_expect_success 'checkout main' \
+ 'mkdir default_checkout &&
+ (cd clone/main &&
+ git worktree add "$base_path/default_checkout/main" "$rev1_hash_main")'
+
+test_expect_failure 'can see submodule diffs just after checkout' \
+ '(cd default_checkout/main && git diff --submodule master"^!" | grep "file1 updated")'
+
+test_expect_success 'checkout main and initialize independed clones' \
+ 'mkdir fully_cloned_submodule &&
+ (cd clone/main &&
+ git worktree add "$base_path/fully_cloned_submodule/main" "$rev1_hash_main") &&
+ (cd fully_cloned_submodule/main && git submodule update)'
+
+test_expect_success 'can see submodule diffs after independed cloning' \
+ '(cd fully_cloned_submodule/main && git diff --submodule master"^!" | grep "file1 updated")'
+
+test_expect_success 'checkout sub manually' \
+ 'mkdir linked_submodule &&
+ (cd clone/main &&
+ git worktree add "$base_path/linked_submodule/main" "$rev1_hash_main") &&
+ (cd clone/main/sub &&
+ git worktree add "$base_path/linked_submodule/main/sub" "$rev1_hash_sub")'
+
+test_expect_success 'can see submodule diffs after manual checkout of linked submodule' \
+ '(cd linked_submodule/main && git diff --submodule master"^!" | grep "file1 updated")'
+
+test_done
diff --git a/t/t7411-submodule-config.sh b/t/t7411-submodule-config.sh
new file mode 100755
index 0000000..fc97c33
--- /dev/null
+++ b/t/t7411-submodule-config.sh
@@ -0,0 +1,153 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 Heiko Voigt
+#
+
+test_description='Test submodules config cache infrastructure
+
+This test verifies that parsing .gitmodules configurations directly
+from the database and from the worktree works.
+'
+
+TEST_NO_CREATE_REPO=1
+. ./test-lib.sh
+
+test_expect_success 'submodule config cache setup' '
+ mkdir submodule &&
+ (cd submodule &&
+ git init &&
+ echo a >a &&
+ git add . &&
+ git commit -ma
+ ) &&
+ mkdir super &&
+ (cd super &&
+ git init &&
+ git submodule add ../submodule &&
+ git submodule add ../submodule a &&
+ git commit -m "add as submodule and as a" &&
+ git mv a b &&
+ git commit -m "move a to b"
+ )
+'
+
+cat >super/expect <<EOF
+Submodule name: 'a' for path 'a'
+Submodule name: 'a' for path 'b'
+Submodule name: 'submodule' for path 'submodule'
+Submodule name: 'submodule' for path 'submodule'
+EOF
+
+test_expect_success 'test parsing and lookup of submodule config by path' '
+ (cd super &&
+ test-submodule-config \
+ HEAD^ a \
+ HEAD b \
+ HEAD^ submodule \
+ HEAD submodule \
+ >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'test parsing and lookup of submodule config by name' '
+ (cd super &&
+ test-submodule-config --name \
+ HEAD^ a \
+ HEAD a \
+ HEAD^ submodule \
+ HEAD submodule \
+ >actual &&
+ test_cmp expect actual
+ )
+'
+
+cat >super/expect_error <<EOF
+Submodule name: 'a' for path 'b'
+Submodule name: 'submodule' for path 'submodule'
+EOF
+
+test_expect_success 'error in one submodule config lets continue' '
+ (cd super &&
+ cp .gitmodules .gitmodules.bak &&
+ echo " value = \"" >>.gitmodules &&
+ git add .gitmodules &&
+ mv .gitmodules.bak .gitmodules &&
+ git commit -m "add error" &&
+ test-submodule-config \
+ HEAD b \
+ HEAD submodule \
+ >actual &&
+ test_cmp expect_error actual
+ )
+'
+
+cat >super/expect_url <<EOF
+Submodule url: 'git@somewhere.else.net:a.git' for path 'b'
+Submodule url: 'git@somewhere.else.net:submodule.git' for path 'submodule'
+EOF
+
+cat >super/expect_local_path <<EOF
+Submodule name: 'a' for path 'c'
+Submodule name: 'submodule' for path 'submodule'
+EOF
+
+test_expect_success 'reading of local configuration' '
+ (cd super &&
+ old_a=$(git config submodule.a.url) &&
+ old_submodule=$(git config submodule.submodule.url) &&
+ git config submodule.a.url git@somewhere.else.net:a.git &&
+ git config submodule.submodule.url git@somewhere.else.net:submodule.git &&
+ test-submodule-config --url \
+ "" b \
+ "" submodule \
+ >actual &&
+ test_cmp expect_url actual &&
+ git config submodule.a.path c &&
+ test-submodule-config \
+ "" c \
+ "" submodule \
+ >actual &&
+ test_cmp expect_local_path actual &&
+ git config submodule.a.url $old_a &&
+ git config submodule.submodule.url $old_submodule &&
+ git config --unset submodule.a.path c
+ )
+'
+
+cat >super/expect_fetchrecurse_die.err <<EOF
+fatal: bad submodule.submodule.fetchrecursesubmodules argument: blabla
+EOF
+
+test_expect_success 'local error in fetchrecursesubmodule dies early' '
+ (cd super &&
+ git config submodule.submodule.fetchrecursesubmodules blabla &&
+ test_must_fail test-submodule-config \
+ "" b \
+ "" submodule \
+ >actual.out 2>actual.err &&
+ touch expect_fetchrecurse_die.out &&
+ test_cmp expect_fetchrecurse_die.out actual.out &&
+ test_cmp expect_fetchrecurse_die.err actual.err &&
+ git config --unset submodule.submodule.fetchrecursesubmodules
+ )
+'
+
+test_expect_success 'error in history in fetchrecursesubmodule lets continue' '
+ (cd super &&
+ git config -f .gitmodules \
+ submodule.submodule.fetchrecursesubmodules blabla &&
+ git add .gitmodules &&
+ git config --unset -f .gitmodules \
+ submodule.submodule.fetchrecursesubmodules &&
+ git commit -m "add error in fetchrecursesubmodules" &&
+ test-submodule-config \
+ HEAD b \
+ HEAD submodule \
+ >actual &&
+ test_cmp expect_error actual &&
+ git reset --hard HEAD^
+ )
+'
+
+test_done
diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh
index aa9c577..116885a 100755
--- a/t/t7500-commit.sh
+++ b/t/t7500-commit.sh
@@ -10,7 +10,12 @@ Tests for selected commit options.'
. ./test-lib.sh
commit_msg_is () {
- test "`git log --pretty=format:%s%b -1`" = "$1"
+ expect=commit_msg_is.expect
+ actual=commit_msg_is.actual
+
+ printf "%s" "$(git log --pretty=format:%s%b -1)" >"$actual" &&
+ printf "%s" "$1" >"$expect" &&
+ test_i18ncmp "$expect" "$actual"
}
# A sanity check to see if commit is working at all.
@@ -23,13 +28,20 @@ test_expect_success 'a basic commit in an empty tree should succeed' '
test_expect_success 'nonexistent template file should return error' '
echo changes >> foo &&
git add foo &&
- test_must_fail git commit --template "$PWD"/notexist
+ (
+ GIT_EDITOR="echo hello >\"\$1\"" &&
+ export GIT_EDITOR &&
+ 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
+ test_config commit.template "$PWD"/notexist &&
+ (
+ GIT_EDITOR="echo hello >\"\$1\"" &&
+ export GIT_EDITOR &&
+ test_must_fail git commit
+ )
'
# From now on we'll use a template file that exists.
@@ -80,14 +92,13 @@ test_expect_success '-t option should be short for --template' '
test_expect_success 'config-specified template should commit' '
echo "new template" > "$TEMPLATE" &&
- git config commit.template "$TEMPLATE" &&
+ test_config commit.template "$TEMPLATE" &&
echo "more content" >> foo &&
git add foo &&
(
test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
git commit
) &&
- git config --unset commit.template &&
commit_msg_is "new templatecommit message"
'
@@ -110,6 +121,20 @@ test_expect_success 'commit message from file should override template' '
commit_msg_is "standard input msg"
'
+cat >"$TEMPLATE" <<\EOF
+
+
+### template
+
+EOF
+test_expect_success 'commit message from template with whitespace issue' '
+ echo "content galore" >>foo &&
+ git add foo &&
+ GIT_EDITOR="$TEST_DIRECTORY"/t7500/add-whitespaced-content git commit \
+ --template "$TEMPLATE" &&
+ commit_msg_is "commit message"
+'
+
test_expect_success 'using alternate GIT_INDEX_FILE (1)' '
cp .git/index saved-index &&
@@ -198,7 +223,8 @@ test_expect_success 'Commit without message is allowed with --allow-empty-messag
git add foo &&
>empty &&
git commit --allow-empty-message <empty &&
- commit_msg_is ""
+ commit_msg_is "" &&
+ git tag empty-message-commit
'
test_expect_success 'Commit without message is no-no without --allow-empty-message' '
@@ -215,4 +241,92 @@ test_expect_success 'Commit a message with --allow-empty-message' '
commit_msg_is "hello there"
'
+test_expect_success 'commit -C empty respects --allow-empty-message' '
+ echo more >>foo &&
+ git add foo &&
+ test_must_fail git commit -C empty-message-commit &&
+ git commit -C empty-message-commit --allow-empty-message &&
+ commit_msg_is ""
+'
+
+commit_for_rebase_autosquash_setup () {
+ echo "first content line" >>foo &&
+ git add foo &&
+ cat >log <<EOF &&
+target message subject line
+
+target message body line 1
+target message body line 2
+EOF
+ git commit -F log &&
+ echo "second content line" >>foo &&
+ git add foo &&
+ git commit -m "intermediate commit" &&
+ echo "third content line" >>foo &&
+ git add foo
+}
+
+test_expect_success 'commit --fixup provides correct one-line commit message' '
+ commit_for_rebase_autosquash_setup &&
+ git commit --fixup HEAD~1 &&
+ commit_msg_is "fixup! target message subject line"
+'
+
+test_expect_success 'commit --squash works with -F' '
+ commit_for_rebase_autosquash_setup &&
+ echo "log message from file" >msgfile &&
+ git commit --squash HEAD~1 -F msgfile &&
+ commit_msg_is "squash! target message subject linelog message from file"
+'
+
+test_expect_success 'commit --squash works with -m' '
+ commit_for_rebase_autosquash_setup &&
+ git commit --squash HEAD~1 -m "foo bar\nbaz" &&
+ commit_msg_is "squash! target message subject linefoo bar\nbaz"
+'
+
+test_expect_success 'commit --squash works with -C' '
+ commit_for_rebase_autosquash_setup &&
+ git commit --squash HEAD~1 -C HEAD &&
+ commit_msg_is "squash! target message subject lineintermediate commit"
+'
+
+test_expect_success 'commit --squash works with -c' '
+ commit_for_rebase_autosquash_setup &&
+ test_set_editor "$TEST_DIRECTORY"/t7500/edit-content &&
+ git commit --squash HEAD~1 -c HEAD &&
+ commit_msg_is "squash! target message subject lineedited commit"
+'
+
+test_expect_success 'commit --squash works with -C for same commit' '
+ commit_for_rebase_autosquash_setup &&
+ git commit --squash HEAD -C HEAD &&
+ commit_msg_is "squash! intermediate commit"
+'
+
+test_expect_success 'commit --squash works with -c for same commit' '
+ commit_for_rebase_autosquash_setup &&
+ test_set_editor "$TEST_DIRECTORY"/t7500/edit-content &&
+ git commit --squash HEAD -c HEAD &&
+ commit_msg_is "squash! edited commit"
+'
+
+test_expect_success 'commit --squash works with editor' '
+ commit_for_rebase_autosquash_setup &&
+ test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+ git commit --squash HEAD~1 &&
+ commit_msg_is "squash! target message subject linecommit message"
+'
+
+test_expect_success 'invalid message options when using --fixup' '
+ echo changes >>foo &&
+ echo "message" >log &&
+ git add foo &&
+ test_must_fail git commit --fixup HEAD~1 --squash HEAD~2 &&
+ test_must_fail git commit --fixup HEAD~1 -C HEAD~2 &&
+ test_must_fail git commit --fixup HEAD~1 -c HEAD~2 &&
+ test_must_fail git commit --fixup HEAD~1 -m "cmdline message" &&
+ test_must_fail git commit --fixup HEAD~1 -F log
+'
+
test_done
diff --git a/t/t7500/add-content-and-comment b/t/t7500/add-content-and-comment
new file mode 100755
index 0000000..c4dccff
--- /dev/null
+++ b/t/t7500/add-content-and-comment
@@ -0,0 +1,5 @@
+#!/bin/sh
+echo "commit message" >> "$1"
+echo "# comment" >> "$1"
+exit 0
+
diff --git a/t/t7500/add-whitespaced-content b/t/t7500/add-whitespaced-content
new file mode 100755
index 0000000..ccf07c6
--- /dev/null
+++ b/t/t7500/add-whitespaced-content
@@ -0,0 +1,8 @@
+#!/bin/sh
+sed -e 's/|$//' >>"$1" <<\EOF
+
+ |
+commit message |
+
+EOF
+exit 0
diff --git a/t/t7500/edit-content b/t/t7500/edit-content
new file mode 100755
index 0000000..08db9fd
--- /dev/null
+++ b/t/t7500/edit-content
@@ -0,0 +1,4 @@
+#!/bin/sh
+sed -e "s/intermediate/edited/g" <"$1" >"$1-"
+mv "$1-" "$1"
+exit 0
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 8297cb4..d84897a 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -8,164 +8,301 @@
test_description='git commit'
. ./test-lib.sh
+. "$TEST_DIRECTORY/diff-lib.sh"
+
+author='The Real Author <someguy@his.email.org>'
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 'initial status' '
+ echo bongo bongo >file &&
+ git add file &&
+ git status >actual &&
+ test_i18ngrep "Initial commit" actual
+'
-test_expect_success \
- "amend commit" \
- "EDITOR=./editor git commit --amend"
+test_expect_success 'fail initial amend' '
+ test_must_fail 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 'setup: initial commit' '
+ git commit -m initial
+'
-test_expect_success \
- "using message from other commit" \
- "git commit -C HEAD^ ."
+test_expect_success '-m and -F do not mix' '
+ git checkout HEAD file && echo >>file && git add file &&
+ test_must_fail git commit -m foo -m bar -F file
+'
-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 '-m and -C do not mix' '
+ git checkout HEAD file && echo >>file && git add file &&
+ test_must_fail git commit -C HEAD -m illegal
+'
+
+test_expect_success 'paths and -a do not mix' '
+ echo King of the bongo >file &&
+ test_must_fail git commit -m foo -a file
+'
+
+test_expect_success PERL 'can use paths with --interactive' '
+ echo bong-o-bong >file &&
+ # 2: update, 1:st path, that is all, 7: quit
+ ( echo 2; echo 1; echo; echo 7 ) |
+ git commit -m foo --interactive file &&
+ git reset --hard HEAD^
+'
+
+test_expect_success 'using invalid commit with -C' '
+ test_must_fail git commit --allow-empty -C bogus
+'
+
+test_expect_success 'nothing to commit' '
+ git reset --hard &&
+ test_must_fail git commit -m initial
+'
+
+test_expect_success '--dry-run fails with nothing to commit' '
+ test_must_fail git commit -m initial --dry-run
+'
+
+test_expect_success '--short fails with nothing to commit' '
+ test_must_fail git commit -m initial --short
+'
+
+test_expect_success '--porcelain fails with nothing to commit' '
+ test_must_fail git commit -m initial --porcelain
+'
+
+test_expect_success '--long fails with nothing to commit' '
+ test_must_fail git commit -m initial --long
+'
+
+test_expect_success 'setup: non-initial commit' '
+ echo bongo bongo bongo >file &&
+ git commit -m next -a
+'
+
+test_expect_success '--dry-run with stuff to commit returns ok' '
+ echo bongo bongo bongo >>file &&
+ git commit -m next -a --dry-run
+'
+
+test_expect_failure '--short with stuff to commit returns ok' '
+ echo bongo bongo bongo >>file &&
+ git commit -m next -a --short
+'
+
+test_expect_failure '--porcelain with stuff to commit returns ok' '
+ echo bongo bongo bongo >>file &&
+ git commit -m next -a --porcelain
+'
+
+test_expect_success '--long with stuff to commit returns ok' '
+ echo bongo bongo bongo >>file &&
+ git commit -m next -a --long
+'
+
+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
+'
+
+test_expect_success 'empty commit message' '
+ # Empty except stray tabs and spaces on a few lines.
+ sed -e "s/@//g" >msg <<-\EOF &&
+ @ @
+ @@
+ @ @
+ @Signed-off-by: hula@
+ EOF
+ test_must_fail git commit -F msg -a
+'
+
+test_expect_success 'template "emptyness" check does not kick in with -F' '
+ git checkout HEAD file && echo >>file && git add file &&
+ git commit -t file -F file
+'
+
+test_expect_success 'template "emptyness" check' '
+ git checkout HEAD file && echo >>file && git add file &&
+ test_must_fail git commit -t file 2>err &&
+ test_i18ngrep "did not edit" err
+'
+
+test_expect_success 'setup: commit message from file' '
+ git checkout HEAD file && echo >>file && git add file &&
+ echo this is the commit message, coming from a file >msg &&
+ git commit -F msg -a
+'
+
+test_expect_success 'amend commit' '
+ cat >editor <<-\EOF &&
+ #!/bin/sh
+ sed -e "s/a file/an amend commit/g" < "$1" > "$1-"
+ mv "$1-" "$1"
+ EOF
+ chmod 755 editor &&
+ EDITOR=./editor git commit --amend
+'
+
+test_expect_success 'amend --only ignores staged contents' '
+ cp file file.expect &&
+ echo changed >file &&
+ git add file &&
+ git commit --no-edit --amend --only &&
+ git cat-file blob HEAD:file >file.actual &&
+ test_cmp file.expect file.actual &&
+ git diff --exit-code
+'
+
+test_expect_success 'set up editor' '
+ cat >editor <<-\EOF &&
+ #!/bin/sh
+ sed -e "s/unamended/amended/g" <"$1" >"$1-"
+ mv "$1-" "$1"
+ EOF
+ chmod 755 editor
+'
+
+test_expect_success 'amend without launching editor' '
+ echo unamended >expect &&
+ git commit --allow-empty -m "unamended" &&
+ echo needs more bongo >file &&
+ git add file &&
+ EDITOR=./editor git commit --no-edit --amend &&
+ git diff --exit-code HEAD -- file &&
+ git diff-tree -s --format=%s HEAD >msg &&
+ test_cmp expect msg
+'
+
+test_expect_success '--amend --edit' '
+ echo amended >expect &&
+ git commit --allow-empty -m "unamended" &&
+ echo bongo again >file &&
+ git add file &&
+ EDITOR=./editor git commit --edit --amend &&
+ git diff-tree -s --format=%s HEAD >msg &&
+ test_cmp expect msg
+'
+
+test_expect_success '--amend --edit of empty message' '
+ cat >replace <<-\EOF &&
+ #!/bin/sh
+ echo "amended" >"$1"
+ EOF
+ chmod 755 replace &&
+ git commit --allow-empty --allow-empty-message -m "" &&
+ echo more bongo >file &&
+ git add file &&
+ EDITOR=./replace git commit --edit --amend &&
+ git diff-tree -s --format=%s HEAD >msg &&
+ ./replace expect &&
+ test_cmp expect msg
+'
+
+test_expect_success '--amend to set message to empty' '
+ echo bata >file &&
+ git add file &&
+ git commit -m "unamended" &&
+ git commit --amend --allow-empty-message -m "" &&
+ git diff-tree -s --format=%s HEAD >msg &&
+ echo "" >expect &&
+ test_cmp expect msg
+'
+
+test_expect_success '--amend to set empty message needs --allow-empty-message' '
+ echo conga >file &&
+ git add file &&
+ git commit -m "unamended" &&
+ test_must_fail git commit --amend -m "" &&
+ git diff-tree -s --format=%s HEAD >msg &&
+ echo "unamended" >expect &&
+ test_cmp expect msg
+'
+
+test_expect_success '-m --edit' '
+ echo amended >expect &&
+ git commit --allow-empty -m buffer &&
+ echo bongo bongo >file &&
+ git add file &&
+ EDITOR=./editor git commit -m unamended --edit &&
+ git diff-tree -s --format=%s HEAD >msg &&
+ test_cmp expect msg
+'
+
+test_expect_success '-m and -F do not mix' '
+ 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^ .
+'
+
+test_expect_success 'editing message from other commit' '
+ cat >editor <<-\EOF &&
+ #!/bin/sh
+ sed -e "s/amend/older/g" < "$1" > "$1-"
+ mv "$1-" "$1"
+ EOF
+ chmod 755 editor &&
+ 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 &&
+ grep Rubber.Duck output
+'
+
+test_expect_success PERL 'interactive add' '
+ echo 7 |
+ git commit --interactive |
+ grep "What now"
+'
+
+test_expect_success PERL "commit --interactive doesn't change index if editor aborts" '
+ echo zoo >file &&
+ test_must_fail git diff --exit-code >diff1 &&
+ (echo u ; echo "*" ; echo q) |
+ (
+ EDITOR=: &&
+ export EDITOR &&
+ test_must_fail git commit --interactive
+ ) &&
+ git diff >diff2 &&
+ compare_diff_patch diff1 diff2
+'
+
+test_expect_success 'editor not invoked if -F is given' '
+ cat >editor <<-\EOF &&
+ #!/bin/sh
+ sed -e s/good/bad/g <"$1" >"$1-"
+ mv "$1-" "$1"
+ EOF
+ chmod 755 editor &&
+
+ echo A good commit message. >msg &&
+ echo moo >file &&
+
+ EDITOR=./editor git commit -a -F msg &&
+ git show -s --pretty=format:%s >subject &&
+ grep -q good subject &&
+
+ echo quack >file &&
+ echo Another good message. |
+ EDITOR=./editor git commit -a -F - &&
+ git show -s --pretty=format:%s >subject &&
+ grep -q good subject
+'
test_expect_success 'partial commit that involves removal (1)' '
@@ -199,7 +336,6 @@ test_expect_success 'partial commit that involves removal (3)' '
'
-author="The Real Author <someguy@his.email.org>"
test_expect_success 'amend commit to fix author' '
oldtick=$GIT_AUTHOR_DATE &&
@@ -230,6 +366,23 @@ test_expect_success 'amend commit to fix date' '
'
+test_expect_success 'commit mentions forced date in output' '
+ git commit --amend --date=2010-01-02T03:04:05 >output &&
+ grep "Date: *Sat Jan 2 03:04:05 2010" output
+'
+
+test_expect_success 'commit complains about completely bogus dates' '
+ test_must_fail git commit --amend --date=seventeen
+'
+
+test_expect_success 'commit --date allows approxidate' '
+ git commit --amend \
+ --date="midnight the 12th of october, anno domini 1979" &&
+ echo "Fri Oct 12 00:00:00 1979 +0000" >expect &&
+ git log -1 --format=%ad >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'sign off (1)' '
echo 1 >positive &&
@@ -324,7 +477,6 @@ test_expect_success 'multiple -m' '
'
-author="The Real Author <someguy@his.email.org>"
test_expect_success 'amend commit to fix author' '
oldtick=$GIT_AUTHOR_DATE &&
@@ -351,15 +503,8 @@ test_expect_success 'git commit <file> with dirty index' '
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
+ git reset --hard &&
+ test_must_fail git commit -m empty
'
@@ -437,4 +582,49 @@ test_expect_success 'amend can copy notes' '
'
+test_expect_success 'commit a file whose name is a dash' '
+ git reset --hard &&
+ for i in 1 2 3 4 5
+ do
+ echo $i
+ done >./- &&
+ git add ./- &&
+ test_tick &&
+ git commit -m "add dash" >output </dev/null &&
+ test_i18ngrep " changed, 5 insertions" output
+'
+
+test_expect_success '--only works on to-be-born branch' '
+ # This test relies on having something in the index, as it
+ # would not otherwise actually prove much. So check this.
+ test -n "$(git ls-files)" &&
+ git checkout --orphan orphan &&
+ echo foo >newfile &&
+ git add newfile &&
+ git commit --only newfile -m"--only on unborn branch" &&
+ echo newfile >expected &&
+ git ls-tree -r --name-only HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '--dry-run with conflicts fixed from a merge' '
+ # setup two branches with conflicting information
+ # in the same file, resolve the conflict,
+ # call commit with --dry-run
+ echo "Initial contents, unimportant" >test-file &&
+ git add test-file &&
+ git commit -m "Initial commit" &&
+ echo "commit-1-state" >test-file &&
+ git commit -m "commit 1" -i test-file &&
+ git tag commit-1 &&
+ git checkout -b branch-2 HEAD^1 &&
+ echo "commit-2-state" >test-file &&
+ git commit -m "commit 2" -i test-file &&
+ ! $(git merge --no-commit commit-1) &&
+ echo "commit-2-state" >test-file &&
+ git add test-file &&
+ git commit --dry-run &&
+ git commit -m "conflicts fixed from merge."
+'
+
test_done
diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh
index ac2e187..725687d 100755
--- a/t/t7502-commit.sh
+++ b/t/t7502-commit.sh
@@ -4,6 +4,15 @@ test_description='git commit porcelain-ish'
. ./test-lib.sh
+commit_msg_is () {
+ expect=commit_msg_is.expect
+ actual=commit_msg_is.actual
+
+ printf "%s" "$(git log --pretty=format:%s%b -1)" >$actual &&
+ printf "%s" "$1" >$expect &&
+ test_i18ncmp $expect $actual
+}
+
# Arguments: [<prefix] [<commit message>] [<commit options>]
check_summary_oneline() {
test_tick &&
@@ -22,7 +31,7 @@ check_summary_oneline() {
SUMMARY_POSTFIX="$(git log -1 --pretty='format:%h')"
echo "[$SUMMARY_PREFIX $SUMMARY_POSTFIX] $2" >exp &&
- test_cmp exp act
+ test_i18ncmp exp act
}
test_expect_success 'output summary format' '
@@ -32,7 +41,10 @@ test_expect_success 'output summary format' '
check_summary_oneline "root-commit" "initial" &&
echo change >>file1 &&
- git add file1 &&
+ git add file1
+'
+
+test_expect_success 'output summary format: root-commit' '
check_summary_oneline "" "a change"
'
@@ -159,23 +171,30 @@ test_expect_success 'verbose' '
test_expect_success 'verbose respects diff config' '
- git config color.diff always &&
+ test_config color.diff always &&
git status -v >actual &&
- grep "\[1mdiff --git" actual &&
- git config --unset color.diff
+ grep "\[1mdiff --git" actual
'
-test_expect_success 'cleanup commit messages (verbatim,-t)' '
+mesg_with_comment_and_newlines='
+# text
+
+'
+
+test_expect_success 'prepare file with comment line and trailing newlines' '
+ printf "%s" "$mesg_with_comment_and_newlines" >expect
+'
+
+test_expect_success 'cleanup commit messages (verbatim option,-t)' '
echo >>negative &&
- { echo;echo "# text";echo; } >expect &&
- git commit --cleanup=verbatim -t expect -a &&
- git cat-file -p HEAD |sed -e "1,/^\$/d" |head -n 3 >actual &&
+ git commit --cleanup=verbatim --no-status -t expect -a &&
+ git cat-file -p HEAD |sed -e "1,/^\$/d" >actual &&
test_cmp expect actual
'
-test_expect_success 'cleanup commit messages (verbatim,-F)' '
+test_expect_success 'cleanup commit messages (verbatim option,-F)' '
echo >>negative &&
git commit --cleanup=verbatim -F expect -a &&
@@ -184,16 +203,16 @@ test_expect_success 'cleanup commit messages (verbatim,-F)' '
'
-test_expect_success 'cleanup commit messages (verbatim,-m)' '
+test_expect_success 'cleanup commit messages (verbatim option,-m)' '
echo >>negative &&
- git commit --cleanup=verbatim -m "$(cat expect)" -a &&
+ git commit --cleanup=verbatim -m "$mesg_with_comment_and_newlines" -a &&
git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
test_cmp expect actual
'
-test_expect_success 'cleanup commit messages (whitespace,-F)' '
+test_expect_success 'cleanup commit messages (whitespace option,-F)' '
echo >>negative &&
{ echo;echo "# text";echo; } >text &&
@@ -204,7 +223,45 @@ test_expect_success 'cleanup commit messages (whitespace,-F)' '
'
-test_expect_success 'cleanup commit messages (strip,-F)' '
+test_expect_success 'cleanup commit messages (scissors option,-F,-e)' '
+
+ echo >>negative &&
+ cat >text <<EOF &&
+
+# to be kept
+
+ # ------------------------ >8 ------------------------
+# to be kept, too
+# ------------------------ >8 ------------------------
+to be removed
+# ------------------------ >8 ------------------------
+to be removed, too
+EOF
+
+ cat >expect <<EOF &&
+# to be kept
+
+ # ------------------------ >8 ------------------------
+# to be kept, too
+EOF
+ git commit --cleanup=scissors -e -F text -a &&
+ git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cleanup commit messages (scissors option,-F,-e, scissors on first line)' '
+
+ echo >>negative &&
+ cat >text <<EOF &&
+# ------------------------ >8 ------------------------
+to be removed
+EOF
+ git commit --cleanup=scissors -e -F text -a --allow-empty-message &&
+ git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'cleanup commit messages (strip option,-F)' '
echo >>negative &&
{ echo;echo "# text";echo sample;echo; } >text &&
@@ -215,60 +272,156 @@ test_expect_success 'cleanup commit messages (strip,-F)' '
'
+test_expect_success 'cleanup commit messages (strip option,-F,-e)' '
+
+ echo >>negative &&
+ { echo;echo sample;echo; } >text &&
+ git commit -e -F text -a &&
+ head -n 4 .git/COMMIT_EDITMSG >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)' '
+test_expect_success 'cleanup commit messages (strip option,-F,-e): output' '
+ test_i18ncmp expect actual
+'
- echo >>negative &&
- { echo;echo sample;echo; } >text &&
- git commit -e -F text -a &&
- head -n 4 .git/COMMIT_EDITMSG >actual &&
- test_cmp expect actual
+test_expect_success 'cleanup commit message (fail on invalid cleanup mode option)' '
+ test_must_fail git commit --cleanup=non-existent
+'
+test_expect_success 'cleanup commit message (fail on invalid cleanup mode configuration)' '
+ test_must_fail git -c commit.cleanup=non-existent commit
'
-echo "#
-# Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
-#" >> expect
+test_expect_success 'cleanup commit message (no config and no option uses default)' '
+ echo content >>file &&
+ git add file &&
+ (
+ test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment &&
+ git commit --no-status
+ ) &&
+ commit_msg_is "commit message"
+'
-test_expect_success 'author different from committer' '
+test_expect_success 'cleanup commit message (option overrides default)' '
+ echo content >>file &&
+ git add file &&
+ (
+ test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment &&
+ git commit --cleanup=whitespace --no-status
+ ) &&
+ commit_msg_is "commit message # comment"
+'
+
+test_expect_success 'cleanup commit message (config overrides default)' '
+ echo content >>file &&
+ git add file &&
+ (
+ test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment &&
+ git -c commit.cleanup=whitespace commit --no-status
+ ) &&
+ commit_msg_is "commit message # comment"
+'
+test_expect_success 'cleanup commit message (option overrides config)' '
+ echo content >>file &&
+ git add file &&
+ (
+ test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment &&
+ git -c commit.cleanup=whitespace commit --cleanup=default
+ ) &&
+ commit_msg_is "commit message"
+'
+
+test_expect_success 'cleanup commit message (default, -m)' '
+ echo content >>file &&
+ git add file &&
+ git commit -m "message #comment " &&
+ commit_msg_is "message #comment"
+'
+
+test_expect_success 'cleanup commit message (whitespace option, -m)' '
+ echo content >>file &&
+ git add file &&
+ git commit --cleanup=whitespace --no-status -m "message #comment " &&
+ commit_msg_is "message #comment"
+'
+
+test_expect_success 'cleanup commit message (whitespace config, -m)' '
+ echo content >>file &&
+ git add file &&
+ git -c commit.cleanup=whitespace commit --no-status -m "message #comment " &&
+ commit_msg_is "message #comment"
+'
+
+test_expect_success 'message shows author when it is not equal to committer' '
echo >>negative &&
- git commit -e -m "sample"
- head -n 7 .git/COMMIT_EDITMSG >actual &&
- test_cmp expect actual
+ git commit -e -m "sample" -a &&
+ test_i18ngrep \
+ "^# Author: *A U Thor <author@example.com>\$" \
+ .git/COMMIT_EDITMSG
'
-mv expect expect.tmp
-sed '$d' < expect.tmp > expect
-rm -f expect.tmp
-echo "# Committer:
-#" >> expect
+test_expect_success 'message shows date when it is explicitly set' '
+ git commit --allow-empty -e -m foo --date="2010-01-02T03:04:05" &&
+ test_i18ngrep \
+ "^# Date: *Sat Jan 2 03:04:05 2010 +0000" \
+ .git/COMMIT_EDITMSG
+'
-test_expect_success 'committer is automatic' '
+test_expect_success AUTOIDENT 'message shows committer when it 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"
+ sane_unset GIT_COMMITTER_EMAIL &&
+ sane_unset GIT_COMMITTER_NAME &&
+ git commit -e -m "sample" -a
) &&
- head -n 8 .git/COMMIT_EDITMSG | \
- sed "s/^# Committer: .*/# Committer:/" >actual &&
- test_cmp expect actual
+ # the ident is calculated from the system, so we cannot
+ # check the actual value, only that it is there
+ test_i18ngrep "^# Committer: " .git/COMMIT_EDITMSG
'
-pwd=`pwd`
-cat >> .git/FAKE_EDITOR << EOF
-#! /bin/sh
-echo editor started > "$pwd/.git/result"
+write_script .git/FAKE_EDITOR <<EOF
+echo editor started > "$(pwd)/.git/result"
exit 0
EOF
-chmod +x .git/FAKE_EDITOR
+
+test_expect_success !AUTOIDENT 'do not fire editor when committer is bogus' '
+ >.git/result &&
+ >expect &&
+
+ echo >>negative &&
+ (
+ sane_unset GIT_COMMITTER_EMAIL &&
+ sane_unset GIT_COMMITTER_NAME &&
+ GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" &&
+ export GIT_EDITOR &&
+ test_must_fail git commit -e -m sample -a
+ ) &&
+ test_cmp expect .git/result
+'
+
+test_expect_success 'do not fire editor if -m <msg> was given' '
+ echo tick >file &&
+ git add file &&
+ echo "editor not started" >.git/result &&
+ (GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" git commit -m tick) &&
+ test "$(cat .git/result)" = "editor not started"
+'
+
+test_expect_success 'do not fire editor if -m "" was given' '
+ echo tock >file &&
+ git add file &&
+ echo "editor not started" >.git/result &&
+ (GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" \
+ git commit -m "" --allow-empty-message) &&
+ test "$(cat .git/result)" = "editor not started"
+'
test_expect_success 'do not fire editor in the presence of conflicts' '
@@ -289,16 +442,14 @@ test_expect_success 'do not fire editor in the presence of conflicts' '
test_must_fail git cherry-pick -n master &&
echo "editor not started" >.git/result &&
(
- GIT_EDITOR="$(pwd)/.git/FAKE_EDITOR" &&
+ GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" &&
export GIT_EDITOR &&
test_must_fail git commit
) &&
test "$(cat .git/result)" = "editor not started"
'
-pwd=`pwd`
-cat >.git/FAKE_EDITOR <<EOF
-#! $SHELL_PATH
+write_script .git/FAKE_EDITOR <<EOF
# kill -TERM command added below.
EOF
@@ -331,109 +482,95 @@ test_expect_success 'A single-liner subject with a token plus colon is not a foo
git reset --hard &&
git commit -s -m "hello: kitty" --allow-empty &&
git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
- test $(wc -l <actual) = 3
+ test_line_count = 3 actual
+
+'
+
+test_expect_success 'commit -s places sob on third line after two empty lines' '
+ git commit -s --allow-empty --allow-empty-message &&
+ cat <<-EOF >expect &&
+
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+
+ EOF
+ sed -e "/^#/d" -e "s/^:.*//" .git/COMMIT_EDITMSG >actual &&
+ test_cmp expect actual
'
-cat >.git/FAKE_EDITOR <<EOF
-#!$SHELL_PATH
-mv "\$1" "\$1.orig"
+write_script .git/FAKE_EDITOR <<\EOF
+mv "$1" "$1.orig"
(
echo message
- cat "\$1.orig"
-) >"\$1"
+ cat "$1.orig"
+) >"$1"
EOF
echo '## Custom template' >template
-clear_config () {
- (
- git config --unset-all "$1"
- case $? in
- 0|5) exit 0 ;;
- *) exit 1 ;;
- esac
- )
-}
-
try_commit () {
git reset --hard &&
echo >>negative &&
GIT_EDITOR=.git/FAKE_EDITOR git commit -a $* $use_template &&
case "$use_template" in
'')
- ! grep "^## Custom template" .git/COMMIT_EDITMSG ;;
+ test_i18ngrep ! "^## Custom template" .git/COMMIT_EDITMSG ;;
*)
- grep "^## Custom template" .git/COMMIT_EDITMSG ;;
+ test_i18ngrep "^## 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_i18ngrep "^# 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_i18ngrep "^# 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
+ try_commit --no-status &&
+ test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
'
test_expect_success 'commit with commit.status = yes' '
- clear_config commit.status &&
- git config commit.status yes &&
+ test_config commit.status yes &&
try_commit "" &&
- grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
'
test_expect_success 'commit with commit.status = no' '
- clear_config commit.status &&
- git config commit.status no &&
+ test_config commit.status no &&
try_commit "" &&
- ! grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
'
test_expect_success 'commit --status with commit.status = yes' '
- clear_config commit.status &&
- git config commit.status yes &&
+ test_config commit.status yes &&
try_commit --status &&
- grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
'
test_expect_success 'commit --no-status with commit.status = yes' '
- clear_config commit.status &&
- git config commit.status yes &&
+ test_config commit.status yes &&
try_commit --no-status &&
- ! grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
'
test_expect_success 'commit --status with commit.status = no' '
- clear_config commit.status &&
- git config commit.status no &&
+ test_config commit.status no &&
try_commit --status &&
- grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
'
test_expect_success 'commit --no-status with commit.status = no' '
- clear_config commit.status &&
- git config commit.status no &&
+ test_config commit.status no &&
try_commit --no-status &&
- ! grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
'
}
@@ -444,4 +581,36 @@ use_template="-t template"
try_commit_status_combo
+test_expect_success 'commit --status with custom comment character' '
+ test_config core.commentchar ";" &&
+ try_commit --status &&
+ test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG
+'
+
+test_expect_success 'switch core.commentchar' '
+ test_commit "#foo" foo &&
+ GIT_EDITOR=.git/FAKE_EDITOR git -c core.commentChar=auto commit --amend &&
+ test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG
+'
+
+test_expect_success 'switch core.commentchar but out of options' '
+ cat >text <<\EOF &&
+# 1
+; 2
+@ 3
+! 4
+$ 5
+% 6
+^ 7
+& 8
+| 9
+: 10
+EOF
+ git commit --amend -F text &&
+ (
+ test_set_editor .git/FAKE_EDITOR &&
+ test_must_fail git -c core.commentChar=auto commit --amend
+ )
+'
+
test_done
diff --git a/t/t7503-pre-commit-hook.sh b/t/t7503-pre-commit-hook.sh
index 8528f64..984889b 100755
--- a/t/t7503-pre-commit-hook.sh
+++ b/t/t7503-pre-commit-hook.sh
@@ -84,5 +84,56 @@ test_expect_success POSIXPERM '--no-verify with non-executable hook' '
git commit --no-verify -m "more content"
'
+chmod +x "$HOOK"
+
+# a hook that checks $GIT_PREFIX and succeeds inside the
+# success/ subdirectory only
+cat > "$HOOK" <<EOF
+#!/bin/sh
+test \$GIT_PREFIX = success/
+EOF
+
+test_expect_success 'with hook requiring GIT_PREFIX' '
+
+ echo "more content" >> file &&
+ git add file &&
+ mkdir success &&
+ (
+ cd success &&
+ git commit -m "hook requires GIT_PREFIX = success/"
+ ) &&
+ rmdir success
+'
+
+test_expect_success 'with failing hook requiring GIT_PREFIX' '
+
+ echo "more content" >> file &&
+ git add file &&
+ mkdir fail &&
+ (
+ cd fail &&
+ test_must_fail git commit -m "hook must fail"
+ ) &&
+ rmdir fail &&
+ git checkout -- file
+'
+
+test_expect_success 'check the author in hook' '
+ write_script "$HOOK" <<-\EOF &&
+ test "$GIT_AUTHOR_NAME" = "New Author" &&
+ test "$GIT_AUTHOR_EMAIL" = "newauthor@example.com"
+ EOF
+ test_must_fail git commit --allow-empty -m "by a.u.thor" &&
+ (
+ GIT_AUTHOR_NAME="New Author" &&
+ GIT_AUTHOR_EMAIL="newauthor@example.com" &&
+ export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL &&
+ git commit --allow-empty -m "by new.author via env" &&
+ git show -s
+ ) &&
+ git commit --author="New Author <newauthor@example.com>" \
+ --allow-empty -m "by new.author via command line" &&
+ git show -s
+'
test_done
diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh
index 1f53ea8..8728db6 100755
--- a/t/t7504-commit-msg-hook.sh
+++ b/t/t7504-commit-msg-hook.sh
@@ -179,7 +179,7 @@ EOF
chmod +x "$HOOK"
commit_msg_is () {
- test "`git log --pretty=format:%s%b -1`" = "$1"
+ test "$(git log --pretty=format:%s%b -1)" = "$1"
}
test_expect_success 'hook edits commit message' '
diff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh
index ff18962..b13f729 100755
--- a/t/t7505-prepare-commit-msg-hook.sh
+++ b/t/t7505-prepare-commit-msg-hook.sh
@@ -53,7 +53,7 @@ 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 "$(git log -1 --pretty=format:%s)" = "message (no editor)"
'
@@ -62,7 +62,7 @@ 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 "$(git log -1 --pretty=format:%s)" = message
'
@@ -71,7 +71,7 @@ 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 "$(git log -1 --pretty=format:%s)" = template
'
@@ -80,7 +80,7 @@ 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 "$(git log -1 --pretty=format:%s)" = "message (no editor)"
'
@@ -89,17 +89,17 @@ 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 "$(git log -1 --pretty=format:%s)" = message
'
test_expect_success 'with hook (-C)' '
- head=`git rev-parse HEAD` &&
+ 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 "$(git log -1 --pretty=format:%s)" = "$head (no editor)"
'
@@ -108,30 +108,54 @@ 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 "$(git log -1 --pretty=format:%s)" = default
'
test_expect_success 'with hook (--amend)' '
- head=`git rev-parse HEAD` &&
+ 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 "$(git log -1 --pretty=format:%s)" = "$head"
'
test_expect_success 'with hook (-c)' '
- head=`git rev-parse HEAD` &&
+ 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"
+ test "$(git log -1 --pretty=format:%s)" = "$head"
'
+test_expect_success 'with hook (merge)' '
+
+ test_when_finished "git checkout -f master" &&
+ git checkout -B other HEAD@{1} &&
+ echo "more" >>file &&
+ git add file &&
+ git commit -m other &&
+ git checkout - &&
+ git merge --no-ff other &&
+ test "$(git log -1 --pretty=format:%s)" = "merge (no editor)"
+'
+
+test_expect_success 'with hook and editor (merge)' '
+
+ test_when_finished "git checkout -f master" &&
+ git checkout -B other HEAD@{1} &&
+ echo "more" >>file &&
+ git add file &&
+ git commit -m other &&
+ git checkout - &&
+ env GIT_EDITOR="\"\$FAKE_EDITOR\"" git merge --no-ff -e other &&
+ test "$(git log -1 --pretty=format:%s)" = "merge"
+'
+
cat > "$HOOK" <<'EOF'
#!/bin/sh
exit 1
@@ -139,21 +163,38 @@ EOF
test_expect_success 'with failing hook' '
- head=`git rev-parse HEAD` &&
+ test_when_finished "git checkout -f master" &&
+ head=$(git rev-parse HEAD) &&
echo "more" >> file &&
git add file &&
- ! GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -c $head
+ test_must_fail env GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -c $head
'
test_expect_success 'with failing hook (--no-verify)' '
- head=`git rev-parse HEAD` &&
+ test_when_finished "git checkout -f master" &&
+ head=$(git rev-parse HEAD) &&
echo "more" >> file &&
git add file &&
- ! GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify -c $head
+ test_must_fail env GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify -c $head
'
+test_expect_success 'with failing hook (merge)' '
+
+ test_when_finished "git checkout -f master" &&
+ git checkout -B other HEAD@{1} &&
+ echo "more" >> file &&
+ git add file &&
+ rm -f "$HOOK" &&
+ git commit -m other &&
+ write_script "$HOOK" <<-EOF &&
+ exit 1
+ EOF
+ git checkout - &&
+ test_must_fail git merge --no-ff other
+
+'
test_done
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index 3d4f85d..d31b34d 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -4,17 +4,21 @@ test_description='git status for submodule'
. ./test-lib.sh
-test_expect_success 'setup' '
- test_create_repo sub &&
+test_create_repo_with_commit () {
+ test_create_repo "$1" &&
(
- cd sub &&
+ cd "$1" &&
: >bar &&
git add bar &&
git commit -m " Add bar" &&
: >foo &&
git add foo &&
git commit -m " Add foo"
- ) &&
+ )
+}
+
+test_expect_success 'setup' '
+ test_create_repo_with_commit sub &&
echo output > .gitignore &&
git add sub .gitignore &&
git commit -m "Add submodule sub"
@@ -22,19 +26,19 @@ test_expect_success 'setup' '
test_expect_success 'status clean' '
git status >output &&
- grep "nothing to commit" output
+ test_i18ngrep "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_i18ngrep "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_i18ngrep "modified: sub (modified content)" output
'
test_expect_success 'status with modified file in submodule (porcelain)' '
@@ -49,7 +53,7 @@ test_expect_success 'status with modified file in submodule (porcelain)' '
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_i18ngrep "modified: sub (modified content)" output
'
test_expect_success 'status with added file in submodule (porcelain)' '
@@ -64,12 +68,12 @@ 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_i18ngrep "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_i18ngrep "^nothing to commit" output
'
test_expect_success 'status with untracked file in submodule (porcelain)' '
@@ -83,7 +87,7 @@ 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_i18ngrep "modified: sub (modified content, untracked content)" output
'
test_expect_success 'status with added and untracked file in submodule (porcelain)' '
@@ -101,7 +105,7 @@ test_expect_success 'status with modified file in modified submodule' '
(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_i18ngrep "modified: sub (new commits, modified content)" output
'
test_expect_success 'status with modified file in modified submodule (porcelain)' '
@@ -116,7 +120,7 @@ test_expect_success 'status with modified file in modified submodule (porcelain)
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_i18ngrep "modified: sub (new commits, modified content)" output
'
test_expect_success 'status with added file in modified submodule (porcelain)' '
@@ -131,7 +135,7 @@ 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_i18ngrep "modified: sub (new commits, untracked content)" output
'
test_expect_success 'status with untracked file in modified submodule (porcelain)' '
@@ -145,7 +149,7 @@ 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_i18ngrep "modified: sub (new commits, modified content, untracked content)" output
'
test_expect_success 'status with added and untracked file in modified submodule (porcelain)' '
@@ -170,7 +174,7 @@ test_expect_success 'setup .git file for sub' '
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_i18ngrep "modified: sub (new commits, modified content)" output
'
test_expect_success 'rm submodule contents' '
@@ -179,12 +183,92 @@ test_expect_success 'rm submodule contents' '
test_expect_success 'status clean (empty submodule dir)' '
git status >output &&
- grep "nothing to commit" output
+ test_i18ngrep "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_i18ngrep "nothing to commit" output
+'
+
+cat >status_expect <<\EOF
+AA .gitmodules
+A sub1
+EOF
+
+test_expect_success 'status with merge conflict in .gitmodules' '
+ git clone . super &&
+ test_create_repo_with_commit sub1 &&
+ test_tick &&
+ test_create_repo_with_commit sub2 &&
+ (
+ cd super &&
+ prev=$(git rev-parse HEAD) &&
+ git checkout -b add_sub1 &&
+ git submodule add ../sub1 &&
+ git commit -m "add sub1" &&
+ git checkout -b add_sub2 $prev &&
+ git submodule add ../sub2 &&
+ git commit -m "add sub2" &&
+ git checkout -b merge_conflict_gitmodules &&
+ test_must_fail git merge add_sub1 &&
+ git status -s >../status_actual 2>&1
+ ) &&
+ test_cmp status_actual status_expect
+'
+
+sha1_merge_sub1=$(cd sub1 && git rev-parse HEAD)
+sha1_merge_sub2=$(cd sub2 && git rev-parse HEAD)
+short_sha1_merge_sub1=$(cd sub1 && git rev-parse --short HEAD)
+short_sha1_merge_sub2=$(cd sub2 && git rev-parse --short HEAD)
+cat >diff_expect <<\EOF
+diff --cc .gitmodules
+index badaa4c,44f999a..0000000
+--- a/.gitmodules
++++ b/.gitmodules
+@@@ -1,3 -1,3 +1,9 @@@
+++<<<<<<< HEAD
+ +[submodule "sub2"]
+ + path = sub2
+ + url = ../sub2
+++=======
++ [submodule "sub1"]
++ path = sub1
++ url = ../sub1
+++>>>>>>> add_sub1
+EOF
+
+cat >diff_submodule_expect <<\EOF
+diff --cc .gitmodules
+index badaa4c,44f999a..0000000
+--- a/.gitmodules
++++ b/.gitmodules
+@@@ -1,3 -1,3 +1,9 @@@
+++<<<<<<< HEAD
+ +[submodule "sub2"]
+ + path = sub2
+ + url = ../sub2
+++=======
++ [submodule "sub1"]
++ path = sub1
++ url = ../sub1
+++>>>>>>> add_sub1
+EOF
+
+test_expect_success 'diff with merge conflict in .gitmodules' '
+ (
+ cd super &&
+ git diff >../diff_actual 2>&1
+ ) &&
+ test_cmp diff_actual diff_expect
+'
+
+test_expect_success 'diff --submodule with merge conflict in .gitmodules' '
+ (
+ cd super &&
+ git diff --submodule >../diff_submodule_actual 2>&1
+ ) &&
+ test_cmp diff_submodule_actual diff_submodule_expect
'
test_done
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index da5bd3b..ed2653d 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -3,11 +3,10 @@
test_description='verbose commit template'
. ./test-lib.sh
-cat >check-for-diff <<EOF
-#!$SHELL_PATH
-exec grep '^diff --git' "\$1"
+write_script "check-for-diff" <<\EOF &&
+grep '^diff --git' "$1" >out
+exit 0
EOF
-chmod +x check-for-diff
test_set_editor "$PWD/check-for-diff"
cat >message <<'EOF'
@@ -23,7 +22,8 @@ test_expect_success 'setup' '
'
test_expect_success 'initial commit shows verbose diff' '
- git commit --amend -v
+ git commit --amend -v &&
+ test_line_count = 1 out
'
test_expect_success 'second commit' '
@@ -39,13 +39,15 @@ check_message() {
test_expect_success 'verbose diff is stripped out' '
git commit --amend -v &&
- check_message message
+ check_message message &&
+ test_line_count = 1 out
'
test_expect_success 'verbose diff is stripped out (mnemonicprefix)' '
git config diff.mnemonicprefix true &&
git commit --amend -v &&
- check_message message
+ check_message message &&
+ test_line_count = 1 out
'
cat >diff <<'EOF'
@@ -65,9 +67,91 @@ test_expect_success 'diff in message is retained without -v' '
check_message diff
'
-test_expect_failure 'diff in message is retained with -v' '
+test_expect_success 'diff in message is retained with -v' '
git commit --amend -F diff -v &&
check_message diff
'
+test_expect_success 'submodule log is stripped out too with -v' '
+ git config diff.submodule log &&
+ git submodule add ./. sub &&
+ git commit -m "sub added" &&
+ (
+ cd sub &&
+ echo "more" >>file &&
+ git commit -a -m "submodule commit"
+ ) &&
+ (
+ GIT_EDITOR=cat &&
+ export GIT_EDITOR &&
+ test_must_fail git commit -a -v 2>err
+ ) &&
+ test_i18ngrep "Aborting commit due to empty commit message." err
+'
+
+test_expect_success 'verbose diff is stripped out with set core.commentChar' '
+ (
+ GIT_EDITOR=cat &&
+ export GIT_EDITOR &&
+ test_must_fail git -c core.commentchar=";" commit -a -v 2>err
+ ) &&
+ test_i18ngrep "Aborting commit due to empty commit message." err
+'
+
+test_expect_success 'status does not verbose without --verbose' '
+ git status >actual &&
+ ! grep "^diff --git" actual
+'
+
+test_expect_success 'setup -v -v' '
+ echo dirty >file
+'
+
+for i in true 1
+do
+ test_expect_success "commit.verbose=$i and --verbose omitted" "
+ git -c commit.verbose=$i commit --amend &&
+ test_line_count = 1 out
+ "
+done
+
+for i in false -2 -1 0
+do
+ test_expect_success "commit.verbose=$i and --verbose omitted" "
+ git -c commit.verbose=$i commit --amend &&
+ test_line_count = 0 out
+ "
+done
+
+for i in 2 3
+do
+ test_expect_success "commit.verbose=$i and --verbose omitted" "
+ git -c commit.verbose=$i commit --amend &&
+ test_line_count = 2 out
+ "
+done
+
+for i in true false -2 -1 0 1 2 3
+do
+ test_expect_success "commit.verbose=$i and --verbose" "
+ git -c commit.verbose=$i commit --amend --verbose &&
+ test_line_count = 1 out
+ "
+
+ test_expect_success "commit.verbose=$i and --no-verbose" "
+ git -c commit.verbose=$i commit --amend --no-verbose &&
+ test_line_count = 0 out
+ "
+
+ test_expect_success "commit.verbose=$i and -v -v" "
+ git -c commit.verbose=$i commit --amend -v -v &&
+ test_line_count = 2 out
+ "
+done
+
+test_expect_success "status ignores commit.verbose=true" '
+ git -c commit.verbose=true status >actual &&
+ ! grep "^diff --git actual"
+'
+
test_done
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index a72fe3a..c3ed7cb 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -7,6 +7,31 @@ test_description='git status'
. ./test-lib.sh
+test_expect_success 'status -h in broken repository' '
+ git config --global advice.statusuoption false &&
+ mkdir broken &&
+ test_when_finished "rm -fr broken" &&
+ (
+ cd broken &&
+ git init &&
+ echo "[status] showuntrackedfiles = CORRUPT" >>.git/config &&
+ test_expect_code 129 git status -h >usage 2>&1
+ ) &&
+ test_i18ngrep "[Uu]sage" broken/usage
+'
+
+test_expect_success 'commit -h in broken repository' '
+ mkdir broken &&
+ test_when_finished "rm -fr broken" &&
+ (
+ cd broken &&
+ git init &&
+ echo "[status] showuntrackedfiles = CORRUPT" >>.git/config &&
+ test_expect_code 129 git commit -h >usage 2>&1
+ ) &&
+ test_i18ngrep "[Uu]sage" broken/usage
+'
+
test_expect_success 'setup' '
: >tracked &&
: >modified &&
@@ -32,19 +57,30 @@ test_expect_success 'setup' '
'
test_expect_success 'status (1)' '
+ test_i18ngrep "use \"git rm --cached <file>\.\.\.\" to unstage" output
+'
- grep "use \"git rm --cached <file>\.\.\.\" to unstage" output
+strip_comments () {
+ tab=' '
+ sed "s/^\# //; s/^\#$//; s/^#$tab/$tab/" <"$1" >"$1".tmp &&
+ rm "$1" && mv "$1".tmp "$1"
+}
-'
+cat >.gitignore <<\EOF
+.gitignore
+expect*
+output*
+EOF
-cat >expect <<\EOF
+test_expect_success 'status --column' '
+ 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:
+# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
@@ -53,48 +89,115 @@ cat >expect <<\EOF
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
-# dir1/untracked
-# dir2/modified
-# dir2/untracked
-# expect
-# output
-# untracked
+# dir1/untracked dir2/untracked
+# dir2/modified untracked
+#
EOF
+ COLUMNS=50 git -c status.displayCommentPrefix=true status --column="column dense" >output &&
+ test_i18ncmp expect output
+'
-test_expect_success 'status (2)' '
-
- git status >output &&
- test_cmp expect output
-
+test_expect_success 'status --column status.displayCommentPrefix=false' '
+ strip_comments expect &&
+ COLUMNS=49 git -c status.displayCommentPrefix=false status --column="column dense" >output &&
+ test_i18ncmp expect output
'
cat >expect <<\EOF
# On branch master
# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
# new file: dir2/added
#
-# Changed but not updated:
+# Changes not staged for commit:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
# modified: dir1/modified
#
# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
# dir1/untracked
# dir2/modified
# dir2/untracked
-# expect
-# output
# untracked
+#
EOF
-git config advice.statusHints false
+test_expect_success 'status with status.displayCommentPrefix=true' '
+ git -c status.displayCommentPrefix=true status >output &&
+ test_i18ncmp expect output
+'
-test_expect_success 'status (advice.statusHints false)' '
+test_expect_success 'status with status.displayCommentPrefix=false' '
+ strip_comments expect &&
+ git -c status.displayCommentPrefix=false status >output &&
+ test_i18ncmp expect output
+'
- git status >output &&
- test_cmp expect output
+test_expect_success 'status -v' '
+ (cat expect && git diff --cached) >expect-with-v &&
+ git status -v >output &&
+ test_i18ncmp expect-with-v output
+'
+
+test_expect_success 'status -v -v' '
+ (cat expect &&
+ echo "Changes to be committed:" &&
+ git -c diff.mnemonicprefix=true diff --cached &&
+ echo "--------------------------------------------------" &&
+ echo "Changes not staged for commit:" &&
+ git -c diff.mnemonicprefix=true diff) >expect-with-v &&
+ git status -v -v >output &&
+ test_i18ncmp expect-with-v output
+'
+test_expect_success 'setup fake editor' '
+ cat >.git/editor <<-\EOF &&
+ #! /bin/sh
+ cp "$1" output
+EOF
+ chmod 755 .git/editor
'
-git config --unset advice.statusHints
+commit_template_commented () {
+ (
+ EDITOR=.git/editor &&
+ export EDITOR &&
+ # Fails due to empty message
+ test_must_fail git commit
+ ) &&
+ ! grep '^[^#]' output
+}
+
+test_expect_success 'commit ignores status.displayCommentPrefix=false in COMMIT_EDITMSG' '
+ commit_template_commented
+'
+
+cat >expect <<\EOF
+On branch master
+Changes to be committed:
+ new file: dir2/added
+
+Changes not staged for commit:
+ modified: dir1/modified
+
+Untracked files:
+ dir1/untracked
+ dir2/modified
+ dir2/untracked
+ untracked
+
+EOF
+
+test_expect_success 'status (advice.statusHints false)' '
+ test_config advice.statusHints false &&
+ git status >output &&
+ test_i18ncmp expect output
+
+'
cat >expect <<\EOF
M dir1/modified
@@ -102,8 +205,6 @@ A dir2/added
?? dir1/untracked
?? dir2/modified
?? dir2/untracked
-?? expect
-?? output
?? untracked
EOF
@@ -114,6 +215,138 @@ test_expect_success 'status -s' '
'
+test_expect_success 'status with gitignore' '
+ {
+ echo ".gitignore" &&
+ echo "expect*" &&
+ echo "output" &&
+ echo "untracked"
+ } >.gitignore &&
+
+ cat >expect <<-\EOF &&
+ M dir1/modified
+ A dir2/added
+ ?? dir2/modified
+ EOF
+ git status -s >output &&
+ test_cmp expect output &&
+
+ cat >expect <<-\EOF &&
+ M dir1/modified
+ A dir2/added
+ ?? dir2/modified
+ !! .gitignore
+ !! dir1/untracked
+ !! dir2/untracked
+ !! expect
+ !! expect-with-v
+ !! output
+ !! untracked
+ EOF
+ git status -s --ignored >output &&
+ test_cmp expect output &&
+
+ cat >expect <<\EOF &&
+On branch master
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ new file: dir2/added
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: dir1/modified
+
+Untracked files:
+ (use "git add <file>..." to include in what will be committed)
+
+ dir2/modified
+
+Ignored files:
+ (use "git add -f <file>..." to include in what will be committed)
+
+ .gitignore
+ dir1/untracked
+ dir2/untracked
+ expect
+ expect-with-v
+ output
+ untracked
+
+EOF
+ git status --ignored >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'status with gitignore (nothing untracked)' '
+ {
+ echo ".gitignore" &&
+ echo "expect*" &&
+ echo "dir2/modified" &&
+ echo "output" &&
+ echo "untracked"
+ } >.gitignore &&
+
+ cat >expect <<-\EOF &&
+ M dir1/modified
+ A dir2/added
+ EOF
+ git status -s >output &&
+ test_cmp expect output &&
+
+ cat >expect <<-\EOF &&
+ M dir1/modified
+ A dir2/added
+ !! .gitignore
+ !! dir1/untracked
+ !! dir2/modified
+ !! dir2/untracked
+ !! expect
+ !! expect-with-v
+ !! output
+ !! untracked
+ EOF
+ git status -s --ignored >output &&
+ test_cmp expect output &&
+
+ cat >expect <<\EOF &&
+On branch master
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ new file: dir2/added
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: dir1/modified
+
+Ignored files:
+ (use "git add -f <file>..." to include in what will be committed)
+
+ .gitignore
+ dir1/untracked
+ dir2/modified
+ dir2/untracked
+ expect
+ expect-with-v
+ output
+ untracked
+
+EOF
+ git status --ignored >output &&
+ test_i18ncmp expect output
+'
+
+cat >.gitignore <<\EOF
+.gitignore
+expect*
+output*
+EOF
+
cat >expect <<\EOF
## master
M dir1/modified
@@ -121,8 +354,6 @@ A dir2/added
?? dir1/untracked
?? dir2/modified
?? dir2/untracked
-?? expect
-?? output
?? untracked
EOF
@@ -133,101 +364,110 @@ test_expect_success 'status -s -b' '
'
-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' '
+test_expect_success 'status -s -z -b' '
+ tr "\\n" Q <expect >expect.q &&
+ mv expect.q expect &&
+ git status -s -z -b >output &&
+ nul_to_q <output >output.q &&
+ mv output.q output &&
+ test_cmp expect output
+'
+
+test_expect_success 'setup dir3' '
mkdir dir3 &&
: >dir3/untracked1 &&
- : >dir3/untracked2 &&
+ : >dir3/untracked2
+'
+
+test_expect_success 'status -uno' '
+ cat >expect <<EOF &&
+On branch master
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ new file: dir2/added
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: dir1/modified
+
+Untracked files not listed (use -u option to show untracked files)
+EOF
git status -uno >output &&
- test_cmp expect output
+ test_i18ncmp expect output
'
test_expect_success 'status (status.showUntrackedFiles no)' '
- git config status.showuntrackedfiles no
+ test_config status.showuntrackedfiles no &&
git status >output &&
- test_cmp expect output
+ test_i18ncmp 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)' '
+ cat >expect <<EOF &&
+On branch master
+Changes to be committed:
+ new file: dir2/added
+
+Changes not staged for commit:
+ modified: dir1/modified
+
+Untracked files not listed
+EOF
+ test_config advice.statusHints false &&
git status -uno >output &&
- test_cmp expect output
+ test_i18ncmp 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 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' '
+ cat >expect <<EOF &&
+On branch master
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ new file: dir2/added
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: dir1/modified
+
+Untracked files:
+ (use "git add <file>..." to include in what will be committed)
+
+ dir1/untracked
+ dir2/modified
+ dir2/untracked
+ dir3/
+ untracked
+
+EOF
git status -unormal >output &&
- test_cmp expect output
+ test_i18ncmp expect output
'
test_expect_success 'status (status.showUntrackedFiles normal)' '
- git config status.showuntrackedfiles normal
+ test_config status.showuntrackedfiles normal &&
git status >output &&
- test_cmp expect output
+ test_i18ncmp expect output
'
cat >expect <<EOF
@@ -237,57 +477,56 @@ A dir2/added
?? 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 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' '
+ cat >expect <<EOF &&
+On branch master
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ new file: dir2/added
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: dir1/modified
+
+Untracked files:
+ (use "git add <file>..." to include in what will be committed)
+
+ dir1/untracked
+ dir2/modified
+ dir2/untracked
+ dir3/untracked1
+ dir3/untracked2
+ untracked
+
+EOF
git status -uall >output &&
- test_cmp expect output
+ test_i18ncmp expect output
'
+
test_expect_success 'status (status.showUntrackedFiles all)' '
- git config status.showuntrackedfiles all
+ test_config status.showuntrackedfiles all &&
git status >output &&
- rm -rf dir3 &&
- git config --unset status.showuntrackedfiles &&
- test_cmp expect output
+ test_i18ncmp expect output
+'
+
+test_expect_success 'teardown dir3' '
+ rm -rf dir3
'
cat >expect <<EOF
@@ -296,52 +535,45 @@ A dir2/added
?? dir1/untracked
?? dir2/modified
?? dir2/untracked
-?? expect
-?? output
?? untracked
EOF
test_expect_success 'status -s -uall' '
- git config --unset status.showuntrackedfiles
+ test_unconfig status.showuntrackedfiles &&
git status -s -uall >output &&
test_cmp expect output
'
test_expect_success 'status -s (status.showUntrackedFiles all)' '
- git config status.showuntrackedfiles all
+ test_config status.showuntrackedfiles all &&
git status -s >output &&
rm -rf dir3 &&
- git config --unset status.showuntrackedfiles &&
test_cmp expect output
'
-cat >expect <<\EOF
-# On branch master
-# Changes to be committed:
-# (use "git reset HEAD <file>..." to unstage)
-#
-# new file: ../dir2/added
-#
-# 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' '
+ cat >expect <<\EOF &&
+On branch master
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
- (cd dir1 && git status) >output &&
- test_cmp expect output
+ new file: ../dir2/added
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: modified
+
+Untracked files:
+ (use "git add <file>..." to include in what will be committed)
+ untracked
+ ../dir2/modified
+ ../dir2/untracked
+ ../untracked
+
+EOF
+ (cd dir1 && git status) >output &&
+ test_i18ncmp expect output
'
cat >expect <<\EOF
@@ -350,8 +582,6 @@ A ../dir2/added
?? untracked
?? ../dir2/modified
?? ../dir2/untracked
-?? ../expect
-?? ../output
?? ../untracked
EOF
test_expect_success 'status -s with relative paths' '
@@ -367,8 +597,6 @@ A dir2/added
?? dir1/untracked
?? dir2/modified
?? dir2/untracked
-?? expect
-?? output
?? untracked
EOF
@@ -381,49 +609,43 @@ test_expect_success 'status --porcelain ignores relative paths setting' '
test_expect_success 'setup unique colors' '
- git config status.color.untracked blue
+ git config status.color.untracked blue &&
+ git config status.color.branch green
'
-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' '
+ cat >expect <<\EOF &&
+On branch <GREEN>master<RESET>
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
- git config color.ui always &&
- git status | test_decode_color >output &&
- test_cmp expect output
+ <GREEN>new file: dir2/added<RESET>
-'
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
-test_expect_success 'status with color.status' '
+ <RED>modified: dir1/modified<RESET>
- git config --unset color.ui &&
- git config color.status always &&
+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>untracked<RESET>
+
+EOF
+ test_config color.ui always &&
git status | test_decode_color >output &&
- test_cmp expect output
+ test_i18ncmp expect output
+'
+test_expect_success 'status with color.status' '
+ test_config color.status always &&
+ git status | test_decode_color >output &&
+ test_i18ncmp expect output
'
cat >expect <<\EOF
@@ -432,14 +654,11 @@ cat >expect <<\EOF
<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
@@ -462,8 +681,6 @@ cat >expect <<\EOF
<BLUE>??<RESET> dir1/untracked
<BLUE>??<RESET> dir2/modified
<BLUE>??<RESET> dir2/untracked
-<BLUE>??<RESET> expect
-<BLUE>??<RESET> output
<BLUE>??<RESET> untracked
EOF
@@ -480,8 +697,6 @@ A dir2/added
?? dir1/untracked
?? dir2/modified
?? dir2/untracked
-?? expect
-?? output
?? untracked
EOF
@@ -507,43 +722,46 @@ test_expect_success 'status --porcelain ignores color.status' '
git config --unset color.status
git config --unset color.ui
-test_expect_success 'status --porcelain ignores -b' '
+test_expect_success 'status --porcelain respects -b' '
git status --porcelain -b >output &&
+ {
+ echo "## master" &&
+ cat expect
+ } >tmp &&
+ mv tmp expect &&
test_cmp expect output
'
-cat >expect <<\EOF
-# On branch master
-# Changes to be committed:
-# (use "git reset HEAD <file>..." to unstage)
-#
-# new file: dir2/added
-#
-# 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' '
+ cat >expect <<\EOF &&
+On branch master
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ new file: dir2/added
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: dir1/modified
+
+Untracked files:
+ (use "git add <file>..." to include in what will be committed)
- git config status.relativePaths false
+ dir1/untracked
+ dir2/modified
+ dir2/untracked
+ untracked
+
+EOF
+ test_config status.relativePaths false &&
(cd dir1 && git status) >output &&
- test_cmp expect output
+ test_i18ncmp expect output
'
@@ -553,37 +771,35 @@ A dir2/added
?? dir1/untracked
?? dir2/modified
?? dir2/untracked
-?? expect
-?? output
?? untracked
EOF
test_expect_success 'status -s without relative paths' '
+ test_config status.relativePaths false &&
(cd dir1 && git status -s) >output &&
test_cmp expect output
'
-cat <<EOF >expect
-# On branch master
-# Changes to be committed:
-# (use "git reset HEAD <file>..." to unstage)
-#
-# modified: dir1/modified
-#
-# Untracked files:
-# (use "git add <file>..." to include in what will be committed)
-#
-# dir1/untracked
-# dir2/
-# expect
-# output
-# untracked
-EOF
test_expect_success 'dry-run of partial commit excluding new file in index' '
+ cat >expect <<EOF &&
+On branch master
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ modified: dir1/modified
+
+Untracked files:
+ (use "git add <file>..." to include in what will be committed)
+
+ dir1/untracked
+ dir2/
+ untracked
+
+EOF
git commit --dry-run dir1/modified >output &&
- test_cmp expect output
+ test_i18ncmp expect output
'
cat >expect <<EOF
@@ -606,39 +822,38 @@ test_expect_success 'setup status submodule summary' '
git add sm
'
-cat >expect <<EOF
-# On branch master
-# Changes to be committed:
-# (use "git reset HEAD <file>..." to unstage)
-#
-# new file: dir2/added
-# new file: sm
-#
-# 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' '
+ cat >expect <<EOF &&
+On branch master
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ new file: dir2/added
+ new file: sm
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: dir1/modified
+
+Untracked files:
+ (use "git add <file>..." to include in what will be committed)
+
+ dir1/untracked
+ dir2/modified
+ dir2/untracked
+ untracked
+
+EOF
git status >output &&
- test_cmp expect output
+ test_i18ncmp 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
+ test_i18ncmp expect output
'
cat >expect <<EOF
@@ -648,8 +863,6 @@ A sm
?? dir1/untracked
?? dir2/modified
?? dir2/untracked
-?? expect
-?? output
?? untracked
EOF
test_expect_success 'status -s submodule summary is disabled by default' '
@@ -665,39 +878,48 @@ test_expect_success 'status -s --untracked-files=all does not show submodule' '
head=$(cd sm && git rev-parse --short=7 --verify HEAD)
-cat >expect <<EOF
-# On branch master
-# Changes to be committed:
-# (use "git reset HEAD <file>..." to unstage)
-#
-# new file: dir2/added
-# new file: sm
-#
-# 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' '
+ cat >expect <<EOF &&
+On branch master
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ new file: dir2/added
+ new file: sm
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: dir1/modified
+
+Submodule changes to be committed:
+
+* sm 0000000...$head (1):
+ > Add foo
+
+Untracked files:
+ (use "git add <file>..." to include in what will be committed)
+
+ dir1/untracked
+ dir2/modified
+ dir2/untracked
+ untracked
+
+EOF
git config status.submodulesummary 10 &&
git status >output &&
- test_cmp expect output
+ test_i18ncmp expect output
+'
+
+test_expect_success 'status submodule summary with status.displayCommentPrefix=false' '
+ strip_comments expect &&
+ git -c status.displayCommentPrefix=false status >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'commit with submodule summary ignores status.displayCommentPrefix' '
+ commit_template_commented
'
cat >expect <<EOF
@@ -707,8 +929,6 @@ A sm
?? dir1/untracked
?? dir2/modified
?? dir2/untracked
-?? expect
-?? output
?? untracked
EOF
test_expect_success 'status -s submodule summary' '
@@ -716,32 +936,31 @@ test_expect_success 'status -s submodule summary' '
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
+test_expect_success 'status submodule summary (clean submodule): commit' '
+ cat >expect <<EOF &&
+On branch master
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: dir1/modified
+
+Untracked files:
+ (use "git add <file>..." to include in what will be committed)
+
+ dir1/untracked
+ dir2/modified
+ dir2/untracked
+ 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 &&
+ test_i18ncmp expect output &&
git status >output &&
- test_cmp expect output
+ test_i18ncmp expect output
'
cat >expect <<EOF
@@ -749,8 +968,6 @@ cat >expect <<EOF
?? dir1/untracked
?? dir2/modified
?? dir2/untracked
-?? expect
-?? output
?? untracked
EOF
test_expect_success 'status -s submodule summary (clean submodule)' '
@@ -758,42 +975,48 @@ test_expect_success 'status -s submodule summary (clean submodule)' '
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 'status -z implies porcelain' '
+ git status --porcelain |
+ perl -pe "s/\012/\000/g" >expect &&
+ git status -z >output &&
+ test_cmp expect output
+'
+
test_expect_success 'commit --dry-run submodule summary (--amend)' '
+ cat >expect <<EOF &&
+On branch master
+Changes to be committed:
+ (use "git reset HEAD^1 <file>..." to unstage)
+
+ new file: dir2/added
+ new file: sm
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: dir1/modified
+
+Submodule changes to be committed:
+
+* sm 0000000...$head (1):
+ > Add foo
+
+Untracked files:
+ (use "git add <file>..." to include in what will be committed)
+
+ dir1/untracked
+ dir2/modified
+ dir2/untracked
+ untracked
+
+EOF
git config status.submodulesummary 10 &&
git commit --dry-run --amend >output &&
- test_cmp expect output
+ test_i18ncmp expect output
'
-test_expect_success POSIXPERM 'status succeeds in a read-only repository' '
+test_expect_success POSIXPERM,SANITY 'status succeeds in a read-only repository' '
(
chmod a-w .git &&
# make dir1/tracked stat-dirty
@@ -808,131 +1031,472 @@ test_expect_success POSIXPERM 'status succeeds in a read-only repository' '
(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
+(cd sm && echo > bar && git add bar && git commit -q -m 'Add bar') && git add sm
+new_head=$(cd sm && git rev-parse --short=7 --verify HEAD)
+touch .gitmodules
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
+ cat > expect << EOF &&
+On branch master
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ modified: sm
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: dir1/modified
+
+Submodule changes to be committed:
+
+* sm $head...$new_head (1):
+ > Add bar
+
+Untracked files:
+ (use "git add <file>..." to include in what will be committed)
+
+ .gitmodules
+ dir1/untracked
+ dir2/modified
+ dir2/untracked
+ untracked
+
+EOF
+ echo modified sm/untracked &&
+ git status --ignore-submodules=untracked >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success '.gitmodules ignore=untracked suppresses submodules with untracked content' '
+ test_config diff.ignoreSubmodules dirty &&
+ git status >output &&
+ test_i18ncmp expect output &&
+ git config --add -f .gitmodules submodule.subname.ignore untracked &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status >output &&
+ test_i18ncmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_success '.git/config ignore=untracked suppresses submodules with untracked content' '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore untracked &&
+ git config --add submodule.subname.path sm &&
+ git status >output &&
+ test_i18ncmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config --remove-section -f .gitmodules submodule.subname
'
test_expect_success '--ignore-submodules=dirty suppresses submodules with untracked content' '
- git status --ignore-submodules=dirty > output &&
- test_cmp expect output
+ git status --ignore-submodules=dirty >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success '.gitmodules ignore=dirty suppresses submodules with untracked content' '
+ test_config diff.ignoreSubmodules dirty &&
+ git status >output &&
+ ! test -s actual &&
+ git config --add -f .gitmodules submodule.subname.ignore dirty &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status >output &&
+ test_i18ncmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_success '.git/config ignore=dirty suppresses submodules with untracked content' '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore dirty &&
+ git config --add submodule.subname.path sm &&
+ git status >output &&
+ test_i18ncmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
'
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
+ echo modified >sm/foo &&
+ git status --ignore-submodules=dirty >output &&
+ test_i18ncmp 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 '.gitmodules ignore=dirty suppresses submodules with modified content' '
+ git config --add -f .gitmodules submodule.subname.ignore dirty &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status >output &&
+ test_i18ncmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_success '.git/config ignore=dirty suppresses submodules with modified content' '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore dirty &&
+ git config --add submodule.subname.path sm &&
+ git status >output &&
+ test_i18ncmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
test_expect_success "--ignore-submodules=untracked doesn't suppress submodules with modified content" '
+ cat > expect << EOF &&
+On branch master
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ modified: sm
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+ (commit or discard the untracked or modified content in submodules)
+
+ modified: dir1/modified
+ modified: sm (modified content)
+
+Submodule changes to be committed:
+
+* sm $head...$new_head (1):
+ > Add bar
+
+Untracked files:
+ (use "git add <file>..." to include in what will be committed)
+
+ .gitmodules
+ dir1/untracked
+ dir2/modified
+ dir2/untracked
+ untracked
+
+EOF
git status --ignore-submodules=untracked > output &&
- test_cmp expect output
+ test_i18ncmp expect output
'
-head2=$(cd sm && git commit -q -m "2nd commit" foo && git rev-parse --short=7 --verify HEAD)
+test_expect_success ".gitmodules ignore=untracked doesn't suppress submodules with modified content" '
+ git config --add -f .gitmodules submodule.subname.ignore untracked &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status >output &&
+ test_i18ncmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
-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 ".git/config ignore=untracked doesn't suppress submodules with modified content" '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore untracked &&
+ git config --add submodule.subname.path sm &&
+ git status >output &&
+ test_i18ncmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+head2=$(cd sm && git commit -q -m "2nd commit" foo && git rev-parse --short=7 --verify HEAD)
test_expect_success "--ignore-submodules=untracked doesn't suppress submodule summary" '
+ cat > expect << EOF &&
+On branch master
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ modified: sm
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: dir1/modified
+ modified: sm (new commits)
+
+Submodule changes to be committed:
+
+* sm $head...$new_head (1):
+ > Add bar
+
+Submodules changed but not updated:
+
+* sm $new_head...$head2 (1):
+ > 2nd commit
+
+Untracked files:
+ (use "git add <file>..." to include in what will be committed)
+
+ .gitmodules
+ dir1/untracked
+ dir2/modified
+ dir2/untracked
+ untracked
+
+EOF
git status --ignore-submodules=untracked > output &&
- test_cmp expect output
+ test_i18ncmp expect output
+'
+
+test_expect_success ".gitmodules ignore=untracked doesn't suppress submodule summary" '
+ git config --add -f .gitmodules submodule.subname.ignore untracked &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status >output &&
+ test_i18ncmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=untracked doesn't suppress submodule summary" '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore untracked &&
+ git config --add submodule.subname.path sm &&
+ git status >output &&
+ test_i18ncmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
'
test_expect_success "--ignore-submodules=dirty doesn't suppress submodule summary" '
git status --ignore-submodules=dirty > output &&
- test_cmp expect output
+ test_i18ncmp expect output
+'
+test_expect_success ".gitmodules ignore=dirty doesn't suppress submodule summary" '
+ git config --add -f .gitmodules submodule.subname.ignore dirty &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status >output &&
+ test_i18ncmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=dirty doesn't suppress submodule summary" '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore dirty &&
+ git config --add submodule.subname.path sm &&
+ git status >output &&
+ test_i18ncmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
'
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")
+; On branch master
+; Changes to be committed:
+; (use "git reset HEAD <file>..." to unstage)
+;
+; modified: sm
+;
+; Changes not staged for commit:
+; (use "git add <file>..." to update what will be committed)
+; (use "git checkout -- <file>..." to discard changes in working directory)
+;
+; modified: dir1/modified
+; modified: sm (new commits)
+;
+; Submodule changes to be committed:
+;
+; * sm $head...$new_head (1):
+; > Add bar
+;
+; Submodules changed but not updated:
+;
+; * sm $new_head...$head2 (1):
+; > 2nd commit
+;
+; Untracked files:
+; (use "git add <file>..." to include in what will be committed)
+;
+; .gitmodules
+; dir1/untracked
+; dir2/modified
+; dir2/untracked
+; untracked
+;
EOF
+test_expect_success "status (core.commentchar with submodule summary)" '
+ test_config core.commentchar ";" &&
+ git -c status.displayCommentPrefix=true status >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success "status (core.commentchar with two chars with submodule summary)" '
+ test_config core.commentchar ";;" &&
+ test_must_fail git -c status.displayCommentPrefix=true status
+'
+
test_expect_success "--ignore-submodules=all suppresses submodule summary" '
+ cat > expect << EOF &&
+On branch master
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: dir1/modified
+
+Untracked files:
+ (use "git add <file>..." to include in what will be committed)
+
+ .gitmodules
+ dir1/untracked
+ dir2/modified
+ dir2/untracked
+ untracked
+
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
git status --ignore-submodules=all > output &&
- test_cmp expect output
+ test_i18ncmp expect output
+'
+
+test_expect_success '.gitmodules ignore=all suppresses unstaged submodule summary' '
+ cat > expect << EOF &&
+On branch master
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ modified: sm
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: dir1/modified
+
+Untracked files:
+ (use "git add <file>..." to include in what will be committed)
+
+ .gitmodules
+ dir1/untracked
+ dir2/modified
+ dir2/untracked
+ untracked
+
+EOF
+ git config --add -f .gitmodules submodule.subname.ignore all &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_success '.git/config ignore=all suppresses unstaged submodule summary' '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore all &&
+ git config --add submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_success 'setup of test environment' '
+ git config status.showUntrackedFiles no &&
+ git status -s >expected_short &&
+ git status --no-short >expected_noshort
+'
+
+test_expect_success '"status.short=true" same as "-s"' '
+ git -c status.short=true status >actual &&
+ test_cmp expected_short actual
+'
+
+test_expect_success '"status.short=true" weaker than "--no-short"' '
+ git -c status.short=true status --no-short >actual &&
+ test_cmp expected_noshort actual
+'
+
+test_expect_success '"status.short=false" same as "--no-short"' '
+ git -c status.short=false status >actual &&
+ test_cmp expected_noshort actual
+'
+
+test_expect_success '"status.short=false" weaker than "-s"' '
+ git -c status.short=false status -s >actual &&
+ test_cmp expected_short actual
+'
+
+test_expect_success '"status.branch=true" same as "-b"' '
+ git status -sb >expected_branch &&
+ git -c status.branch=true status -s >actual &&
+ test_cmp expected_branch actual
+'
+
+test_expect_success '"status.branch=true" different from "--no-branch"' '
+ git status -s --no-branch >expected_nobranch &&
+ git -c status.branch=true status -s >actual &&
+ test_must_fail test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=true" weaker than "--no-branch"' '
+ git -c status.branch=true status -s --no-branch >actual &&
+ test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=true" weaker than "--porcelain"' '
+ git -c status.branch=true status --porcelain >actual &&
+ test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=false" same as "--no-branch"' '
+ git -c status.branch=false status -s >actual &&
+ test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=false" weaker than "-b"' '
+ git -c status.branch=false status -sb >actual &&
+ test_cmp expected_branch actual
+'
+
+test_expect_success 'Restore default test environment' '
+ git config --unset status.showUntrackedFiles
+'
+
+test_expect_success 'git commit will commit a staged but ignored submodule' '
+ git config --add -f .gitmodules submodule.subname.ignore all &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore all &&
+ git status -s --ignore-submodules=dirty >output &&
+ test_i18ngrep "^M. sm" output &&
+ GIT_EDITOR="echo hello >>\"\$1\"" &&
+ export GIT_EDITOR &&
+ git commit -uno &&
+ git status -s --ignore-submodules=dirty >output &&
+ test_i18ngrep ! "^M. sm" output
+'
+
+test_expect_success 'git commit --dry-run will show a staged but ignored submodule' '
+ git reset HEAD^ &&
+ git add sm &&
+ cat >expect << EOF &&
+On branch master
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ modified: sm
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: dir1/modified
+
+Untracked files not listed (use -u option to show untracked files)
+EOF
+ git commit -uno --dry-run >output &&
+ test_i18ncmp expect output &&
+ git status -s --ignore-submodules=dirty >output &&
+ test_i18ngrep "^M. sm" output
+'
+
+test_expect_success 'git commit -m will commit a staged but ignored submodule' '
+ git commit -uno -m message &&
+ git status -s --ignore-submodules=dirty >output &&
+ test_i18ngrep ! "^M. sm" output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
'
test_done
diff --git a/t/t7509-commit.sh b/t/t7509-commit.sh
index 3ea33db..db9774e 100755
--- a/t/t7509-commit.sh
+++ b/t/t7509-commit.sh
@@ -40,7 +40,7 @@ test_expect_success '-C option copies only the message with --reset-author' '
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
+ author_header HEAD >actual &&
test_cmp expect actual &&
message_body Initial >expect &&
@@ -77,6 +77,7 @@ test_expect_success '--amend option copies authorship' '
git commit -a --amend -m "amend test" &&
author_header Initial >expect &&
author_header HEAD >actual &&
+ test_cmp expect actual &&
echo "amend test" >expect &&
message_body HEAD >actual &&
@@ -89,43 +90,30 @@ sha1_file() {
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 &&
+ test_must_fail 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 &&
+ test_must_fail git commit -a -m "malformed author" --amend 2>err &&
grep "empty ident" err
'
@@ -157,4 +145,33 @@ test_expect_success '--reset-author should be rejected without -c/-C/--amend' '
test_must_fail git commit -a --reset-author -m done
'
+test_expect_success 'commit respects CHERRY_PICK_HEAD and MERGE_MSG' '
+ echo "cherry-pick 1a" >>foo &&
+ test_tick &&
+ git commit -am "cherry-pick 1" --author="Cherry <cherry@pick.er>" &&
+ git tag cherry-pick-head &&
+ git rev-parse cherry-pick-head >.git/CHERRY_PICK_HEAD &&
+ echo "This is a MERGE_MSG" >.git/MERGE_MSG &&
+ echo "cherry-pick 1b" >>foo &&
+ test_tick &&
+ git commit -a &&
+ author_header cherry-pick-head >expect &&
+ author_header HEAD >actual &&
+ test_cmp expect actual &&
+
+ echo "This is a MERGE_MSG" >expect &&
+ message_body HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--reset-author with CHERRY_PICK_HEAD' '
+ git rev-parse cherry-pick-head >.git/CHERRY_PICK_HEAD &&
+ echo "cherry-pick 2" >>foo &&
+ test_tick &&
+ git commit -am "cherry-pick 2" --reset-author &&
+ echo "author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
+ author_header HEAD >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh
new file mode 100755
index 0000000..4177a86
--- /dev/null
+++ b/t/t7510-signed-commit.sh
@@ -0,0 +1,213 @@
+#!/bin/sh
+
+test_description='signed commit tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success GPG 'create signed commits' '
+ test_when_finished "test_unconfig commit.gpgsign" &&
+
+ echo 1 >file && git add file &&
+ test_tick && git commit -S -m initial &&
+ git tag initial &&
+ git branch side &&
+
+ echo 2 >file && test_tick && git commit -a -S -m second &&
+ git tag second &&
+
+ git checkout side &&
+ echo 3 >elif && git add elif &&
+ test_tick && git commit -m "third on side" &&
+
+ git checkout master &&
+ test_tick && git merge -S side &&
+ git tag merge &&
+
+ echo 4 >file && test_tick && git commit -a -m "fourth unsigned" &&
+ git tag fourth-unsigned &&
+
+ test_tick && git commit --amend -S -m "fourth signed" &&
+ git tag fourth-signed &&
+
+ git config commit.gpgsign true &&
+ echo 5 >file && test_tick && git commit -a -m "fifth signed" &&
+ git tag fifth-signed &&
+
+ git config commit.gpgsign false &&
+ echo 6 >file && test_tick && git commit -a -m "sixth" &&
+ git tag sixth-unsigned &&
+
+ git config commit.gpgsign true &&
+ echo 7 >file && test_tick && git commit -a -m "seventh" --no-gpg-sign &&
+ git tag seventh-unsigned &&
+
+ test_tick && git rebase -f HEAD^^ && git tag sixth-signed HEAD^ &&
+ git tag seventh-signed &&
+
+ echo 8 >file && test_tick && git commit -a -m eighth -SB7227189 &&
+ git tag eighth-signed-alt &&
+
+ # commit.gpgsign is still on but this must not be signed
+ git tag ninth-unsigned $(echo 9 | git commit-tree HEAD^{tree}) &&
+ # explicit -S of course must sign.
+ git tag tenth-signed $(echo 9 | git commit-tree -S HEAD^{tree})
+'
+
+test_expect_success GPG 'verify and show signatures' '
+ (
+ for commit in initial second merge fourth-signed \
+ fifth-signed sixth-signed seventh-signed tenth-signed
+ do
+ git verify-commit $commit &&
+ git show --pretty=short --show-signature $commit >actual &&
+ grep "Good signature from" actual &&
+ ! grep "BAD signature from" actual &&
+ echo $commit OK || exit 1
+ done
+ ) &&
+ (
+ for commit in merge^2 fourth-unsigned sixth-unsigned \
+ seventh-unsigned ninth-unsigned
+ do
+ test_must_fail git verify-commit $commit &&
+ git show --pretty=short --show-signature $commit >actual &&
+ ! grep "Good signature from" actual &&
+ ! grep "BAD signature from" actual &&
+ echo $commit OK || exit 1
+ done
+ ) &&
+ (
+ for commit in eighth-signed-alt
+ do
+ git show --pretty=short --show-signature $commit >actual &&
+ grep "Good signature from" actual &&
+ ! grep "BAD signature from" actual &&
+ grep "not certified" actual &&
+ echo $commit OK || exit 1
+ done
+ )
+'
+
+test_expect_success GPG 'verify-commit exits success on untrusted signature' '
+ git verify-commit eighth-signed-alt 2>actual &&
+ grep "Good signature from" actual &&
+ ! grep "BAD signature from" actual &&
+ grep "not certified" actual
+'
+
+test_expect_success GPG 'verify signatures with --raw' '
+ (
+ for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed
+ do
+ git verify-commit --raw $commit 2>actual &&
+ grep "GOODSIG" actual &&
+ ! grep "BADSIG" actual &&
+ echo $commit OK || exit 1
+ done
+ ) &&
+ (
+ for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned
+ do
+ test_must_fail git verify-commit --raw $commit 2>actual &&
+ ! grep "GOODSIG" actual &&
+ ! grep "BADSIG" actual &&
+ echo $commit OK || exit 1
+ done
+ ) &&
+ (
+ for commit in eighth-signed-alt
+ do
+ git verify-commit --raw $commit 2>actual &&
+ grep "GOODSIG" actual &&
+ ! grep "BADSIG" actual &&
+ grep "TRUST_UNDEFINED" actual &&
+ echo $commit OK || exit 1
+ done
+ )
+'
+
+test_expect_success GPG 'show signed commit with signature' '
+ git show -s initial >commit &&
+ git show -s --show-signature initial >show &&
+ git verify-commit -v initial >verify.1 2>verify.2 &&
+ git cat-file commit initial >cat &&
+ grep -v -e "gpg: " -e "Warning: " show >show.commit &&
+ grep -e "gpg: " -e "Warning: " show >show.gpg &&
+ grep -v "^ " cat | grep -v "^gpgsig " >cat.commit &&
+ test_cmp show.commit commit &&
+ test_cmp show.gpg verify.2 &&
+ test_cmp cat.commit verify.1
+'
+
+test_expect_success GPG 'detect fudged signature' '
+ git cat-file commit seventh-signed >raw &&
+
+ sed -e "s/seventh/7th forged/" raw >forged1 &&
+ git hash-object -w -t commit forged1 >forged1.commit &&
+ ! git verify-commit $(cat forged1.commit) &&
+ git show --pretty=short --show-signature $(cat forged1.commit) >actual1 &&
+ grep "BAD signature from" actual1 &&
+ ! grep "Good signature from" actual1
+'
+
+test_expect_success GPG 'detect fudged signature with NUL' '
+ git cat-file commit seventh-signed >raw &&
+ cat raw >forged2 &&
+ echo Qwik | tr "Q" "\000" >>forged2 &&
+ git hash-object -w -t commit forged2 >forged2.commit &&
+ ! git verify-commit $(cat forged2.commit) &&
+ git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
+ grep "BAD signature from" actual2 &&
+ ! grep "Good signature from" actual2
+'
+
+test_expect_success GPG 'amending already signed commit' '
+ git checkout fourth-signed^0 &&
+ git commit --amend -S --no-edit &&
+ git verify-commit HEAD &&
+ git show -s --show-signature HEAD >actual &&
+ grep "Good signature from" actual &&
+ ! grep "BAD signature from" actual
+'
+
+test_expect_success GPG 'show good signature with custom format' '
+ cat >expect <<-\EOF &&
+ G
+ 13B6F51ECDDE430D
+ C O Mitter <committer@example.com>
+ EOF
+ git log -1 --format="%G?%n%GK%n%GS" sixth-signed >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show bad signature with custom format' '
+ cat >expect <<-\EOF &&
+ B
+ 13B6F51ECDDE430D
+ C O Mitter <committer@example.com>
+ EOF
+ git log -1 --format="%G?%n%GK%n%GS" $(cat forged1.commit) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show unknown signature with custom format' '
+ cat >expect <<-\EOF &&
+ U
+ 61092E85B7227189
+ Eris Discordia <discord@example.net>
+ EOF
+ git log -1 --format="%G?%n%GK%n%GS" eighth-signed-alt >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show lack of signature with custom format' '
+ cat >expect <<-\EOF &&
+ N
+
+
+ EOF
+ git log -1 --format="%G?%n%GK%n%GS" seventh-unsigned >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t7511-status-index.sh b/t/t7511-status-index.sh
new file mode 100755
index 0000000..b5fdc04
--- /dev/null
+++ b/t/t7511-status-index.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='git status with certain file name lengths'
+
+. ./test-lib.sh
+
+files="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 w x y z"
+
+check() {
+ len=$1
+ prefix=$2
+
+ for i in $files
+ do
+ : >$prefix$i
+ done
+
+ test_expect_success "status, filename length $len" "
+ git add $prefix* &&
+ git status
+ "
+ rm $prefix* .git/index
+}
+
+check 1
+check 2 p
+check 3 px
+check 4 pre
+check 5 pref
+check 6 prefi
+check 7 prefix
+check 8 prefix-
+check 9 prefix-p
+check 10 prefix-pr
+check 11 prefix-pre
+check 12 prefix-pref
+check 13 prefix-prefi
+check 14 prefix-prefix
+check 15 prefix-prefix-
+check 16 prefix-prefix-p
+check 17 prefix-prefix-pr
+check 18 prefix-prefix-pre
+check 19 prefix-prefix-pref
+check 20 prefix-prefix-prefi
+check 21 prefix-prefix-prefix
+check 22 prefix-prefix-prefix-
+check 23 prefix-prefix-prefix-p
+check 24 prefix-prefix-prefix-pr
+
+test_done
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
new file mode 100755
index 0000000..49d19a3
--- /dev/null
+++ b/t/t7512-status-help.sh
@@ -0,0 +1,946 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Valentin Duperray, Lucien Kong, Franck Jonas,
+# Thomas Nguy, Khoi Nguyen
+# Grenoble INP Ensimag
+#
+
+test_description='git status advice'
+
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+set_fake_editor
+
+test_expect_success 'prepare for conflicts' '
+ git config --global advice.statusuoption false &&
+ test_commit init main.txt init &&
+ git branch conflicts &&
+ test_commit on_master main.txt on_master &&
+ git checkout conflicts &&
+ test_commit on_conflicts main.txt on_conflicts
+'
+
+
+test_expect_success 'status when conflicts unresolved' '
+ test_must_fail git merge master &&
+ cat >expected <<\EOF &&
+On branch conflicts
+You have unmerged paths.
+ (fix conflicts and run "git commit")
+
+Unmerged paths:
+ (use "git add <file>..." to mark resolution)
+
+ both modified: main.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status when conflicts resolved before commit' '
+ git reset --hard conflicts &&
+ test_must_fail git merge master &&
+ echo one >main.txt &&
+ git add main.txt &&
+ cat >expected <<\EOF &&
+On branch conflicts
+All conflicts fixed but you are still merging.
+ (use "git commit" to conclude merge)
+
+Changes to be committed:
+
+ modified: main.txt
+
+Untracked files not listed (use -u option to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'prepare for rebase conflicts' '
+ git reset --hard master &&
+ git checkout -b rebase_conflicts &&
+ test_commit one_rebase main.txt one &&
+ test_commit two_rebase main.txt two &&
+ test_commit three_rebase main.txt three
+'
+
+
+test_expect_success 'status when rebase in progress before resolving conflicts' '
+ test_when_finished "git rebase --abort" &&
+ ONTO=$(git rev-parse --short HEAD^^) &&
+ test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+ cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
+ (fix conflicts and then run "git rebase --continue")
+ (use "git rebase --skip" to skip this patch)
+ (use "git rebase --abort" to check out the original branch)
+
+Unmerged paths:
+ (use "git reset HEAD <file>..." to unstage)
+ (use "git add <file>..." to mark resolution)
+
+ both modified: main.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status when rebase in progress before rebase --continue' '
+ git reset --hard rebase_conflicts &&
+ test_when_finished "git rebase --abort" &&
+ ONTO=$(git rev-parse --short HEAD^^) &&
+ test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+ echo three >main.txt &&
+ git add main.txt &&
+ cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
+ (all conflicts fixed: run "git rebase --continue")
+
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ modified: main.txt
+
+Untracked files not listed (use -u option to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'prepare for rebase_i_conflicts' '
+ git reset --hard master &&
+ git checkout -b rebase_i_conflicts &&
+ test_commit one_unmerge main.txt one_unmerge &&
+ git branch rebase_i_conflicts_second &&
+ test_commit one_master main.txt one_master &&
+ git checkout rebase_i_conflicts_second &&
+ test_commit one_second main.txt one_second
+'
+
+
+test_expect_success 'status during rebase -i when conflicts unresolved' '
+ test_when_finished "git rebase --abort" &&
+ ONTO=$(git rev-parse --short rebase_i_conflicts) &&
+ LAST_COMMIT=$(git rev-parse --short rebase_i_conflicts_second) &&
+ test_must_fail git rebase -i rebase_i_conflicts &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last command done (1 command done):
+ pick $LAST_COMMIT one_second
+No commands remaining.
+You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
+ (fix conflicts and then run "git rebase --continue")
+ (use "git rebase --skip" to skip this patch)
+ (use "git rebase --abort" to check out the original branch)
+
+Unmerged paths:
+ (use "git reset HEAD <file>..." to unstage)
+ (use "git add <file>..." to mark resolution)
+
+ both modified: main.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status during rebase -i after resolving conflicts' '
+ git reset --hard rebase_i_conflicts_second &&
+ test_when_finished "git rebase --abort" &&
+ ONTO=$(git rev-parse --short rebase_i_conflicts) &&
+ LAST_COMMIT=$(git rev-parse --short rebase_i_conflicts_second) &&
+ test_must_fail git rebase -i rebase_i_conflicts &&
+ git add main.txt &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last command done (1 command done):
+ pick $LAST_COMMIT one_second
+No commands remaining.
+You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
+ (all conflicts fixed: run "git rebase --continue")
+
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ modified: main.txt
+
+Untracked files not listed (use -u option to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status when rebasing -i in edit mode' '
+ git reset --hard master &&
+ git checkout -b rebase_i_edit &&
+ test_commit one_rebase_i main.txt one &&
+ test_commit two_rebase_i main.txt two &&
+ COMMIT2=$(git rev-parse --short rebase_i_edit) &&
+ test_commit three_rebase_i main.txt three &&
+ COMMIT3=$(git rev-parse --short rebase_i_edit) &&
+ FAKE_LINES="1 edit 2" &&
+ export FAKE_LINES &&
+ test_when_finished "git rebase --abort" &&
+ ONTO=$(git rev-parse --short HEAD~2) &&
+ git rebase -i HEAD~2 &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last commands done (2 commands done):
+ pick $COMMIT2 two_rebase_i
+ edit $COMMIT3 three_rebase_i
+No commands remaining.
+You are currently editing a commit while rebasing branch '\''rebase_i_edit'\'' on '\''$ONTO'\''.
+ (use "git commit --amend" to amend the current commit)
+ (use "git rebase --continue" once you are satisfied with your changes)
+
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status when splitting a commit' '
+ git reset --hard master &&
+ git checkout -b split_commit &&
+ test_commit one_split main.txt one &&
+ test_commit two_split main.txt two &&
+ COMMIT2=$(git rev-parse --short split_commit) &&
+ test_commit three_split main.txt three &&
+ COMMIT3=$(git rev-parse --short split_commit) &&
+ test_commit four_split main.txt four &&
+ COMMIT4=$(git rev-parse --short split_commit) &&
+ FAKE_LINES="1 edit 2 3" &&
+ export FAKE_LINES &&
+ test_when_finished "git rebase --abort" &&
+ ONTO=$(git rev-parse --short HEAD~3) &&
+ git rebase -i HEAD~3 &&
+ git reset HEAD^ &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last commands done (2 commands done):
+ pick $COMMIT2 two_split
+ edit $COMMIT3 three_split
+Next command to do (1 remaining command):
+ pick $COMMIT4 four_split
+ (use "git rebase --edit-todo" to view and edit)
+You are currently splitting a commit while rebasing branch '\''split_commit'\'' on '\''$ONTO'\''.
+ (Once your working directory is clean, run "git rebase --continue")
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: main.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status after editing the last commit with --amend during a rebase -i' '
+ git reset --hard master &&
+ git checkout -b amend_last &&
+ test_commit one_amend main.txt one &&
+ test_commit two_amend main.txt two &&
+ test_commit three_amend main.txt three &&
+ COMMIT3=$(git rev-parse --short amend_last) &&
+ test_commit four_amend main.txt four &&
+ COMMIT4=$(git rev-parse --short amend_last) &&
+ FAKE_LINES="1 2 edit 3" &&
+ export FAKE_LINES &&
+ test_when_finished "git rebase --abort" &&
+ ONTO=$(git rev-parse --short HEAD~3) &&
+ git rebase -i HEAD~3 &&
+ git commit --amend -m "foo" &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last commands done (3 commands done):
+ pick $COMMIT3 three_amend
+ edit $COMMIT4 four_amend
+ (see more in file .git/rebase-merge/done)
+No commands remaining.
+You are currently editing a commit while rebasing branch '\''amend_last'\'' on '\''$ONTO'\''.
+ (use "git commit --amend" to amend the current commit)
+ (use "git rebase --continue" once you are satisfied with your changes)
+
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'prepare for several edits' '
+ git reset --hard master &&
+ git checkout -b several_edits &&
+ test_commit one_edits main.txt one &&
+ test_commit two_edits main.txt two &&
+ test_commit three_edits main.txt three &&
+ test_commit four_edits main.txt four
+'
+
+
+test_expect_success 'status: (continue first edit) second edit' '
+ FAKE_LINES="edit 1 edit 2 3" &&
+ export FAKE_LINES &&
+ test_when_finished "git rebase --abort" &&
+ COMMIT2=$(git rev-parse --short several_edits^^) &&
+ COMMIT3=$(git rev-parse --short several_edits^) &&
+ COMMIT4=$(git rev-parse --short several_edits) &&
+ ONTO=$(git rev-parse --short HEAD~3) &&
+ git rebase -i HEAD~3 &&
+ git rebase --continue &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last commands done (2 commands done):
+ edit $COMMIT2 two_edits
+ edit $COMMIT3 three_edits
+Next command to do (1 remaining command):
+ pick $COMMIT4 four_edits
+ (use "git rebase --edit-todo" to view and edit)
+You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+ (use "git commit --amend" to amend the current commit)
+ (use "git rebase --continue" once you are satisfied with your changes)
+
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status: (continue first edit) second edit and split' '
+ git reset --hard several_edits &&
+ FAKE_LINES="edit 1 edit 2 3" &&
+ export FAKE_LINES &&
+ test_when_finished "git rebase --abort" &&
+ COMMIT2=$(git rev-parse --short several_edits^^) &&
+ COMMIT3=$(git rev-parse --short several_edits^) &&
+ COMMIT4=$(git rev-parse --short several_edits) &&
+ ONTO=$(git rev-parse --short HEAD~3) &&
+ git rebase -i HEAD~3 &&
+ git rebase --continue &&
+ git reset HEAD^ &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last commands done (2 commands done):
+ edit $COMMIT2 two_edits
+ edit $COMMIT3 three_edits
+Next command to do (1 remaining command):
+ pick $COMMIT4 four_edits
+ (use "git rebase --edit-todo" to view and edit)
+You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+ (Once your working directory is clean, run "git rebase --continue")
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: main.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status: (continue first edit) second edit and amend' '
+ git reset --hard several_edits &&
+ FAKE_LINES="edit 1 edit 2 3" &&
+ export FAKE_LINES &&
+ test_when_finished "git rebase --abort" &&
+ COMMIT2=$(git rev-parse --short several_edits^^) &&
+ COMMIT3=$(git rev-parse --short several_edits^) &&
+ COMMIT4=$(git rev-parse --short several_edits) &&
+ ONTO=$(git rev-parse --short HEAD~3) &&
+ git rebase -i HEAD~3 &&
+ git rebase --continue &&
+ git commit --amend -m "foo" &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last commands done (2 commands done):
+ edit $COMMIT2 two_edits
+ edit $COMMIT3 three_edits
+Next command to do (1 remaining command):
+ pick $COMMIT4 four_edits
+ (use "git rebase --edit-todo" to view and edit)
+You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+ (use "git commit --amend" to amend the current commit)
+ (use "git rebase --continue" once you are satisfied with your changes)
+
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status: (amend first edit) second edit' '
+ git reset --hard several_edits &&
+ FAKE_LINES="edit 1 edit 2 3" &&
+ export FAKE_LINES &&
+ test_when_finished "git rebase --abort" &&
+ COMMIT2=$(git rev-parse --short several_edits^^) &&
+ COMMIT3=$(git rev-parse --short several_edits^) &&
+ COMMIT4=$(git rev-parse --short several_edits) &&
+ ONTO=$(git rev-parse --short HEAD~3) &&
+ git rebase -i HEAD~3 &&
+ git commit --amend -m "a" &&
+ git rebase --continue &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last commands done (2 commands done):
+ edit $COMMIT2 two_edits
+ edit $COMMIT3 three_edits
+Next command to do (1 remaining command):
+ pick $COMMIT4 four_edits
+ (use "git rebase --edit-todo" to view and edit)
+You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+ (use "git commit --amend" to amend the current commit)
+ (use "git rebase --continue" once you are satisfied with your changes)
+
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status: (amend first edit) second edit and split' '
+ git reset --hard several_edits &&
+ FAKE_LINES="edit 1 edit 2 3" &&
+ export FAKE_LINES &&
+ test_when_finished "git rebase --abort" &&
+ ONTO=$(git rev-parse --short HEAD~3) &&
+ COMMIT2=$(git rev-parse --short several_edits^^) &&
+ COMMIT3=$(git rev-parse --short several_edits^) &&
+ COMMIT4=$(git rev-parse --short several_edits) &&
+ git rebase -i HEAD~3 &&
+ git commit --amend -m "b" &&
+ git rebase --continue &&
+ git reset HEAD^ &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last commands done (2 commands done):
+ edit $COMMIT2 two_edits
+ edit $COMMIT3 three_edits
+Next command to do (1 remaining command):
+ pick $COMMIT4 four_edits
+ (use "git rebase --edit-todo" to view and edit)
+You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+ (Once your working directory is clean, run "git rebase --continue")
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: main.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status: (amend first edit) second edit and amend' '
+ git reset --hard several_edits &&
+ FAKE_LINES="edit 1 edit 2 3" &&
+ export FAKE_LINES &&
+ test_when_finished "git rebase --abort" &&
+ COMMIT2=$(git rev-parse --short several_edits^^) &&
+ COMMIT3=$(git rev-parse --short several_edits^) &&
+ COMMIT4=$(git rev-parse --short several_edits) &&
+ ONTO=$(git rev-parse --short HEAD~3) &&
+ git rebase -i HEAD~3 &&
+ git commit --amend -m "c" &&
+ git rebase --continue &&
+ git commit --amend -m "d" &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last commands done (2 commands done):
+ edit $COMMIT2 two_edits
+ edit $COMMIT3 three_edits
+Next command to do (1 remaining command):
+ pick $COMMIT4 four_edits
+ (use "git rebase --edit-todo" to view and edit)
+You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+ (use "git commit --amend" to amend the current commit)
+ (use "git rebase --continue" once you are satisfied with your changes)
+
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status: (split first edit) second edit' '
+ git reset --hard several_edits &&
+ FAKE_LINES="edit 1 edit 2 3" &&
+ export FAKE_LINES &&
+ test_when_finished "git rebase --abort" &&
+ COMMIT2=$(git rev-parse --short several_edits^^) &&
+ COMMIT3=$(git rev-parse --short several_edits^) &&
+ COMMIT4=$(git rev-parse --short several_edits) &&
+ ONTO=$(git rev-parse --short HEAD~3) &&
+ git rebase -i HEAD~3 &&
+ git reset HEAD^ &&
+ git add main.txt &&
+ git commit -m "e" &&
+ git rebase --continue &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last commands done (2 commands done):
+ edit $COMMIT2 two_edits
+ edit $COMMIT3 three_edits
+Next command to do (1 remaining command):
+ pick $COMMIT4 four_edits
+ (use "git rebase --edit-todo" to view and edit)
+You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+ (use "git commit --amend" to amend the current commit)
+ (use "git rebase --continue" once you are satisfied with your changes)
+
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status: (split first edit) second edit and split' '
+ git reset --hard several_edits &&
+ FAKE_LINES="edit 1 edit 2 3" &&
+ export FAKE_LINES &&
+ test_when_finished "git rebase --abort" &&
+ COMMIT2=$(git rev-parse --short several_edits^^) &&
+ COMMIT3=$(git rev-parse --short several_edits^) &&
+ COMMIT4=$(git rev-parse --short several_edits) &&
+ ONTO=$(git rev-parse --short HEAD~3) &&
+ git rebase -i HEAD~3 &&
+ git reset HEAD^ &&
+ git add main.txt &&
+ git commit --amend -m "f" &&
+ git rebase --continue &&
+ git reset HEAD^ &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last commands done (2 commands done):
+ edit $COMMIT2 two_edits
+ edit $COMMIT3 three_edits
+Next command to do (1 remaining command):
+ pick $COMMIT4 four_edits
+ (use "git rebase --edit-todo" to view and edit)
+You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+ (Once your working directory is clean, run "git rebase --continue")
+
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: main.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status: (split first edit) second edit and amend' '
+ git reset --hard several_edits &&
+ FAKE_LINES="edit 1 edit 2 3" &&
+ export FAKE_LINES &&
+ test_when_finished "git rebase --abort" &&
+ COMMIT2=$(git rev-parse --short several_edits^^) &&
+ COMMIT3=$(git rev-parse --short several_edits^) &&
+ COMMIT4=$(git rev-parse --short several_edits) &&
+ ONTO=$(git rev-parse --short HEAD~3) &&
+ git rebase -i HEAD~3 &&
+ git reset HEAD^ &&
+ git add main.txt &&
+ git commit --amend -m "g" &&
+ git rebase --continue &&
+ git commit --amend -m "h" &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last commands done (2 commands done):
+ edit $COMMIT2 two_edits
+ edit $COMMIT3 three_edits
+Next command to do (1 remaining command):
+ pick $COMMIT4 four_edits
+ (use "git rebase --edit-todo" to view and edit)
+You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+ (use "git commit --amend" to amend the current commit)
+ (use "git rebase --continue" once you are satisfied with your changes)
+
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'prepare am_session' '
+ git reset --hard master &&
+ git checkout -b am_session &&
+ test_commit one_am one.txt "one" &&
+ test_commit two_am two.txt "two" &&
+ test_commit three_am three.txt "three"
+'
+
+
+test_expect_success 'status in an am session: file already exists' '
+ git checkout -b am_already_exists &&
+ test_when_finished "rm Maildir/* && git am --abort" &&
+ git format-patch -1 -oMaildir &&
+ test_must_fail git am Maildir/*.patch &&
+ cat >expected <<\EOF &&
+On branch am_already_exists
+You are in the middle of an am session.
+ (fix conflicts and then run "git am --continue")
+ (use "git am --skip" to skip this patch)
+ (use "git am --abort" to restore the original branch)
+
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status in an am session: file does not exist' '
+ git reset --hard am_session &&
+ git checkout -b am_not_exists &&
+ git rm three.txt &&
+ git commit -m "delete three.txt" &&
+ test_when_finished "rm Maildir/* && git am --abort" &&
+ git format-patch -1 -oMaildir &&
+ test_must_fail git am Maildir/*.patch &&
+ cat >expected <<\EOF &&
+On branch am_not_exists
+You are in the middle of an am session.
+ (fix conflicts and then run "git am --continue")
+ (use "git am --skip" to skip this patch)
+ (use "git am --abort" to restore the original branch)
+
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status in an am session: empty patch' '
+ git reset --hard am_session &&
+ git checkout -b am_empty &&
+ test_when_finished "rm Maildir/* && git am --abort" &&
+ git format-patch -3 -oMaildir &&
+ git rm one.txt two.txt three.txt &&
+ git commit -m "delete all am_empty" &&
+ echo error >Maildir/0002-two_am.patch &&
+ test_must_fail git am Maildir/*.patch &&
+ cat >expected <<\EOF &&
+On branch am_empty
+You are in the middle of an am session.
+The current patch is empty.
+ (use "git am --skip" to skip this patch)
+ (use "git am --abort" to restore the original branch)
+
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status when bisecting' '
+ git reset --hard master &&
+ git checkout -b bisect &&
+ test_commit one_bisect main.txt one &&
+ test_commit two_bisect main.txt two &&
+ test_commit three_bisect main.txt three &&
+ test_when_finished "git bisect reset" &&
+ git bisect start &&
+ git bisect bad &&
+ git bisect good one_bisect &&
+ TGT=$(git rev-parse --short two_bisect) &&
+ cat >expected <<EOF &&
+HEAD detached at $TGT
+You are currently bisecting, started from branch '\''bisect'\''.
+ (use "git bisect reset" to get back to the original branch)
+
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status when rebase conflicts with statushints disabled' '
+ git reset --hard master &&
+ git checkout -b statushints_disabled &&
+ test_when_finished "git config --local advice.statushints true" &&
+ git config --local advice.statushints false &&
+ test_commit one_statushints main.txt one &&
+ test_commit two_statushints main.txt two &&
+ test_commit three_statushints main.txt three &&
+ test_when_finished "git rebase --abort" &&
+ ONTO=$(git rev-parse --short HEAD^^) &&
+ test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+ cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
+
+Unmerged paths:
+ both modified: main.txt
+
+no changes added to commit
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'prepare for cherry-pick conflicts' '
+ git reset --hard master &&
+ git checkout -b cherry_branch &&
+ test_commit one_cherry main.txt one &&
+ test_commit two_cherries main.txt two &&
+ git checkout -b cherry_branch_second &&
+ test_commit second_cherry main.txt second &&
+ git checkout cherry_branch &&
+ test_commit three_cherries main.txt three
+'
+
+
+test_expect_success 'status when cherry-picking before resolving conflicts' '
+ test_when_finished "git cherry-pick --abort" &&
+ test_must_fail git cherry-pick cherry_branch_second &&
+ TO_CHERRY_PICK=$(git rev-parse --short CHERRY_PICK_HEAD) &&
+ cat >expected <<EOF &&
+On branch cherry_branch
+You are currently cherry-picking commit $TO_CHERRY_PICK.
+ (fix conflicts and run "git cherry-pick --continue")
+ (use "git cherry-pick --abort" to cancel the cherry-pick operation)
+
+Unmerged paths:
+ (use "git add <file>..." to mark resolution)
+
+ both modified: main.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status when cherry-picking after resolving conflicts' '
+ git reset --hard cherry_branch &&
+ test_when_finished "git cherry-pick --abort" &&
+ test_must_fail git cherry-pick cherry_branch_second &&
+ TO_CHERRY_PICK=$(git rev-parse --short CHERRY_PICK_HEAD) &&
+ echo end >main.txt &&
+ git add main.txt &&
+ cat >expected <<EOF &&
+On branch cherry_branch
+You are currently cherry-picking commit $TO_CHERRY_PICK.
+ (all conflicts fixed: run "git cherry-pick --continue")
+ (use "git cherry-pick --abort" to cancel the cherry-pick operation)
+
+Changes to be committed:
+
+ modified: main.txt
+
+Untracked files not listed (use -u option to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+test_expect_success 'status showing detached at and from a tag' '
+ test_commit atag tagging &&
+ git checkout atag &&
+ cat >expected <<\EOF &&
+HEAD detached at atag
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual &&
+
+ git reset --hard HEAD^ &&
+ cat >expected <<\EOF &&
+HEAD detached from atag
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+test_expect_success 'status while reverting commit (conflicts)' '
+ git checkout master &&
+ echo before >to-revert.txt &&
+ test_commit before to-revert.txt &&
+ echo old >to-revert.txt &&
+ test_commit old to-revert.txt &&
+ echo new >to-revert.txt &&
+ test_commit new to-revert.txt &&
+ TO_REVERT=$(git rev-parse --short HEAD^) &&
+ test_must_fail git revert $TO_REVERT &&
+ cat >expected <<EOF &&
+On branch master
+You are currently reverting commit $TO_REVERT.
+ (fix conflicts and run "git revert --continue")
+ (use "git revert --abort" to cancel the revert operation)
+
+Unmerged paths:
+ (use "git reset HEAD <file>..." to unstage)
+ (use "git add <file>..." to mark resolution)
+
+ both modified: to-revert.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+test_expect_success 'status while reverting commit (conflicts resolved)' '
+ echo reverted >to-revert.txt &&
+ git add to-revert.txt &&
+ cat >expected <<EOF &&
+On branch master
+You are currently reverting commit $TO_REVERT.
+ (all conflicts fixed: run "git revert --continue")
+ (use "git revert --abort" to cancel the revert operation)
+
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ modified: to-revert.txt
+
+Untracked files not listed (use -u option to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+test_expect_success 'status after reverting commit' '
+ git revert --continue &&
+ cat >expected <<\EOF &&
+On branch master
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+test_expect_success 'prepare for different number of commits rebased' '
+ git reset --hard master &&
+ git checkout -b several_commits &&
+ test_commit one_commit main.txt one &&
+ test_commit two_commit main.txt two &&
+ test_commit three_commit main.txt three &&
+ test_commit four_commit main.txt four
+'
+
+test_expect_success 'status: one command done nothing remaining' '
+ FAKE_LINES="exec_exit_15" &&
+ export FAKE_LINES &&
+ test_when_finished "git rebase --abort" &&
+ ONTO=$(git rev-parse --short HEAD~3) &&
+ test_must_fail git rebase -i HEAD~3 &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last command done (1 command done):
+ exec exit 15
+No commands remaining.
+You are currently editing a commit while rebasing branch '\''several_commits'\'' on '\''$ONTO'\''.
+ (use "git commit --amend" to amend the current commit)
+ (use "git rebase --continue" once you are satisfied with your changes)
+
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+test_expect_success 'status: two commands done with some white lines in done file' '
+ FAKE_LINES="1 > exec_exit_15 2 3" &&
+ export FAKE_LINES &&
+ test_when_finished "git rebase --abort" &&
+ ONTO=$(git rev-parse --short HEAD~3) &&
+ COMMIT4=$(git rev-parse --short HEAD) &&
+ COMMIT3=$(git rev-parse --short HEAD^) &&
+ COMMIT2=$(git rev-parse --short HEAD^^) &&
+ test_must_fail git rebase -i HEAD~3 &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last commands done (2 commands done):
+ pick $COMMIT2 two_commit
+ exec exit 15
+Next commands to do (2 remaining commands):
+ pick $COMMIT3 three_commit
+ pick $COMMIT4 four_commit
+ (use "git rebase --edit-todo" to view and edit)
+You are currently editing a commit while rebasing branch '\''several_commits'\'' on '\''$ONTO'\''.
+ (use "git commit --amend" to amend the current commit)
+ (use "git rebase --continue" once you are satisfied with your changes)
+
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+test_expect_success 'status: two remaining commands with some white lines in todo file' '
+ FAKE_LINES="1 2 exec_exit_15 3 > 4" &&
+ export FAKE_LINES &&
+ test_when_finished "git rebase --abort" &&
+ ONTO=$(git rev-parse --short HEAD~4) &&
+ COMMIT4=$(git rev-parse --short HEAD) &&
+ COMMIT3=$(git rev-parse --short HEAD^) &&
+ COMMIT2=$(git rev-parse --short HEAD^^) &&
+ test_must_fail git rebase -i HEAD~4 &&
+ cat >expected <<EOF &&
+interactive rebase in progress; onto $ONTO
+Last commands done (3 commands done):
+ pick $COMMIT2 two_commit
+ exec exit 15
+ (see more in file .git/rebase-merge/done)
+Next commands to do (2 remaining commands):
+ pick $COMMIT3 three_commit
+ pick $COMMIT4 four_commit
+ (use "git rebase --edit-todo" to view and edit)
+You are currently editing a commit while rebasing branch '\''several_commits'\'' on '\''$ONTO'\''.
+ (use "git commit --amend" to amend the current commit)
+ (use "git rebase --continue" once you are satisfied with your changes)
+
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+test_done
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
new file mode 100755
index 0000000..aee785c
--- /dev/null
+++ b/t/t7513-interpret-trailers.sh
@@ -0,0 +1,962 @@
+#!/bin/sh
+#
+# Copyright (c) 2013, 2014 Christian Couder
+#
+
+test_description='git interpret-trailers'
+
+. ./test-lib.sh
+
+# When we want one trailing space at the end of each line, let's use sed
+# to make sure that these spaces are not removed by any automatic tool.
+
+test_expect_success 'setup' '
+ : >empty &&
+ cat >basic_message <<-\EOF &&
+ subject
+
+ body
+ EOF
+ cat >complex_message_body <<-\EOF &&
+ my subject
+
+ my body which is long
+ and contains some special
+ chars like : = ? !
+
+ EOF
+ sed -e "s/ Z\$/ /" >complex_message_trailers <<-\EOF &&
+ Fixes: Z
+ Acked-by: Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ cat >basic_patch <<-\EOF
+ ---
+ foo.txt | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+ diff --git a/foo.txt b/foo.txt
+ index 0353767..1d91aa1 100644
+ --- a/foo.txt
+ +++ b/foo.txt
+ @@ -1,3 +1,3 @@
+
+ -bar
+ +baz
+
+ --
+ 1.9.rc0.11.ga562ddc
+
+ EOF
+'
+
+test_expect_success 'without config' '
+ sed -e "s/ Z\$/ /" >expected <<-\EOF &&
+
+ ack: Peff
+ Reviewed-by: Z
+ Acked-by: Johan
+ EOF
+ git interpret-trailers --trailer "ack = Peff" --trailer "Reviewed-by" \
+ --trailer "Acked-by: Johan" empty >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'without config in another order' '
+ sed -e "s/ Z\$/ /" >expected <<-\EOF &&
+
+ Acked-by: Johan
+ Reviewed-by: Z
+ ack: Peff
+ EOF
+ git interpret-trailers --trailer "Acked-by: Johan" --trailer "Reviewed-by" \
+ --trailer "ack = Peff" empty >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '--trim-empty without config' '
+ cat >expected <<-\EOF &&
+
+ ack: Peff
+ Acked-by: Johan
+ EOF
+ git interpret-trailers --trim-empty --trailer ack=Peff \
+ --trailer "Reviewed-by" --trailer "Acked-by: Johan" \
+ --trailer "sob:" empty >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with config option on the command line' '
+ cat >expected <<-\EOF &&
+
+ Acked-by: Johan
+ Reviewed-by: Peff
+ EOF
+ { echo; echo "Acked-by: Johan"; } |
+ git -c "trailer.Acked-by.ifexists=addifdifferent" interpret-trailers \
+ --trailer "Reviewed-by: Peff" --trailer "Acked-by: Johan" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with only a title in the message' '
+ cat >expected <<-\EOF &&
+ area: change
+
+ Reviewed-by: Peff
+ Acked-by: Johan
+ EOF
+ echo "area: change" |
+ git interpret-trailers --trailer "Reviewed-by: Peff" \
+ --trailer "Acked-by: Johan" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with multiline title in the message' '
+ cat >expected <<-\EOF &&
+ place of
+ code: change
+
+ Reviewed-by: Peff
+ Acked-by: Johan
+ EOF
+ printf "%s\n" "place of" "code: change" |
+ git interpret-trailers --trailer "Reviewed-by: Peff" \
+ --trailer "Acked-by: Johan" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with config setup' '
+ git config trailer.ack.key "Acked-by: " &&
+ cat >expected <<-\EOF &&
+
+ Acked-by: Peff
+ EOF
+ git interpret-trailers --trim-empty --trailer "ack = Peff" empty >actual &&
+ test_cmp expected actual &&
+ git interpret-trailers --trim-empty --trailer "Acked-by = Peff" empty >actual &&
+ test_cmp expected actual &&
+ git interpret-trailers --trim-empty --trailer "Acked-by :Peff" empty >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with config setup and ":=" as separators' '
+ git config trailer.separators ":=" &&
+ git config trailer.ack.key "Acked-by= " &&
+ cat >expected <<-\EOF &&
+
+ Acked-by= Peff
+ EOF
+ git interpret-trailers --trim-empty --trailer "ack = Peff" empty >actual &&
+ test_cmp expected actual &&
+ git interpret-trailers --trim-empty --trailer "Acked-by= Peff" empty >actual &&
+ test_cmp expected actual &&
+ git interpret-trailers --trim-empty --trailer "Acked-by : Peff" empty >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with config setup and "%" as separators' '
+ git config trailer.separators "%" &&
+ cat >expected <<-\EOF &&
+
+ bug% 42
+ count% 10
+ bug% 422
+ EOF
+ git interpret-trailers --trim-empty --trailer "bug = 42" \
+ --trailer count%10 --trailer "test: stuff" \
+ --trailer "bug % 422" empty >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with "%" as separators and a message with trailers' '
+ cat >special_message <<-\EOF &&
+ Special Message
+
+ bug% 42
+ count% 10
+ bug% 422
+ EOF
+ cat >expected <<-\EOF &&
+ Special Message
+
+ bug% 42
+ count% 10
+ bug% 422
+ count% 100
+ EOF
+ git interpret-trailers --trailer count%100 \
+ special_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with config setup and ":=#" as separators' '
+ git config trailer.separators ":=#" &&
+ git config trailer.bug.key "Bug #" &&
+ cat >expected <<-\EOF &&
+
+ Bug #42
+ EOF
+ git interpret-trailers --trim-empty --trailer "bug = 42" empty >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with commit basic message' '
+ cat basic_message >expected &&
+ echo >>expected &&
+ git interpret-trailers <basic_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with basic patch' '
+ cat basic_message >input &&
+ cat basic_patch >>input &&
+ cat basic_message >expected &&
+ echo >>expected &&
+ cat basic_patch >>expected &&
+ git interpret-trailers <input >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with commit complex message as argument' '
+ cat complex_message_body complex_message_trailers >complex_message &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with 2 files arguments' '
+ cat basic_message >>expected &&
+ echo >>expected &&
+ cat basic_patch >>expected &&
+ git interpret-trailers complex_message input >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with message that has comments' '
+ cat basic_message >message_with_comments &&
+ sed -e "s/ Z\$/ /" >>message_with_comments <<-\EOF &&
+ # comment
+
+ # other comment
+ Cc: Z
+ # yet another comment
+ Reviewed-by: Johan
+ Reviewed-by: Z
+ # last comment
+
+ EOF
+ cat basic_patch >>message_with_comments &&
+ cat basic_message >expected &&
+ cat >>expected <<-\EOF &&
+ # comment
+
+ Reviewed-by: Johan
+ Cc: Peff
+ # last comment
+
+ EOF
+ cat basic_patch >>expected &&
+ git interpret-trailers --trim-empty --trailer "Cc: Peff" message_with_comments >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with message that has an old style conflict block' '
+ cat basic_message >message_with_comments &&
+ sed -e "s/ Z\$/ /" >>message_with_comments <<-\EOF &&
+ # comment
+
+ # other comment
+ Cc: Z
+ # yet another comment
+ Reviewed-by: Johan
+ Reviewed-by: Z
+ # last comment
+
+ Conflicts:
+
+ EOF
+ cat basic_message >expected &&
+ cat >>expected <<-\EOF &&
+ # comment
+
+ Reviewed-by: Johan
+ Cc: Peff
+ # last comment
+
+ Conflicts:
+
+ EOF
+ git interpret-trailers --trim-empty --trailer "Cc: Peff" message_with_comments >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with commit complex message and trailer args' '
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ Acked-by= Peff
+ Bug #42
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "bug: 42" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with complex patch, args and --trim-empty' '
+ cat complex_message >complex_patch &&
+ cat basic_patch >>complex_patch &&
+ cat complex_message_body >expected &&
+ cat >>expected <<-\EOF &&
+ Acked-by= Peff
+ Bug #42
+ EOF
+ cat basic_patch >>expected &&
+ git interpret-trailers --trim-empty --trailer "ack: Peff" \
+ --trailer "bug: 42" <complex_patch >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'in-place editing with basic patch' '
+ cat basic_message >message &&
+ cat basic_patch >>message &&
+ cat basic_message >expected &&
+ echo >>expected &&
+ cat basic_patch >>expected &&
+ git interpret-trailers --in-place message &&
+ test_cmp expected message
+'
+
+test_expect_success 'in-place editing with additional trailer' '
+ cat basic_message >message &&
+ cat basic_patch >>message &&
+ cat basic_message >expected &&
+ echo >>expected &&
+ cat >>expected <<-\EOF &&
+ Reviewed-by: Alice
+ EOF
+ cat basic_patch >>expected &&
+ git interpret-trailers --trailer "Reviewed-by: Alice" --in-place message &&
+ test_cmp expected message
+'
+
+test_expect_success 'in-place editing on stdin disallowed' '
+ test_must_fail git interpret-trailers --trailer "Reviewed-by: Alice" --in-place < basic_message
+'
+
+test_expect_success 'in-place editing on non-existing file' '
+ test_must_fail git interpret-trailers --trailer "Reviewed-by: Alice" --in-place nonexisting &&
+ test_path_is_missing nonexisting
+'
+
+test_expect_success POSIXPERM,SANITY "in-place editing doesn't clobber original file on error" '
+ cat basic_message >message &&
+ chmod -r message &&
+ test_must_fail git interpret-trailers --trailer "Reviewed-by: Alice" --in-place message &&
+ chmod +r message &&
+ test_cmp message basic_message
+'
+
+test_expect_success 'using "where = before"' '
+ git config trailer.bug.where "before" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ Acked-by= Peff
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "bug: 42" complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "where = after"' '
+ git config trailer.ack.where "after" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "bug: 42" complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "where = end"' '
+ git config trailer.review.key "Reviewed-by" &&
+ git config trailer.review.where "end" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ Reviewed-by: Junio
+ Reviewed-by: Johannes
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "Reviewed-by: Junio" --trailer "Reviewed-by: Johannes" \
+ complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "where = start"' '
+ git config trailer.review.key "Reviewed-by" &&
+ git config trailer.review.where "start" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Reviewed-by: Johannes
+ Reviewed-by: Junio
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "Reviewed-by: Junio" --trailer "Reviewed-by: Johannes" \
+ complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "where = before" for a token in the middle of the message' '
+ git config trailer.review.key "Reviewed-by:" &&
+ git config trailer.review.where "before" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Reviewed-by:Johan
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "bug: 42" \
+ --trailer "review: Johan" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "where = before" and --trim-empty' '
+ cat complex_message_body >expected &&
+ cat >>expected <<-\EOF &&
+ Bug #46
+ Bug #42
+ Acked-by= Peff
+ Reviewed-by:Johan
+ EOF
+ git interpret-trailers --trim-empty --trailer "ack: Peff" \
+ --trailer "bug: 42" --trailer "review: Johan" \
+ --trailer "Bug: 46" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'the default is "ifExists = addIfDifferentNeighbor"' '
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "review:" \
+ --trailer "ack: Junio" --trailer "bug: 42" --trailer "ack: Peff" \
+ --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'default "ifExists" is now "addIfDifferent"' '
+ git config trailer.ifexists "addIfDifferent" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Acked-by= Junio
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "review:" \
+ --trailer "ack: Junio" --trailer "bug: 42" --trailer "ack: Peff" \
+ --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = addIfDifferent" with "where = end"' '
+ git config trailer.ack.ifExists "addIfDifferent" &&
+ git config trailer.ack.where "end" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Acked-by= Peff
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "review:" \
+ --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = addIfDifferent" with "where = before"' '
+ git config trailer.ack.ifExists "addIfDifferent" &&
+ git config trailer.ack.where "before" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Peff
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "review:" \
+ --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = end"' '
+ git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+ git config trailer.ack.where "end" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Acked-by= Peff
+ Acked-by= Junio
+ Tested-by: Jakub
+ Acked-by= Junio
+ Acked-by= Peff
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "review:" \
+ --trailer "ack: Junio" --trailer "bug: 42" \
+ --trailer "Tested-by: Jakub" --trailer "ack: Junio" \
+ --trailer "ack: Junio" --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = after"' '
+ git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+ git config trailer.ack.where "after" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ Tested-by: Jakub
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "review:" \
+ --trailer "ack: Junio" --trailer "bug: 42" \
+ --trailer "Tested-by: Jakub" --trailer "ack: Junio" \
+ --trailer "ack: Junio" --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = addIfDifferentNeighbor" and --trim-empty' '
+ git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+ cat complex_message_body >expected &&
+ cat >>expected <<-\EOF &&
+ Bug #42
+ Acked-by= Peff
+ Acked-by= Junio
+ Acked-by= Peff
+ EOF
+ git interpret-trailers --trim-empty --trailer "ack: Peff" \
+ --trailer "Acked-by= Peff" --trailer "review:" \
+ --trailer "ack: Junio" --trailer "bug: 42" \
+ --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = add" with "where = end"' '
+ git config trailer.ack.ifExists "add" &&
+ git config trailer.ack.where "end" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Acked-by= Peff
+ Acked-by= Peff
+ Tested-by: Jakub
+ Acked-by= Junio
+ Tested-by: Johannes
+ Acked-by= Peff
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "Acked-by= Peff" --trailer "review:" \
+ --trailer "Tested-by: Jakub" --trailer "ack: Junio" \
+ --trailer "bug: 42" --trailer "Tested-by: Johannes" \
+ --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = add" with "where = after"' '
+ git config trailer.ack.ifExists "add" &&
+ git config trailer.ack.where "after" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Acked-by= Peff
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "Acked-by= Peff" --trailer "review:" \
+ --trailer "ack: Junio" --trailer "bug: 42" \
+ --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = replace"' '
+ git config trailer.fix.key "Fixes: " &&
+ git config trailer.fix.ifExists "replace" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ Fixes: 22
+ EOF
+ git interpret-trailers --trailer "review:" \
+ --trailer "fix=53" --trailer "ack: Junio" --trailer "fix=22" \
+ --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = replace" with "where = after"' '
+ git config trailer.fix.where "after" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: 22
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" \
+ --trailer "fix=53" --trailer "ack: Junio" --trailer "fix=22" \
+ --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = doNothing"' '
+ git config trailer.fix.ifExists "doNothing" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=53" \
+ --trailer "ack: Junio" --trailer "fix=22" \
+ --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'the default is "ifMissing = add"' '
+ git config trailer.cc.key "Cc: " &&
+ git config trailer.cc.where "before" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Cc: Linus
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=53" \
+ --trailer "cc=Linus" --trailer "ack: Junio" \
+ --trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'when default "ifMissing" is "doNothing"' '
+ git config trailer.ifmissing "doNothing" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=53" \
+ --trailer "cc=Linus" --trailer "ack: Junio" \
+ --trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual &&
+ git config trailer.ifmissing "add"
+'
+
+test_expect_success 'using "ifMissing = add" with "where = end"' '
+ git config trailer.cc.key "Cc: " &&
+ git config trailer.cc.where "end" &&
+ git config trailer.cc.ifMissing "add" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ Cc: Linus
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=53" \
+ --trailer "ack: Junio" --trailer "fix=22" \
+ --trailer "bug: 42" --trailer "cc=Linus" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifMissing = add" with "where = before"' '
+ git config trailer.cc.key "Cc: " &&
+ git config trailer.cc.where "before" &&
+ git config trailer.cc.ifMissing "add" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Cc: Linus
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=53" \
+ --trailer "ack: Junio" --trailer "fix=22" \
+ --trailer "bug: 42" --trailer "cc=Linus" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifMissing = doNothing"' '
+ git config trailer.cc.ifMissing "doNothing" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=53" \
+ --trailer "cc=Linus" --trailer "ack: Junio" \
+ --trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'default "where" is now "after"' '
+ git config trailer.where "after" &&
+ git config --unset trailer.ack.where &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Acked-by= Peff
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ Tested-by: Jakub
+ Tested-by: Johannes
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "Acked-by= Peff" --trailer "review:" \
+ --trailer "Tested-by: Jakub" --trailer "ack: Junio" \
+ --trailer "bug: 42" --trailer "Tested-by: Johannes" \
+ --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with simple command' '
+ git config trailer.sign.key "Signed-off-by: " &&
+ git config trailer.sign.where "after" &&
+ git config trailer.sign.ifExists "addIfDifferentNeighbor" &&
+ git config trailer.sign.command "echo \"A U Thor <author@example.com>\"" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Signed-off-by: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=22" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with command using commiter information' '
+ git config trailer.sign.ifExists "addIfDifferent" &&
+ git config trailer.sign.command "echo \"\$GIT_COMMITTER_NAME <\$GIT_COMMITTER_EMAIL>\"" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Signed-off-by: C O Mitter <committer@example.com>
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=22" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with command using author information' '
+ git config trailer.sign.key "Signed-off-by: " &&
+ git config trailer.sign.where "after" &&
+ git config trailer.sign.ifExists "addIfDifferentNeighbor" &&
+ git config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Signed-off-by: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=22" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'setup a commit' '
+ echo "Content of the first commit." > a.txt &&
+ git add a.txt &&
+ git commit -m "Add file a.txt"
+'
+
+test_expect_success 'with command using $ARG' '
+ git config trailer.fix.ifExists "replace" &&
+ git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+ FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-EOF &&
+ Fixes: $FIXED
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Signed-off-by: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with failing command using $ARG' '
+ git config trailer.fix.ifExists "replace" &&
+ git config trailer.fix.command "false \$ARG" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Signed-off-by: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with empty tokens' '
+ git config --unset trailer.fix.command &&
+ cat >expected <<-EOF &&
+
+ Signed-off-by: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer ":" --trailer ":test" >actual <<-EOF &&
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'with command but no key' '
+ git config --unset trailer.sign.key &&
+ cat >expected <<-EOF &&
+
+ sign: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers >actual <<-EOF &&
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'with no command and no key' '
+ git config --unset trailer.review.key &&
+ cat >expected <<-EOF &&
+
+ review: Junio
+ sign: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer "review:Junio" >actual <<-EOF &&
+ EOF
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t7514-commit-patch.sh b/t/t7514-commit-patch.sh
new file mode 100755
index 0000000..998a210
--- /dev/null
+++ b/t/t7514-commit-patch.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+test_description='hunk edit with "commit -p -m"'
+. ./test-lib.sh
+
+if ! test_have_prereq PERL
+then
+ skip_all="skipping '$test_description' tests, perl not available"
+ test_done
+fi
+
+test_expect_success 'setup (initial)' '
+ echo line1 >file &&
+ git add file &&
+ git commit -m commit1
+'
+
+test_expect_success 'edit hunk "commit -p -m message"' '
+ test_when_finished "rm -f editor_was_started" &&
+ rm -f editor_was_started &&
+ echo more >>file &&
+ echo e | env GIT_EDITOR=": >editor_was_started" git commit -p -m commit2 file &&
+ test -r editor_was_started
+'
+
+test_expect_success 'edit hunk "commit --dry-run -p -m message"' '
+ test_when_finished "rm -f editor_was_started" &&
+ rm -f editor_was_started &&
+ echo more >>file &&
+ echo e | env GIT_EDITOR=": >editor_was_started" git commit -p -m commit3 file &&
+ test -r editor_was_started
+'
+
+test_done
diff --git a/t/t7515-status-symlinks.sh b/t/t7515-status-symlinks.sh
new file mode 100755
index 0000000..9f989be
--- /dev/null
+++ b/t/t7515-status-symlinks.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+test_description='git status and symlinks'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo .gitignore >.gitignore &&
+ echo actual >>.gitignore &&
+ echo expect >>.gitignore &&
+ mkdir dir &&
+ echo x >dir/file1 &&
+ echo y >dir/file2 &&
+ git add dir &&
+ git commit -m initial &&
+ git tag initial
+'
+
+test_expect_success SYMLINKS 'symlink to a directory' '
+ test_when_finished "rm symlink" &&
+ ln -s dir symlink &&
+ echo "?? symlink" >expect &&
+ git status --porcelain >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success SYMLINKS 'symlink replacing a directory' '
+ test_when_finished "rm -rf copy && git reset --hard initial" &&
+ mkdir copy &&
+ cp dir/file1 copy/file1 &&
+ echo "changed in copy" >copy/file2 &&
+ git add copy &&
+ git commit -m second &&
+ rm -rf copy &&
+ ln -s dir copy &&
+ echo " D copy/file1" >expect &&
+ echo " D copy/file2" >>expect &&
+ echo "?? copy" >>expect &&
+ git status --porcelain >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t7516-commit-races.sh b/t/t7516-commit-races.sh
new file mode 100755
index 0000000..f2ce14e
--- /dev/null
+++ b/t/t7516-commit-races.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+test_description='git commit races'
+. ./test-lib.sh
+
+test_expect_success 'race to create orphan commit' '
+ write_script hare-editor <<-\EOF &&
+ git commit --allow-empty -m hare
+ EOF
+ test_must_fail env EDITOR=./hare-editor git commit --allow-empty -m tortoise -e &&
+ git show -s --pretty=format:%s >subject &&
+ grep hare subject &&
+ test -z "$(git show -s --pretty=format:%P)"
+'
+
+test_expect_success 'race to create non-orphan commit' '
+ write_script airplane-editor <<-\EOF &&
+ git commit --allow-empty -m airplane
+ EOF
+ git checkout --orphan branch &&
+ git commit --allow-empty -m base &&
+ git rev-parse HEAD >base &&
+ test_must_fail env EDITOR=./airplane-editor git commit --allow-empty -m ship -e &&
+ git show -s --pretty=format:%s >subject &&
+ grep airplane subject &&
+ git rev-parse HEAD^ >parent &&
+ test_cmp base parent
+'
+
+test_done
diff --git a/t/t7517-per-repo-email.sh b/t/t7517-per-repo-email.sh
new file mode 100755
index 0000000..337e6e3
--- /dev/null
+++ b/t/t7517-per-repo-email.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# Copyright (c) 2016 Dan Aloni
+# Copyright (c) 2016 Jeff King
+#
+
+test_description='per-repo forced setting of email address'
+
+. ./test-lib.sh
+
+test_expect_success 'setup a likely user.useConfigOnly use case' '
+ # we want to make sure a reflog is written, since that needs
+ # a non-strict ident. So be sure we have an actual commit.
+ test_commit foo &&
+
+ sane_unset GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL &&
+ sane_unset GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL &&
+ git config user.name "test" &&
+ git config --global user.useConfigOnly true
+'
+
+test_expect_success 'fails committing if clone email is not set' '
+ test_must_fail git commit --allow-empty -m msg
+'
+
+test_expect_success 'fails committing if clone email is not set, but EMAIL set' '
+ test_must_fail env EMAIL=test@fail.com git commit --allow-empty -m msg
+'
+
+test_expect_success 'succeeds committing if clone email is set' '
+ test_config user.email "test@ok.com" &&
+ git commit --allow-empty -m msg
+'
+
+test_expect_success 'succeeds cloning if global email is not set' '
+ git clone . clone
+'
+
+test_done
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index cde8390..85248a1 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -5,190 +5,111 @@
test_description='git merge
-Testing basic merge operations/option parsing.'
+Testing basic merge operations/option parsing.
+
+! [c0] commit 0
+ ! [c1] commit 1
+ ! [c2] commit 2
+ ! [c3] commit 3
+ ! [c4] c4
+ ! [c5] c5
+ ! [c6] c6
+ * [master] Merge commit 'c1'
+--------
+ - [master] Merge commit 'c1'
+ + * [c1] commit 1
+ + [c6] c6
+ + [c5] c5
+ ++ [c4] c4
+ ++++ [c3] commit 3
+ + [c2] commit 2
++++++++* [c0] commit 0
+'
. ./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
+. "$TEST_DIRECTORY"/lib-gpg.sh
+
+printf '%s\n' 1 2 3 4 5 6 7 8 9 >file
+printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >file.1
+printf '%s\n' 1 2 3 4 '5 X' 6 7 8 9 >file.5
+printf '%s\n' 1 2 3 4 5 6 7 8 '9 X' >file.9
+printf '%s\n' 1 2 3 4 5 6 7 8 '9 Y' >file.9y
+printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >result.1
+printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5
+printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9
+printf '%s\n' 1 2 3 4 5 6 7 8 '9 Z' >result.9z
+>empty
+
+create_merge_msgs () {
+ echo "Merge tag 'c2'" >msg.1-5 &&
+ echo "Merge tags 'c2' and 'c3'" >msg.1-5-9 &&
+ {
+ echo "Squashed commit of the following:" &&
+ echo &&
+ git log --no-merges ^HEAD c1
+ } >squash.1 &&
+ {
+ echo "Squashed commit of the following:" &&
+ echo &&
+ git log --no-merges ^HEAD c2
+ } >squash.1-5 &&
+ {
+ echo "Squashed commit of the following:" &&
+ echo &&
+ git log --no-merges ^HEAD c2 c3
+ } >squash.1-5-9 &&
+ : >msg.nologff &&
+ : >msg.nolognoff &&
+ {
+ echo "* tag 'c3':" &&
+ echo " commit 3"
+ } >msg.log
}
-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 &&
+verify_merge () {
+ test_cmp "$2" "$1" &&
+ git update-index --refresh &&
+ git diff --exit-code &&
if test -n "$3"
then
- git show -s --pretty=format:%s HEAD >msg.act &&
- verify_diff "$3" msg.act "[OOPS] bad merge message"
+ git show -s --pretty=tformat:%s HEAD >msg.act &&
+ test_cmp "$3" msg.act
fi
}
-verify_head() {
- if test "$1" != "$(git rev-parse HEAD)"
- then
- echo "[OOPS] HEAD != $1"
- false
- fi
+verify_head () {
+ echo "$1" >head.expected &&
+ git rev-parse HEAD >head.actual &&
+ test_cmp head.expected head.actual
}
-verify_parents() {
- i=1
- while test $# -gt 0
+verify_parents () {
+ printf '%s\n' "$@" >parents.expected &&
+ >parents.actual &&
+ i=1 &&
+ while test $i -le $#
do
- if test "$1" != "$(git rev-parse HEAD^$i)"
- then
- echo "[OOPS] HEAD^$i != $1"
- return 1
- fi
- i=$(expr $i + 1)
- shift
- done
+ git rev-parse HEAD^$i >>parents.actual &&
+ i=$(expr $i + 1) ||
+ return 1
+ done &&
+ test_must_fail git rev-parse --verify "HEAD^$i" &&
+ test_cmp parents.expected parents.actual
}
-verify_mergeheads() {
- i=1
- if ! test -f .git/MERGE_HEAD
- then
- echo "[OOPS] MERGE_HEAD is missing"
- false
- fi &&
- while test $# -gt 0
+verify_mergeheads () {
+ printf '%s\n' "$@" >mergehead.expected &&
+ while read sha1 rest
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
+ git rev-parse $sha1
+ done <.git/MERGE_HEAD >mergehead.actual &&
+ test_cmp mergehead.expected mergehead.actual
}
-verify_no_mergehead() {
- if test -f .git/MERGE_HEAD
- then
- echo "[OOPS] MERGE_HEAD exists"
- false
- fi
+verify_no_mergehead () {
+ ! test -e .git/MERGE_HEAD
}
-
test_expect_success 'setup' '
git add file &&
test_tick &&
@@ -209,17 +130,23 @@ test_expect_success 'setup' '
git tag c2 &&
c2=$(git rev-parse HEAD) &&
git reset --hard "$c0" &&
+ cp file.9y file &&
+ git add file &&
+ test_tick &&
+ git commit -m "commit 7" &&
+ git tag c7 &&
+ 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)
+ c3=$(git rev-parse HEAD) &&
git reset --hard "$c0" &&
create_merge_msgs
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'test option parsing' '
test_must_fail git merge -$ c1 &&
@@ -230,18 +157,35 @@ test_expect_success 'test option parsing' '
test_must_fail git merge
'
+test_expect_success 'merge -h with invalid index' '
+ mkdir broken &&
+ (
+ cd broken &&
+ git init &&
+ >.git/index &&
+ test_expect_code 129 git merge -h 2>usage
+ ) &&
+ test_i18ngrep "[Uu]sage: git merge" broken/usage
+'
+
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' '
+ echo "OBJID HEAD@{0}: merge c1: Fast-forward" >reflog.expected &&
+
git reset --hard c0 &&
git merge c1 &&
verify_merge file result.1 &&
- verify_head "$c1"
+ verify_head "$c1" &&
+
+ git reflog -1 >reflog.actual &&
+ sed "s/$_x05[0-9a-f]*/OBJID/g" reflog.actual >reflog.fuzzy &&
+ test_cmp reflog.expected reflog.fuzzy
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c0 with c1 with --ff-only' '
git reset --hard c0 &&
@@ -251,7 +195,28 @@ test_expect_success 'merge c0 with c1 with --ff-only' '
verify_head "$c1"
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
+
+test_expect_success 'merge from unborn branch' '
+ git checkout -f master &&
+ test_might_fail git branch -D kid &&
+
+ echo "OBJID HEAD@{0}: initial pull" >reflog.expected &&
+
+ git checkout --orphan kid &&
+ test_when_finished "git checkout -f master" &&
+ git rm -fr . &&
+ test_tick &&
+ git merge --ff-only c1 &&
+ verify_merge file result.1 &&
+ verify_head "$c1" &&
+
+ git reflog -1 >reflog.actual &&
+ sed "s/$_x05[0-9a-f][0-9a-f]/OBJID/g" reflog.actual >reflog.fuzzy &&
+ test_cmp reflog.expected reflog.fuzzy
+'
+
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2' '
git reset --hard c1 &&
@@ -261,7 +226,27 @@ test_expect_success 'merge c1 with c2' '
verify_parents $c1 $c2
'
-test_debug 'gitk --all'
+test_expect_success 'merge --squash c3 with c7' '
+ git reset --hard c3 &&
+ test_must_fail git merge --squash c7 &&
+ cat result.9z >file &&
+ git commit --no-edit -a &&
+
+ {
+ cat <<-EOF
+ Squashed commit of the following:
+
+ $(git show -s c7)
+
+ # Conflicts:
+ # file
+ EOF
+ } >expect &&
+ git cat-file commit HEAD | sed -e '1,/^$/d' >actual &&
+ test_cmp expect actual
+'
+
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 and c3' '
git reset --hard c1 &&
@@ -271,14 +256,29 @@ test_expect_success 'merge c1 with c2 and c3' '
verify_parents $c1 $c2 $c3
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
-test_expect_success 'failing merges with --ff-only' '
+test_expect_success '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_must_fail git merge --ff-only c2 c3 &&
+ git reset --hard c0 &&
+ git merge c3 &&
+ verify_head $c3
+'
+
+test_expect_success 'merges with merge.ff=only' '
+ git reset --hard c1 &&
+ test_tick &&
+ test_config merge.ff "only" &&
+ test_must_fail git merge c2 &&
+ test_must_fail git merge c3 &&
+ test_must_fail git merge c2 c3 &&
+ git reset --hard c0 &&
+ git merge c3 &&
+ verify_head $c3
'
test_expect_success 'merge c0 with c1 (no-commit)' '
@@ -288,7 +288,7 @@ test_expect_success 'merge c0 with c1 (no-commit)' '
verify_head $c1
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 (no-commit)' '
git reset --hard c1 &&
@@ -298,7 +298,7 @@ test_expect_success 'merge c1 with c2 (no-commit)' '
verify_mergeheads $c2
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 and c3 (no-commit)' '
git reset --hard c1 &&
@@ -308,7 +308,7 @@ test_expect_success 'merge c1 with c2 and c3 (no-commit)' '
verify_mergeheads $c2 $c3
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c0 with c1 (squash)' '
git reset --hard c0 &&
@@ -316,10 +316,10 @@ test_expect_success 'merge c0 with c1 (squash)' '
verify_merge file result.1 &&
verify_head $c0 &&
verify_no_mergehead &&
- verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message"
+ test_cmp squash.1 .git/SQUASH_MSG
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c0 with c1 (squash, ff-only)' '
git reset --hard c0 &&
@@ -327,10 +327,10 @@ test_expect_success 'merge c0 with c1 (squash, ff-only)' '
verify_merge file result.1 &&
verify_head $c0 &&
verify_no_mergehead &&
- verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message"
+ test_cmp squash.1 .git/SQUASH_MSG
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 (squash)' '
git reset --hard c1 &&
@@ -338,17 +338,17 @@ test_expect_success 'merge c1 with c2 (squash)' '
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_cmp squash.1-5 .git/SQUASH_MSG
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
-test_expect_success 'unsuccesful merge of c1 with c2 (squash, ff-only)' '
+test_expect_success 'unsuccessful merge of c1 with c2 (squash, ff-only)' '
git reset --hard c1 &&
test_must_fail git merge --squash --ff-only c2
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 and c3 (squash)' '
git reset --hard c1 &&
@@ -356,37 +356,64 @@ test_expect_success 'merge c1 with c2 and c3 (squash)' '
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_cmp squash.1-5-9 .git/SQUASH_MSG
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 (no-commit in config)' '
git reset --hard c1 &&
- git config branch.master.mergeoptions "--no-commit" &&
+ test_config branch.master.mergeoptions "--no-commit" &&
git merge c2 &&
verify_merge file result.1-5 &&
verify_head $c1 &&
verify_mergeheads $c2
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
+
+test_expect_success 'merge c1 with c2 (log in config)' '
+ git reset --hard c1 &&
+ git merge --log c2 &&
+ git show -s --pretty=tformat:%s%n%b >expect &&
+
+ test_config branch.master.mergeoptions "--log" &&
+ git reset --hard c1 &&
+ git merge c2 &&
+ git show -s --pretty=tformat:%s%n%b >actual &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'merge c1 with c2 (log in config gets overridden)' '
+ git reset --hard c1 &&
+ git merge c2 &&
+ git show -s --pretty=tformat:%s%n%b >expect &&
+
+ test_config branch.master.mergeoptions "--no-log" &&
+ test_config merge.log "true" &&
+ git reset --hard c1 &&
+ git merge c2 &&
+ git show -s --pretty=tformat:%s%n%b >actual &&
+
+ test_cmp expect actual
+'
test_expect_success 'merge c1 with c2 (squash in config)' '
git reset --hard c1 &&
- git config branch.master.mergeoptions "--squash" &&
+ test_config branch.master.mergeoptions "--squash" &&
git merge c2 &&
verify_merge file result.1-5 &&
verify_head $c1 &&
verify_no_mergehead &&
- verify_diff squash.1-5 .git/SQUASH_MSG "[OOPS] bad squash message"
+ test_cmp squash.1-5 .git/SQUASH_MSG
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'override config option -n with --summary' '
git reset --hard c1 &&
- git config branch.master.mergeoptions "-n" &&
+ test_config branch.master.mergeoptions "-n" &&
test_tick &&
git merge --summary c2 >diffstat.txt &&
verify_merge file result.1-5 msg.1-5 &&
@@ -400,7 +427,7 @@ test_expect_success 'override config option -n with --summary' '
test_expect_success 'override config option -n with --stat' '
git reset --hard c1 &&
- git config branch.master.mergeoptions "-n" &&
+ test_config branch.master.mergeoptions "-n" &&
test_tick &&
git merge --stat c2 >diffstat.txt &&
verify_merge file result.1-5 msg.1-5 &&
@@ -412,11 +439,11 @@ test_expect_success 'override config option -n with --stat' '
fi
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'override config option --stat' '
git reset --hard c1 &&
- git config branch.master.mergeoptions "--stat" &&
+ test_config branch.master.mergeoptions "--stat" &&
test_tick &&
git merge -n c2 >diffstat.txt &&
verify_merge file result.1-5 msg.1-5 &&
@@ -428,54 +455,89 @@ test_expect_success 'override config option --stat' '
fi
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 (override --no-commit)' '
git reset --hard c1 &&
- git config branch.master.mergeoptions "--no-commit" &&
+ test_config branch.master.mergeoptions "--no-commit" &&
test_tick &&
git merge --commit c2 &&
verify_merge file result.1-5 msg.1-5 &&
verify_parents $c1 $c2
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 (override --squash)' '
git reset --hard c1 &&
- git config branch.master.mergeoptions "--squash" &&
+ test_config branch.master.mergeoptions "--squash" &&
test_tick &&
git merge --no-squash c2 &&
verify_merge file result.1-5 msg.1-5 &&
verify_parents $c1 $c2
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c0 with c1 (no-ff)' '
git reset --hard c0 &&
- git config branch.master.mergeoptions "" &&
test_tick &&
git merge --no-ff c1 &&
verify_merge file result.1 &&
verify_parents $c0 $c1
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
+
+test_expect_success 'merge c0 with c1 (merge.ff=false)' '
+ git reset --hard c0 &&
+ test_config merge.ff "false" &&
+ test_tick &&
+ git merge c1 &&
+ verify_merge file result.1 &&
+ verify_parents $c0 $c1
+'
+test_debug 'git log --graph --decorate --oneline --all'
+
+test_expect_success 'combine branch.master.mergeoptions with merge.ff' '
+ git reset --hard c0 &&
+ test_config branch.master.mergeoptions "--ff" &&
+ test_config merge.ff "false" &&
+ test_tick &&
+ git merge c1 &&
+ verify_merge file result.1 &&
+ verify_parents "$c0"
+'
+
+test_expect_success 'tolerate unknown values for merge.ff' '
+ git reset --hard c0 &&
+ test_config merge.ff "something-new" &&
+ test_tick &&
+ git merge c1 2>message &&
+ verify_head "$c1" &&
+ test_cmp empty message
+'
test_expect_success 'combining --squash and --no-ff is refused' '
+ git reset --hard c0 &&
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 'option --ff-only overwrites --no-ff' '
+ git merge --no-ff --ff-only c1 &&
+ test_must_fail git merge --no-ff --ff-only c2
+'
+
+test_expect_success 'option --no-ff overrides merge.ff=only config' '
+ git reset --hard c0 &&
+ test_config merge.ff only &&
+ git merge --no-ff c1
'
test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
git reset --hard c0 &&
- git config branch.master.mergeoptions "--no-ff" &&
+ test_config branch.master.mergeoptions "--no-ff" &&
git merge --ff c1 &&
verify_merge file result.1 &&
verify_head $c1
@@ -485,53 +547,56 @@ 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" &&
+ test_cmp msg.nologff msg.act &&
+
+ git reset --hard c0 &&
+ test_config branch.master.mergeoptions "--no-ff" &&
+ git merge --no-log c2 &&
+ git show -s --pretty=format:%b HEAD >msg.act &&
+ test_cmp msg.nolognoff msg.act &&
git merge --log c3 &&
git show -s --pretty=format:%b HEAD >msg.act &&
- verify_diff msg.log msg.act "[OOPS] bad merge log message" &&
+ test_cmp msg.log msg.act &&
git reset --hard HEAD^ &&
- git config merge.log yes &&
+ test_config merge.log "yes" &&
git merge c3 &&
git show -s --pretty=format:%b HEAD >msg.act &&
- verify_diff msg.log msg.act "[OOPS] bad merge log message"
+ test_cmp msg.log msg.act
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c0, c2, c0, and c1' '
git reset --hard c1 &&
- git config branch.master.mergeoptions "" &&
test_tick &&
git merge c0 c2 c0 c1 &&
verify_merge file result.1-5 &&
verify_parents $c1 $c2
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c0, c2, c0, and c1' '
git reset --hard c1 &&
- git config branch.master.mergeoptions "" &&
test_tick &&
git merge c0 c2 c0 c1 &&
verify_merge file result.1-5 &&
verify_parents $c1 $c2
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c1 and c2' '
git reset --hard c1 &&
- git config branch.master.mergeoptions "" &&
test_tick &&
git merge c1 c2 &&
verify_merge file result.1-5 &&
verify_parents $c1 $c2
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge fast-forward in a dirty tree' '
git reset --hard c0 &&
@@ -541,16 +606,16 @@ test_expect_success 'merge fast-forward in a dirty tree' '
git merge c2
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'in-index merge' '
git reset --hard c0 &&
- git merge --no-ff -s resolve c1 > out &&
- grep "Wonderful." out &&
+ git merge --no-ff -s resolve c1 >out &&
+ test_i18ngrep "Wonderful." out &&
verify_parents $c0 $c1
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'refresh the index before merging' '
git reset --hard c1 &&
@@ -558,31 +623,39 @@ test_expect_success 'refresh the index before merging' '
git merge c3
'
-cat >expected <<EOF
-Merge branch 'c5' (early part)
+cat >expected.branch <<\EOF
+Merge branch 'c5-branch' (early part)
+EOF
+cat >expected.tag <<\EOF
+Merge commit 'c5~1'
EOF
test_expect_success 'merge early part of c2' '
git reset --hard c3 &&
- echo c4 > c4.c &&
+ echo c4 >c4.c &&
git add c4.c &&
git commit -m c4 &&
git tag c4 &&
- echo c5 > c5.c &&
+ echo c5 >c5.c &&
git add c5.c &&
git commit -m c5 &&
git tag c5 &&
git reset --hard c3 &&
- echo c6 > c6.c &&
+ echo c6 >c6.c &&
git add c6.c &&
git commit -m c6 &&
git tag c6 &&
+ git branch -f c5-branch c5 &&
+ git merge c5-branch~1 &&
+ git show -s --pretty=tformat:%s HEAD >actual.branch &&
+ git reset --keep HEAD^ &&
git merge c5~1 &&
- git show -s --pretty=format:%s HEAD > actual &&
- test_cmp actual expected
+ git show -s --pretty=tformat:%s HEAD >actual.tag &&
+ test_cmp expected.branch actual.branch &&
+ test_cmp expected.tag actual.tag
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge --no-ff --no-commit && commit' '
git reset --hard c0 &&
@@ -591,13 +664,103 @@ test_expect_success 'merge --no-ff --no-commit && commit' '
verify_parents $c0 $c1
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'amending no-ff merge commit' '
EDITOR=: git commit --amend &&
verify_parents $c0 $c1
'
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
+
+cat >editor <<\EOF
+#!/bin/sh
+# Add a new message string that was not in the template
+(
+ echo "Merge work done on the side branch c1"
+ echo
+ cat <"$1"
+) >"$1.tmp" && mv "$1.tmp" "$1"
+# strip comments and blank lines from end of message
+sed -e '/^#/d' < "$1" | sed -e :a -e '/^\n*$/{$d;N;ba' -e '}' > expected
+EOF
+chmod 755 editor
+
+test_expect_success 'merge --no-ff --edit' '
+ git reset --hard c0 &&
+ EDITOR=./editor git merge --no-ff --edit c1 &&
+ verify_parents $c0 $c1 &&
+ git cat-file commit HEAD >raw &&
+ grep "work done on the side branch" raw &&
+ sed "1,/^$/d" >actual raw &&
+ test_cmp actual expected
+'
+
+test_expect_success GPG 'merge --ff-only tag' '
+ git reset --hard c0 &&
+ git commit --allow-empty -m "A newer commit" &&
+ git tag -s -m "A newer commit" signed &&
+ git reset --hard c0 &&
+
+ git merge --ff-only signed &&
+ git rev-parse signed^0 >expect &&
+ git rev-parse HEAD >actual &&
+ test_cmp actual expect
+'
+
+test_expect_success GPG 'merge --no-edit tag should skip editor' '
+ git reset --hard c0 &&
+ git commit --allow-empty -m "A newer commit" &&
+ git tag -f -s -m "A newer commit" signed &&
+ git reset --hard c0 &&
+
+ EDITOR=false git merge --no-edit signed &&
+ git rev-parse signed^0 >expect &&
+ git rev-parse HEAD^2 >actual &&
+ test_cmp actual expect
+'
+
+test_expect_success 'set up mod-256 conflict scenario' '
+ # 256 near-identical stanzas...
+ for i in $(test_seq 1 256); do
+ for j in 1 2 3 4 5; do
+ echo $i-$j
+ done
+ done >file &&
+ git add file &&
+ git commit -m base &&
+
+ # one side changes the first line of each to "master"
+ sed s/-1/-master/ <file >tmp &&
+ mv tmp file &&
+ git commit -am master &&
+
+ # and the other to "side"; merging the two will
+ # yield 256 separate conflicts
+ git checkout -b side HEAD^ &&
+ sed s/-1/-side/ <file >tmp &&
+ mv tmp file &&
+ git commit -am side
+'
+
+test_expect_success 'merge detects mod-256 conflicts (recursive)' '
+ git reset --hard &&
+ test_must_fail git merge -s recursive master
+'
+
+test_expect_success 'merge detects mod-256 conflicts (resolve)' '
+ git reset --hard &&
+ test_must_fail git merge -s resolve master
+'
+
+test_expect_success 'merge nothing into void' '
+ git init void &&
+ (
+ cd void &&
+ git remote add up .. &&
+ git fetch up &&
+ test_must_fail git merge FETCH_HEAD
+ )
+'
test_done
diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh
index 7ba94ea..c6c44ec 100755
--- a/t/t7601-merge-pull-config.sh
+++ b/t/t7601-merge-pull-config.sh
@@ -38,6 +38,35 @@ test_expect_success 'merge c1 with c2' '
test -f c2.c
'
+test_expect_success 'fast-forward pull succeeds with "true" in pull.ff' '
+ git reset --hard c0 &&
+ test_config pull.ff true &&
+ git pull . c1 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse c1)"
+'
+
+test_expect_success 'pull.ff=true overrides merge.ff=false' '
+ git reset --hard c0 &&
+ test_config merge.ff false &&
+ test_config pull.ff true &&
+ git pull . c1 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse c1)"
+'
+
+test_expect_success 'fast-forward pull creates merge with "false" in pull.ff' '
+ git reset --hard c0 &&
+ test_config pull.ff false &&
+ git pull . c1 &&
+ test "$(git rev-parse HEAD^1)" = "$(git rev-parse c0)" &&
+ test "$(git rev-parse HEAD^2)" = "$(git rev-parse c1)"
+'
+
+test_expect_success 'pull prevents non-fast-forward with "only" in pull.ff' '
+ git reset --hard c1 &&
+ test_config pull.ff only &&
+ test_must_fail git pull . c3
+'
+
test_expect_success 'merge c1 with c2 (ours in pull.twohead)' '
git reset --hard c1 &&
git config pull.twohead ours &&
@@ -109,18 +138,18 @@ test_expect_success 'setup conflicted merge' '
'
# First do the merge with resolve and recursive then verify that
-# recusive is choosen.
+# recursive is chosen.
test_expect_success 'merge picks up the best result' '
git config --unset-all pull.twohead &&
git reset --hard c5 &&
- git merge -s resolve c6
+ test_must_fail git merge -s resolve c6 &&
resolve_count=$(conflict_count) &&
git reset --hard c5 &&
- git merge -s recursive c6
+ test_must_fail git merge -s recursive c6 &&
recursive_count=$(conflict_count) &&
git reset --hard c5 &&
- git merge -s recursive -s resolve c6
+ test_must_fail git merge -s recursive -s resolve c6 &&
auto_count=$(conflict_count) &&
test $auto_count = $recursive_count &&
test $auto_count != $resolve_count
@@ -129,13 +158,13 @@ test_expect_success 'merge picks up the best result' '
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
+ test_must_fail git merge -s resolve c6 &&
resolve_count=$(conflict_count) &&
git reset --hard c5 &&
- git merge -s recursive c6
+ test_must_fail git merge -s recursive c6 &&
recursive_count=$(conflict_count) &&
git reset --hard c5 &&
- git merge c6
+ test_must_fail git merge c6 &&
auto_count=$(conflict_count) &&
test $auto_count = $recursive_count &&
test $auto_count != $resolve_count
diff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh
index 2746169..6abe441 100755
--- a/t/t7602-merge-octopus-many.sh
+++ b/t/t7602-merge-octopus-many.sh
@@ -19,7 +19,7 @@ test_expect_success 'setup' '
git add c$i.c &&
git commit -m c$i &&
git tag c$i &&
- i=`expr $i + 1` || return 1
+ i=$(expr $i + 1) || return 1
done
'
@@ -30,22 +30,22 @@ test_expect_success 'merge c1 with c2, c3, c4, ... c29' '
while test $i -le 30
do
refs="$refs c$i"
- i=`expr $i + 1`
- done
+ 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
+ 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
+ i=$(expr $i + 1) || return 1
done
'
@@ -53,11 +53,11 @@ 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(-)
+Merge made by the 'octopus' strategy.
+ c2.c | 1 +
+ c3.c | 1 +
+ c4.c | 1 +
+ 3 files changed, 3 insertions(+)
create mode 100644 c2.c
create mode 100644 c3.c
create mode 100644 c4.c
@@ -66,30 +66,28 @@ EOF
test_expect_success 'merge output uses pretty names' '
git reset --hard c1 &&
git merge c2 c3 c4 >actual &&
- test_cmp actual expected
+ test_i18ncmp expected actual
'
cat >expected <<\EOF
-Already up-to-date with c4
-Trying simple merge with c5
-Merge made by octopus.
- c5.c | 1 +
- 1 files changed, 1 insertions(+), 0 deletions(-)
+Merge made by the 'recursive' strategy.
+ c5.c | 1 +
+ 1 file changed, 1 insertion(+)
create mode 100644 c5.c
EOF
-test_expect_success 'merge up-to-date output uses pretty names' '
- git merge c4 c5 >actual &&
- test_cmp actual expected
+test_expect_success 'merge reduces irrelevant remote heads' '
+ GIT_MERGE_VERBOSITY=0 git merge c4 c5 >actual &&
+ test_i18ncmp expected actual
'
cat >expected <<\EOF
Fast-forwarding to: c1
Trying simple merge with c2
-Merge made by octopus.
- c1.c | 1 +
- c2.c | 1 +
- 2 files changed, 2 insertions(+), 0 deletions(-)
+Merge made by the 'octopus' strategy.
+ c1.c | 1 +
+ c2.c | 1 +
+ 2 files changed, 2 insertions(+)
create mode 100644 c1.c
create mode 100644 c2.c
EOF
@@ -97,7 +95,7 @@ EOF
test_expect_success 'merge fast-forward output uses pretty names' '
git reset --hard c0 &&
git merge c1 c2 >actual &&
- test_cmp actual expected
+ test_i18ncmp expected actual
'
test_done
diff --git a/t/t7603-merge-reduce-heads.sh b/t/t7603-merge-reduce-heads.sh
index 7e17eb4..9894895 100755
--- a/t/t7603-merge-reduce-heads.sh
+++ b/t/t7603-merge-reduce-heads.sh
@@ -57,7 +57,36 @@ test_expect_success 'merge c1 with c2, c3, c4, c5' '
test -f c2.c &&
test -f c3.c &&
test -f c4.c &&
- test -f c5.c
+ test -f c5.c &&
+ git show --format=%s -s >actual &&
+ ! grep c1 actual &&
+ grep c2 actual &&
+ grep c3 actual &&
+ ! grep c4 actual &&
+ grep c5 actual
+'
+
+test_expect_success 'pull c2, c3, c4, c5 into c1' '
+ git reset --hard c1 &&
+ git pull . c2 c3 c4 c5 &&
+ test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
+ test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
+ test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
+ test "$(git rev-parse c3)" = "$(git rev-parse HEAD^3)" &&
+ test "$(git rev-parse c5)" = "$(git rev-parse HEAD^4)" &&
+ git diff --exit-code &&
+ test -f c0.c &&
+ test -f c1.c &&
+ test -f c2.c &&
+ test -f c3.c &&
+ test -f c4.c &&
+ test -f c5.c &&
+ git show --format=%s -s >actual &&
+ ! grep c1 actual &&
+ grep c2 actual &&
+ grep c3 actual &&
+ ! grep c4 actual &&
+ grep c5 actual
'
test_expect_success 'setup' '
@@ -113,4 +142,23 @@ test_expect_success 'verify merge result' '
test $(git rev-parse HEAD^1) = $(git rev-parse E2) &&
test $(git rev-parse HEAD^2) = $(git rev-parse I2)
'
+
+test_expect_success 'fast-forward to redundant refs' '
+ git reset --hard c0 &&
+ git merge c4 c5
+'
+
+test_expect_success 'verify merge result' '
+ test $(git rev-parse HEAD) = $(git rev-parse c5)
+'
+
+test_expect_success 'merge up-to-date redundant refs' '
+ git reset --hard c5 &&
+ git merge c0 c4
+'
+
+test_expect_success 'verify merge result' '
+ test $(git rev-parse HEAD) = $(git rev-parse c5)
+'
+
test_done
diff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh
index 9114785..89619cf 100755
--- a/t/t7604-merge-custom-message.sh
+++ b/t/t7604-merge-custom-message.sh
@@ -11,7 +11,7 @@ create_merge_msgs() {
cp exp.subject exp.log &&
echo >>exp.log "" &&
- echo >>exp.log "* commit 'c2':" &&
+ echo >>exp.log "* tag 'c2':" &&
echo >>exp.log " c2"
}
diff --git a/t/t7605-merge-resolve.sh b/t/t7605-merge-resolve.sh
index 0cb9d11..5d56c38 100755
--- a/t/t7605-merge-resolve.sh
+++ b/t/t7605-merge-resolve.sh
@@ -27,7 +27,7 @@ test_expect_success 'setup' '
git tag c3
'
-test_expect_success 'merge c1 to c2' '
+merge_c1_to_c2_cmds='
git reset --hard c1 &&
git merge -s resolve c2 &&
test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
@@ -41,6 +41,10 @@ test_expect_success 'merge c1 to c2' '
test 3 = $(git ls-files | wc -l)
'
+test_expect_success 'merge c1 to c2' "$merge_c1_to_c2_cmds"
+
+test_expect_success 'merge c1 to c2, again' "$merge_c1_to_c2_cmds"
+
test_expect_success 'merge c2 to c3 (fails)' '
git reset --hard c2 &&
test_must_fail git merge -s resolve c3
diff --git a/t/t7606-merge-custom.sh b/t/t7606-merge-custom.sh
index 52a451d..8e8c4d7 100755
--- a/t/t7606-merge-custom.sh
+++ b/t/t7606-merge-custom.sh
@@ -1,49 +1,93 @@
#!/bin/sh
-test_description='git merge
+test_description="git merge
-Testing a custom strategy.'
+Testing a custom strategy.
+
+* (HEAD, master) Merge commit 'c3'
+|\
+| * (tag: c3) c3
+* | (tag: c1) c1
+|/
+| * tag: c2) c2
+|/
+* (tag: c0) c0
+"
. ./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 'set up custom strategy' '
+ 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 &&
+ test_commit c0 c0.c &&
+ test_commit c1 c1.c &&
+ git reset --keep c0 &&
echo c1c1 >c1.c &&
- echo c2 >c2.c &&
- git add c1.c c2.c &&
- git commit -m c2 &&
- git tag c2
+ git add c1.c &&
+ test_commit c2 c2.c &&
+ git reset --keep c0 &&
+ test_commit c3 c3.c
'
test_expect_success 'merge c2 with a custom strategy' '
git reset --hard c1 &&
+
+ git rev-parse c1 >head.old &&
+ git rev-parse c2 >second-parent.expected &&
+ git rev-parse c2^{tree} >tree.expected &&
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 rev-parse HEAD >head.new &&
+ git rev-parse HEAD^1 >first-parent &&
+ git rev-parse HEAD^2 >second-parent &&
+ git rev-parse HEAD^{tree} >tree &&
+ git update-index --refresh &&
git diff --exit-code &&
git diff --exit-code c2 HEAD &&
git diff --exit-code c2 &&
+
+ ! test_cmp head.old head.new &&
+ test_cmp head.old first-parent &&
+ test_cmp second-parent.expected second-parent &&
+ test_cmp tree.expected tree &&
test -f c0.c &&
grep c1c1 c1.c &&
test -f c2.c
'
+test_expect_success 'trivial merge with custom strategy' '
+ git reset --hard c1 &&
+
+ git rev-parse c1 >head.old &&
+ git rev-parse c3 >second-parent.expected &&
+ git rev-parse c3^{tree} >tree.expected &&
+ git merge -s theirs c3 &&
+
+ git rev-parse HEAD >head.new &&
+ git rev-parse HEAD^1 >first-parent &&
+ git rev-parse HEAD^2 >second-parent &&
+ git rev-parse HEAD^{tree} >tree &&
+ git update-index --refresh &&
+ git diff --exit-code &&
+ git diff --exit-code c3 HEAD &&
+ git diff --exit-code c3 &&
+
+ ! test_cmp head.old head.new &&
+ test_cmp head.old first-parent &&
+ test_cmp second-parent.expected second-parent &&
+ test_cmp tree.expected tree &&
+ test -f c0.c &&
+ ! test -e c1.c &&
+ test -f c3.c
+'
+
test_done
diff --git a/t/t7607-merge-overwrite.sh b/t/t7607-merge-overwrite.sh
index 49f4e15..758a623 100755
--- a/t/t7607-merge-overwrite.sh
+++ b/t/t7607-merge-overwrite.sh
@@ -7,48 +7,54 @@ 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 &&
+ test_commit c0 c0.c &&
+ test_commit c1 c1.c &&
+ test_commit c1a c1.c "c1 a" &&
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 &&
+ test_commit c2 c2.c &&
+ git reset --hard c0 &&
+ mkdir sub &&
+ echo "sub/f" > sub/f &&
+ mkdir sub2 &&
+ echo "sub2/f" > sub2/f &&
+ git add sub/f sub2/f &&
+ git commit -m sub &&
+ git tag sub &&
echo "VERY IMPORTANT CHANGES" > important
'
test_expect_success 'will not overwrite untracked file' '
git reset --hard c1 &&
- cat important > c2.c &&
- ! git merge c2 &&
+ cp important c2.c &&
+ test_must_fail git merge c2 &&
+ test_path_is_missing .git/MERGE_HEAD &&
test_cmp important c2.c
'
+test_expect_success 'will overwrite tracked file' '
+ git reset --hard c1 &&
+ cp important c2.c &&
+ git add c2.c &&
+ git commit -m important &&
+ git checkout c2
+'
+
test_expect_success 'will not overwrite new file' '
git reset --hard c1 &&
- cat important > c2.c &&
+ cp important c2.c &&
git add c2.c &&
- ! git merge c2 &&
+ test_must_fail git merge c2 &&
+ test_path_is_missing .git/MERGE_HEAD &&
test_cmp important c2.c
'
test_expect_success 'will not overwrite staged changes' '
git reset --hard c1 &&
- cat important > c2.c &&
+ cp important c2.c &&
git add c2.c &&
rm c2.c &&
- ! git merge c2 &&
+ test_must_fail git merge c2 &&
+ test_path_is_missing .git/MERGE_HEAD &&
git checkout c2.c &&
test_cmp important c2.c
'
@@ -57,8 +63,8 @@ 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 &&
+ cp important c1.c &&
+ test_must_fail git merge c1a &&
test_cmp important c1.c
'
@@ -66,9 +72,10 @@ 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 &&
+ cp important c1.c &&
git add c1.c &&
- ! git merge c1a &&
+ test_must_fail git merge c1a &&
+ test_path_is_missing .git/MERGE_HEAD &&
test_cmp important c1.c
'
@@ -76,12 +83,110 @@ 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 &&
+ cp important c1.c &&
git add c1.c &&
rm c1.c &&
- ! git merge c1a &&
+ test_must_fail git merge c1a &&
+ test_path_is_missing .git/MERGE_HEAD &&
git checkout c1.c &&
test_cmp important c1.c
'
+test_expect_failure 'will not overwrite unstaged changes in renamed file' '
+ git reset --hard c1 &&
+ git mv c1.c other.c &&
+ git commit -m rename &&
+ cp important other.c &&
+ git merge c1a &&
+ test_cmp important other.c
+'
+
+test_expect_success 'will not overwrite untracked subtree' '
+ git reset --hard c0 &&
+ rm -rf sub &&
+ mkdir -p sub/f &&
+ cp important sub/f/important &&
+ test_must_fail git merge sub &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ test_cmp important sub/f/important
+'
+
+cat >expect <<\EOF
+error: The following untracked working tree files would be overwritten by merge:
+ sub
+ sub2
+Please move or remove them before you can merge.
+Aborting
+EOF
+
+test_expect_success 'will not overwrite untracked file in leading path' '
+ git reset --hard c0 &&
+ rm -rf sub &&
+ cp important sub &&
+ cp important sub2 &&
+ test_must_fail git merge sub 2>out &&
+ test_cmp out expect &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ test_cmp important sub &&
+ test_cmp important sub2 &&
+ rm -f sub sub2
+'
+
+test_expect_success SYMLINKS 'will not overwrite untracked symlink in leading path' '
+ git reset --hard c0 &&
+ rm -rf sub &&
+ mkdir sub2 &&
+ ln -s sub2 sub &&
+ test_must_fail git merge sub &&
+ test_path_is_missing .git/MERGE_HEAD
+'
+
+test_expect_success 'will not be confused by symlink in leading path' '
+ git reset --hard c0 &&
+ rm -rf sub &&
+ test_ln_s_add sub2 sub &&
+ git commit -m ln &&
+ git checkout sub
+'
+
+cat >expect <<\EOF
+error: Untracked working tree file 'c0.c' would be overwritten by merge.
+fatal: read-tree failed
+EOF
+
+test_expect_success 'will not overwrite untracked file on unborn branch' '
+ git reset --hard c0 &&
+ git rm -fr . &&
+ git checkout --orphan new &&
+ cp important c0.c &&
+ test_must_fail git merge c0 2>out &&
+ test_i18ncmp out expect
+'
+
+test_expect_success 'will not overwrite untracked file on unborn branch .git/MERGE_HEAD sanity etc.' '
+ test_when_finished "rm c0.c" &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ test_cmp important c0.c
+'
+
+test_expect_success 'failed merge leaves unborn branch in the womb' '
+ test_must_fail git rev-parse --verify HEAD
+'
+
+test_expect_success 'set up unborn branch and content' '
+ git symbolic-ref HEAD refs/heads/unborn &&
+ rm -f .git/index &&
+ echo foo > tracked-file &&
+ git add tracked-file &&
+ echo bar > untracked-file
+'
+
+test_expect_success 'will not clobber WT/index when merging into unborn' '
+ git merge master &&
+ grep foo tracked-file &&
+ git show :tracked-file >expect &&
+ grep foo expect &&
+ grep bar untracked-file
+'
+
test_done
diff --git a/t/t7608-merge-messages.sh b/t/t7608-merge-messages.sh
index 28d5679..8e7e0a5 100755
--- a/t/t7608-merge-messages.sh
+++ b/t/t7608-merge-messages.sh
@@ -35,7 +35,7 @@ test_expect_success 'merge tag' '
git checkout master &&
test_commit master-3 &&
git merge tag-1 &&
- check_oneline "Merge commit Qtag-1Q"
+ check_oneline "Merge tag Qtag-1Q"
'
test_expect_success 'ambiguous tag' '
@@ -44,17 +44,17 @@ test_expect_success 'ambiguous tag' '
git checkout master &&
test_commit master-4 &&
git merge ambiguous &&
- check_oneline "Merge commit QambiguousQ"
+ check_oneline "Merge tag QambiguousQ"
'
-test_expect_success 'remote branch' '
+test_expect_success 'remote-tracking 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"
+ check_oneline "Merge remote-tracking branch Qorigin/masterQ"
'
test_done
diff --git a/t/t7609-merge-co-error-msgs.sh b/t/t7609-merge-co-error-msgs.sh
new file mode 100755
index 0000000..6729cb3
--- /dev/null
+++ b/t/t7609-merge-co-error-msgs.sh
@@ -0,0 +1,138 @@
+#!/bin/sh
+
+test_description='unpack-trees error messages'
+
+. ./test-lib.sh
+
+
+test_expect_success 'setup' '
+ echo one >one &&
+ git add one &&
+ git commit -a -m First &&
+
+ git checkout -b branch &&
+ echo two >two &&
+ echo three >three &&
+ echo four >four &&
+ echo five >five &&
+ git add two three four five &&
+ git commit -m Second &&
+
+ git checkout master &&
+ echo other >two &&
+ echo other >three &&
+ echo other >four &&
+ echo other >five
+'
+
+cat >expect <<\EOF
+error: The following untracked working tree files would be overwritten by merge:
+ five
+ four
+ three
+ two
+Please move or remove them before you can merge.
+Aborting
+EOF
+
+test_expect_success 'untracked files overwritten by merge (fast and non-fast forward)' '
+ test_must_fail git merge branch 2>out &&
+ test_i18ncmp out expect &&
+ git commit --allow-empty -m empty &&
+ (
+ GIT_MERGE_VERBOSITY=0 &&
+ export GIT_MERGE_VERBOSITY &&
+ test_must_fail git merge branch 2>out2
+ ) &&
+ test_i18ncmp out2 expect &&
+ git reset --hard HEAD^
+'
+
+cat >expect <<\EOF
+error: Your local changes to the following files would be overwritten by merge:
+ four
+ three
+ two
+Please commit your changes or stash them before you can merge.
+error: The following untracked working tree files would be overwritten by merge:
+ five
+Please move or remove them before you can merge.
+Aborting
+EOF
+
+test_expect_success 'untracked files or local changes ovewritten by merge' '
+ git add two &&
+ git add three &&
+ git add four &&
+ test_must_fail git merge branch 2>out &&
+ test_i18ncmp out expect
+'
+
+cat >expect <<\EOF
+error: Your local changes to the following files would be overwritten by checkout:
+ rep/one
+ rep/two
+Please commit your changes or stash them before you can switch branches.
+Aborting
+EOF
+
+test_expect_success 'cannot switch branches because of local changes' '
+ git add five &&
+ mkdir rep &&
+ echo one >rep/one &&
+ echo two >rep/two &&
+ git add rep/one rep/two &&
+ git commit -m Fourth &&
+ git checkout master &&
+ echo uno >rep/one &&
+ echo dos >rep/two &&
+ test_must_fail git checkout branch 2>out &&
+ test_i18ncmp out expect
+'
+
+cat >expect <<\EOF
+error: Your local changes to the following files would be overwritten by checkout:
+ rep/one
+ rep/two
+Please commit your changes or stash them before you can switch branches.
+Aborting
+EOF
+
+test_expect_success 'not uptodate file porcelain checkout error' '
+ git add rep/one rep/two &&
+ test_must_fail git checkout branch 2>out &&
+ test_i18ncmp out expect
+'
+
+cat >expect <<\EOF
+error: Updating the following directories would lose untracked files in it:
+ rep
+ rep2
+
+Aborting
+EOF
+
+test_expect_success 'not_uptodate_dir porcelain checkout error' '
+ git init uptodate &&
+ cd uptodate &&
+ mkdir rep &&
+ mkdir rep2 &&
+ touch rep/foo &&
+ touch rep2/foo &&
+ git add rep/foo rep2/foo &&
+ git commit -m init &&
+ git checkout -b branch &&
+ git rm rep -r &&
+ git rm rep2 -r &&
+ >rep &&
+ >rep2 &&
+ git add rep rep2&&
+ git commit -m "added test as a file" &&
+ git checkout master &&
+ >rep/untracked-file &&
+ >rep2/untracked-file &&
+ test_must_fail git checkout branch 2>out &&
+ test_i18ncmp out ../expect
+'
+
+test_done
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index e768c3e..76306cf 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -14,76 +14,591 @@ Testing basic merge tool invocation'
# 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_config rerere.enabled true &&
+ echo master >file1 &&
+ echo master spaced >"spaced name" &&
+ echo master file11 >file11 &&
+ echo master file12 >file12 &&
+ echo master file13 >file13 &&
+ echo master file14 >file14 &&
+ mkdir subdir &&
+ echo master sub >subdir/file3 &&
+ test_create_repo submod &&
+ (
+ cd submod &&
+ : >foo &&
+ git add foo &&
+ git commit -m "Add foo"
+ ) &&
+ git submodule add git://example.com/submod submod &&
+ git add file1 "spaced name" file1[1-4] subdir/file3 .gitmodules submod &&
+ git commit -m "add initial versions" &&
+
+ git checkout -b branch1 master &&
+ git submodule update -N &&
+ echo branch1 change >file1 &&
+ echo branch1 newfile >file2 &&
+ echo branch1 spaced >"spaced name" &&
+ echo branch1 both added >both &&
+ echo branch1 change file11 >file11 &&
+ echo branch1 change file13 >file13 &&
+ echo branch1 sub >subdir/file3 &&
+ (
+ cd submod &&
+ echo branch1 submodule >bar &&
+ git add bar &&
+ git commit -m "Add bar on branch1" &&
+ git checkout -b submod-branch1
+ ) &&
+ git add file1 "spaced name" file11 file13 file2 subdir/file3 submod &&
+ git add both &&
+ git rm file12 &&
+ git commit -m "branch1 changes" &&
+
+ git checkout -b stash1 master &&
+ echo stash1 change file11 >file11 &&
+ git add file11 &&
+ git commit -m "stash1 changes" &&
+
+ git checkout -b stash2 master &&
+ echo stash2 change file11 >file11 &&
+ git add file11 &&
+ git commit -m "stash2 changes" &&
+
+ git checkout master &&
+ git submodule update -N &&
+ echo master updated >file1 &&
+ echo master new >file2 &&
+ echo master updated spaced >"spaced name" &&
+ echo master both added >both &&
+ echo master updated file12 >file12 &&
+ echo master updated file14 >file14 &&
+ echo master new sub >subdir/file3 &&
+ (
+ cd submod &&
+ echo master submodule >bar &&
+ git add bar &&
+ git commit -m "Add bar on master" &&
+ git checkout -b submod-master
+ ) &&
+ git add file1 "spaced name" file12 file14 file2 subdir/file3 submod &&
+ git add both &&
+ git rm file11 &&
+ git commit -m "master updates" &&
+
+ git config merge.tool mytool &&
+ git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
+ git config mergetool.mytool.trustExitCode true &&
+ git config mergetool.mybase.cmd "cat \"\$BASE\" >\"\$MERGED\"" &&
+ git config mergetool.mybase.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"
+ git checkout -b test1 branch1 &&
+ git submodule update -N &&
+ test_must_fail git merge master >/dev/null 2>&1 &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool file1 file1 ) &&
+ ( yes "" | git mergetool file2 "spaced name" >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
+ ( yes "l" | git mergetool submod >/dev/null 2>&1 ) &&
+ test "$(cat file1)" = "master updated" &&
+ test "$(cat file2)" = "master new" &&
+ test "$(cat subdir/file3)" = "master new sub" &&
+ test "$(cat submod/bar)" = "branch1 submodule" &&
+ 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_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 "spaced name" >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
+ ( yes "r" | git mergetool submod >/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 submodule update -N &&
+ test "$(cat submod/bar)" = "master submodule" &&
+ git commit -m "branch1 resolved with mergetool - autocrlf" &&
+ test_config core.autocrlf false &&
+ git reset --hard
'
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" )
+ git checkout -b test3 branch1 &&
+ git submodule update -N &&
+ (
+ cd subdir &&
+ test_must_fail git merge master >/dev/null 2>&1 &&
+ ( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
+ 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_expect_success 'mergetool on file in parent dir' '
+ (
+ cd subdir &&
+ ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool ../file2 ../spaced\ name >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool ../both >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool ../file11 >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool ../file12 >/dev/null 2>&1 ) &&
+ ( yes "l" | git mergetool ../submod >/dev/null 2>&1 ) &&
+ test "$(cat ../file1)" = "master updated" &&
+ test "$(cat ../file2)" = "master new" &&
+ test "$(cat ../submod/bar)" = "branch1 submodule" &&
+ git commit -m "branch1 resolved with mergetool - subdir"
+ )
+'
+
+test_expect_success 'mergetool skips autoresolved' '
+ git checkout -b test4 branch1 &&
+ git submodule update -N &&
+ test_must_fail git merge master &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
+ ( yes "l" | git mergetool submod >/dev/null 2>&1 ) &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git reset --hard
+'
+
+test_expect_success 'mergetool merges all from subdir' '
+ test_config rerere.enabled false &&
+ (
+ cd subdir &&
+ test_must_fail git merge master &&
+ ( yes "r" | git mergetool ../submod ) &&
+ ( yes "d" "d" | git mergetool --no-prompt ) &&
+ test "$(cat ../file1)" = "master updated" &&
+ test "$(cat ../file2)" = "master new" &&
+ test "$(cat file3)" = "master new sub" &&
+ ( cd .. && git submodule update -N ) &&
+ test "$(cat ../submod/bar)" = "master submodule" &&
+ git commit -m "branch2 resolved by mergetool from subdir"
+ )
+'
+
+test_expect_success 'mergetool skips resolved paths when rerere is active' '
+ test_config rerere.enabled true &&
+ rm -rf .git/rr-cache &&
+ git checkout -b test5 branch1 &&
+ git submodule update -N &&
+ test_must_fail git merge master >/dev/null 2>&1 &&
+ ( yes "l" | git mergetool --no-prompt submod >/dev/null 2>&1 ) &&
+ ( yes "d" "d" | git mergetool --no-prompt >/dev/null 2>&1 ) &&
+ git submodule update -N &&
+ output="$(yes "n" | git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git reset --hard
+'
+
+test_expect_success 'conflicted stash sets up rerere' '
+ test_config rerere.enabled true &&
+ git checkout stash1 &&
+ echo "Conflicting stash content" >file11 &&
+ git stash &&
+
+ git checkout --detach stash2 &&
+ test_must_fail git stash apply &&
+
+ test -n "$(git ls-files -u)" &&
+ conflicts="$(git rerere remaining)" &&
+ test "$conflicts" = "file11" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" != "No files need merging" &&
+
+ git commit -am "save the stash resolution" &&
+
+ git reset --hard stash2 &&
+ test_must_fail git stash apply &&
+
+ test -n "$(git ls-files -u)" &&
+ conflicts="$(git rerere remaining)" &&
+ test -z "$conflicts" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging"
+'
+
+test_expect_success 'mergetool takes partial path' '
+ git reset --hard &&
+ test_config rerere.enabled false &&
+ git checkout -b test12 branch1 &&
+ git submodule update -N &&
+ test_must_fail git merge master &&
+
+ ( yes "" | git mergetool subdir ) &&
+
+ test "$(cat subdir/file3)" = "master new sub" &&
+ git reset --hard
+'
+
+test_expect_success 'mergetool delete/delete conflict' '
+ git checkout -b delete-base branch1 &&
+ mkdir -p a/a &&
+ (echo one; echo two; echo 3; echo 4) >a/a/file.txt &&
+ git add a/a/file.txt &&
+ git commit -m"base file" &&
+ git checkout -b move-to-b delete-base &&
+ mkdir -p b/b &&
+ git mv a/a/file.txt b/b/file.txt &&
+ (echo one; echo two; echo 4) >b/b/file.txt &&
+ git commit -a -m"move to b" &&
+ git checkout -b move-to-c delete-base &&
+ mkdir -p c/c &&
+ git mv a/a/file.txt c/c/file.txt &&
+ (echo one; echo two; echo 3) >c/c/file.txt &&
+ git commit -a -m"move to c" &&
+ test_must_fail git merge move-to-b &&
+ echo d | git mergetool a/a/file.txt &&
+ ! test -f a/a/file.txt &&
+ git reset --hard HEAD &&
+ test_must_fail git merge move-to-b &&
+ echo m | git mergetool a/a/file.txt &&
+ test -f b/b/file.txt &&
+ git reset --hard HEAD &&
+ test_must_fail git merge move-to-b &&
+ ! echo a | git mergetool a/a/file.txt &&
+ ! test -f a/a/file.txt &&
+ git reset --hard HEAD
+'
+
+test_expect_success 'mergetool produces no errors when keepBackup is used' '
+ test_config mergetool.keepBackup true &&
+ test_must_fail git merge move-to-b &&
+ : >expect &&
+ echo d | git mergetool a/a/file.txt 2>actual &&
+ test_cmp expect actual &&
+ ! test -d a &&
+ git reset --hard HEAD
+'
+
+test_expect_success 'mergetool honors tempfile config for deleted files' '
+ test_config mergetool.keepTemporaries false &&
+ test_must_fail git merge move-to-b &&
+ echo d | git mergetool a/a/file.txt &&
+ ! test -d a &&
+ git reset --hard HEAD
+'
+
+test_expect_success 'mergetool keeps tempfiles when aborting delete/delete' '
+ test_config mergetool.keepTemporaries true &&
+ test_must_fail git merge move-to-b &&
+ ! (echo a; echo n) | git mergetool a/a/file.txt &&
+ test -d a/a &&
+ cat >expect <<-\EOF &&
+ file_BASE_.txt
+ file_LOCAL_.txt
+ file_REMOTE_.txt
+ EOF
+ ls -1 a/a | sed -e "s/[0-9]*//g" >actual &&
+ test_cmp expect actual &&
+ git clean -fdx &&
+ git reset --hard HEAD
+'
+
+test_expect_success 'deleted vs modified submodule' '
+ git checkout -b test6 branch1 &&
+ git submodule update -N &&
+ mv submod submod-movedaside &&
+ git rm --cached submod &&
+ git commit -m "Submodule deleted from branch" &&
+ git checkout -b test6.a test6 &&
+ test_must_fail git merge master &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+ ( yes "r" | git mergetool submod ) &&
+ rmdir submod && mv submod-movedaside submod &&
+ test "$(cat submod/bar)" = "branch1 submodule" &&
+ git submodule update -N &&
+ test "$(cat submod/bar)" = "master submodule" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git commit -m "Merge resolved by keeping module" &&
+
+ mv submod submod-movedaside &&
+ git checkout -b test6.b test6 &&
+ git submodule update -N &&
+ test_must_fail git merge master &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+ ( yes "l" | git mergetool submod ) &&
+ test ! -e submod &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git commit -m "Merge resolved by deleting module" &&
+
+ mv submod-movedaside submod &&
+ git checkout -b test6.c master &&
+ git submodule update -N &&
+ test_must_fail git merge test6 &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+ ( yes "r" | git mergetool submod ) &&
+ test ! -e submod &&
+ test -d submod.orig &&
+ git submodule update -N &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git commit -m "Merge resolved by deleting module" &&
+ mv submod.orig submod &&
+
+ git checkout -b test6.d master &&
+ git submodule update -N &&
+ test_must_fail git merge test6 &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+ ( yes "l" | git mergetool submod ) &&
+ test "$(cat submod/bar)" = "master submodule" &&
+ git submodule update -N &&
+ test "$(cat submod/bar)" = "master submodule" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git commit -m "Merge resolved by keeping module" &&
+ git reset --hard HEAD
+'
+
+test_expect_success 'file vs modified submodule' '
+ git checkout -b test7 branch1 &&
+ git submodule update -N &&
+ mv submod submod-movedaside &&
+ git rm --cached submod &&
+ echo not a submodule >submod &&
+ git add submod &&
+ git commit -m "Submodule path becomes file" &&
+ git checkout -b test7.a branch1 &&
+ test_must_fail git merge master &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+ ( yes "r" | git mergetool submod ) &&
+ rmdir submod && mv submod-movedaside submod &&
+ test "$(cat submod/bar)" = "branch1 submodule" &&
+ git submodule update -N &&
+ test "$(cat submod/bar)" = "master submodule" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git commit -m "Merge resolved by keeping module" &&
+
+ mv submod submod-movedaside &&
+ git checkout -b test7.b test7 &&
+ test_must_fail git merge master &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+ ( yes "l" | git mergetool submod ) &&
+ git submodule update -N &&
+ test "$(cat submod)" = "not a submodule" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git commit -m "Merge resolved by keeping file" &&
+
+ git checkout -b test7.c master &&
+ rmdir submod && mv submod-movedaside submod &&
+ test ! -e submod.orig &&
+ git submodule update -N &&
+ test_must_fail git merge test7 &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+ ( yes "r" | git mergetool submod ) &&
+ test -d submod.orig &&
+ git submodule update -N &&
+ test "$(cat submod)" = "not a submodule" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git commit -m "Merge resolved by keeping file" &&
+
+ git checkout -b test7.d master &&
+ rmdir submod && mv submod.orig submod &&
+ git submodule update -N &&
+ test_must_fail git merge test7 &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both>/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+ ( yes "l" | git mergetool submod ) &&
+ test "$(cat submod/bar)" = "master submodule" &&
+ git submodule update -N &&
+ test "$(cat submod/bar)" = "master submodule" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git commit -m "Merge resolved by keeping module"
+'
+
+test_expect_success 'submodule in subdirectory' '
+ git checkout -b test10 branch1 &&
+ git submodule update -N &&
+ (
+ cd subdir &&
+ test_create_repo subdir_module &&
+ (
+ cd subdir_module &&
+ : >file15 &&
+ git add file15 &&
+ git commit -m "add initial versions"
+ )
+ ) &&
+ git submodule add git://example.com/subsubmodule subdir/subdir_module &&
+ git add subdir/subdir_module &&
+ git commit -m "add submodule in subdirectory" &&
+
+ git checkout -b test10.a test10 &&
+ git submodule update -N &&
+ (
+ cd subdir/subdir_module &&
+ git checkout -b super10.a &&
+ echo test10.a >file15 &&
+ git add file15 &&
+ git commit -m "on branch 10.a"
+ ) &&
+ git add subdir/subdir_module &&
+ git commit -m "change submodule in subdirectory on test10.a" &&
+
+ git checkout -b test10.b test10 &&
+ git submodule update -N &&
+ (
+ cd subdir/subdir_module &&
+ git checkout -b super10.b &&
+ echo test10.b >file15 &&
+ git add file15 &&
+ git commit -m "on branch 10.b"
+ ) &&
+ git add subdir/subdir_module &&
+ git commit -m "change submodule in subdirectory on test10.b" &&
+
+ test_must_fail git merge test10.a >/dev/null 2>&1 &&
+ (
+ cd subdir &&
+ ( yes "l" | git mergetool subdir_module )
+ ) &&
+ test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
+ git submodule update -N &&
+ test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
+ git reset --hard &&
+ git submodule update -N &&
+
+ test_must_fail git merge test10.a >/dev/null 2>&1 &&
+ ( yes "r" | git mergetool subdir/subdir_module ) &&
+ test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
+ git submodule update -N &&
+ test "$(cat subdir/subdir_module/file15)" = "test10.a" &&
+ git commit -m "branch1 resolved with mergetool" &&
+ rm -rf subdir/subdir_module
+'
+
+test_expect_success 'directory vs modified submodule' '
+ git checkout -b test11 branch1 &&
+ mv submod submod-movedaside &&
+ git rm --cached submod &&
+ mkdir submod &&
+ echo not a submodule >submod/file16 &&
+ git add submod/file16 &&
+ git commit -m "Submodule path becomes directory" &&
+
+ test_must_fail git merge master &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "l" | git mergetool submod ) &&
+ test "$(cat submod/file16)" = "not a submodule" &&
+ rm -rf submod.orig &&
+
+ git reset --hard >/dev/null 2>&1 &&
+ test_must_fail git merge master &&
+ test -n "$(git ls-files -u)" &&
+ test ! -e submod.orig &&
+ ( yes "r" | git mergetool submod ) &&
+ test -d submod.orig &&
+ test "$(cat submod.orig/file16)" = "not a submodule" &&
+ rm -r submod.orig &&
+ mv submod-movedaside/.git submod &&
+ ( cd submod && git clean -f && git reset --hard ) &&
+ git submodule update -N &&
+ test "$(cat submod/bar)" = "master submodule" &&
+ git reset --hard >/dev/null 2>&1 && rm -rf submod-movedaside &&
+
+ git checkout -b test11.c master &&
+ git submodule update -N &&
+ test_must_fail git merge test11 &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "l" | git mergetool submod ) &&
+ git submodule update -N &&
+ test "$(cat submod/bar)" = "master submodule" &&
+
+ git reset --hard >/dev/null 2>&1 &&
+ git submodule update -N &&
+ test_must_fail git merge test11 &&
+ test -n "$(git ls-files -u)" &&
+ test ! -e submod.orig &&
+ ( yes "r" | git mergetool submod ) &&
+ test "$(cat submod/file16)" = "not a submodule" &&
+
+ git reset --hard master >/dev/null 2>&1 &&
+ ( cd submod && git clean -f && git reset --hard ) &&
+ git submodule update -N
+'
+
+test_expect_success 'file with no base' '
+ git checkout -b test13 branch1 &&
+ test_must_fail git merge master &&
+ git mergetool --no-prompt --tool mybase -- both &&
+ >expected &&
+ test_cmp both expected &&
+ git reset --hard master >/dev/null 2>&1
+'
+
+test_expect_success 'custom commands override built-ins' '
+ git checkout -b test14 branch1 &&
+ test_config mergetool.defaults.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
+ test_config mergetool.defaults.trustExitCode true &&
+ test_must_fail git merge master &&
+ git mergetool --no-prompt --tool defaults -- both &&
+ echo master both added >expected &&
+ test_cmp both expected &&
+ git reset --hard master >/dev/null 2>&1
+'
+
+test_expect_success 'filenames seen by tools start with ./' '
+ git checkout -b test15 branch1 &&
+ test_config mergetool.writeToTemp false &&
+ test_config mergetool.myecho.cmd "echo \"\$LOCAL\"" &&
+ test_config mergetool.myecho.trustExitCode true &&
+ test_must_fail git merge master &&
+ git mergetool --no-prompt --tool myecho -- both >actual &&
+ grep ^\./both_LOCAL_ actual >/dev/null &&
+ git reset --hard master >/dev/null 2>&1
+'
+
+test_expect_success 'temporary filenames are used with mergetool.writeToTemp' '
+ git checkout -b test16 branch1 &&
+ test_config mergetool.writeToTemp true &&
+ test_config mergetool.myecho.cmd "echo \"\$LOCAL\"" &&
+ test_config mergetool.myecho.trustExitCode true &&
+ test_must_fail git merge master &&
+ git mergetool --no-prompt --tool myecho -- both >actual &&
+ test_must_fail grep ^\./both_LOCAL_ actual >/dev/null &&
+ grep /both_LOCAL_ actual >/dev/null &&
+ git reset --hard master >/dev/null 2>&1
+'
test_done
diff --git a/t/t7611-merge-abort.sh b/t/t7611-merge-abort.sh
new file mode 100755
index 0000000..7b4798e
--- /dev/null
+++ b/t/t7611-merge-abort.sh
@@ -0,0 +1,319 @@
+#!/bin/sh
+
+test_description='test aborting in-progress merges
+
+Set up repo with conflicting and non-conflicting branches:
+
+There are three files foo/bar/baz, and the following graph illustrates the
+content of these files in each commit:
+
+# foo/bar/baz --- foo/bar/bazz <-- master
+# \
+# --- foo/barf/bazf <-- conflict_branch
+# \
+# --- foo/bart/baz <-- clean_branch
+
+Next, test git merge --abort with the following variables:
+- before/after successful merge (should fail when not in merge context)
+- with/without conflicts
+- clean/dirty index before merge
+- clean/dirty worktree before merge
+- dirty index before merge matches contents on remote branch
+- changed/unchanged worktree after merge
+- changed/unchanged index after merge
+'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ # Create the above repo
+ echo foo > foo &&
+ echo bar > bar &&
+ echo baz > baz &&
+ git add foo bar baz &&
+ git commit -m initial &&
+ echo bazz > baz &&
+ git commit -a -m "second" &&
+ git checkout -b conflict_branch HEAD^ &&
+ echo barf > bar &&
+ echo bazf > baz &&
+ git commit -a -m "conflict" &&
+ git checkout -b clean_branch HEAD^ &&
+ echo bart > bar &&
+ git commit -a -m "clean" &&
+ git checkout master
+'
+
+pre_merge_head="$(git rev-parse HEAD)"
+
+test_expect_success 'fails without MERGE_HEAD (unstarted merge)' '
+ test_must_fail git merge --abort 2>output &&
+ test_i18ngrep MERGE_HEAD output
+'
+
+test_expect_success 'fails without MERGE_HEAD (unstarted merge): .git/MERGE_HEAD sanity' '
+ test ! -f .git/MERGE_HEAD &&
+ test "$pre_merge_head" = "$(git rev-parse HEAD)"
+'
+
+test_expect_success 'fails without MERGE_HEAD (completed merge)' '
+ git merge clean_branch &&
+ test ! -f .git/MERGE_HEAD &&
+ # Merge successfully completed
+ post_merge_head="$(git rev-parse HEAD)" &&
+ test_must_fail git merge --abort 2>output &&
+ test_i18ngrep MERGE_HEAD output
+'
+
+test_expect_success 'fails without MERGE_HEAD (completed merge): .git/MERGE_HEAD sanity' '
+ test ! -f .git/MERGE_HEAD &&
+ test "$post_merge_head" = "$(git rev-parse HEAD)"
+'
+
+test_expect_success 'Forget previous merge' '
+ git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Abort after --no-commit' '
+ # Redo merge, but stop before creating merge commit
+ git merge --no-commit clean_branch &&
+ test -f .git/MERGE_HEAD &&
+ # Abort non-conflicting merge
+ git merge --abort &&
+ test ! -f .git/MERGE_HEAD &&
+ test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+ test -z "$(git diff)" &&
+ test -z "$(git diff --staged)"
+'
+
+test_expect_success 'Abort after conflicts' '
+ # Create conflicting merge
+ test_must_fail git merge conflict_branch &&
+ test -f .git/MERGE_HEAD &&
+ # Abort conflicting merge
+ git merge --abort &&
+ test ! -f .git/MERGE_HEAD &&
+ test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+ test -z "$(git diff)" &&
+ test -z "$(git diff --staged)"
+'
+
+test_expect_success 'Clean merge with dirty index fails' '
+ echo xyzzy >> foo &&
+ git add foo &&
+ git diff --staged > expect &&
+ test_must_fail git merge clean_branch &&
+ test ! -f .git/MERGE_HEAD &&
+ test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+ test -z "$(git diff)" &&
+ git diff --staged > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Conflicting merge with dirty index fails' '
+ test_must_fail git merge conflict_branch &&
+ test ! -f .git/MERGE_HEAD &&
+ test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+ test -z "$(git diff)" &&
+ git diff --staged > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Reset index (but preserve worktree changes)' '
+ git reset "$pre_merge_head" &&
+ git diff > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Abort clean merge with non-conflicting dirty worktree' '
+ git merge --no-commit clean_branch &&
+ test -f .git/MERGE_HEAD &&
+ # Abort merge
+ git merge --abort &&
+ test ! -f .git/MERGE_HEAD &&
+ test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+ test -z "$(git diff --staged)" &&
+ git diff > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Abort conflicting merge with non-conflicting dirty worktree' '
+ test_must_fail git merge conflict_branch &&
+ test -f .git/MERGE_HEAD &&
+ # Abort merge
+ git merge --abort &&
+ test ! -f .git/MERGE_HEAD &&
+ test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+ test -z "$(git diff --staged)" &&
+ git diff > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Reset worktree changes' '
+ git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Fail clean merge with conflicting dirty worktree' '
+ echo xyzzy >> bar &&
+ git diff > expect &&
+ test_must_fail git merge --no-commit clean_branch &&
+ test ! -f .git/MERGE_HEAD &&
+ test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+ test -z "$(git diff --staged)" &&
+ git diff > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Fail conflicting merge with conflicting dirty worktree' '
+ test_must_fail git merge conflict_branch &&
+ test ! -f .git/MERGE_HEAD &&
+ test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+ test -z "$(git diff --staged)" &&
+ git diff > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Reset worktree changes' '
+ git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Fail clean merge with matching dirty worktree' '
+ echo bart > bar &&
+ git diff > expect &&
+ test_must_fail git merge --no-commit clean_branch &&
+ test ! -f .git/MERGE_HEAD &&
+ test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+ test -z "$(git diff --staged)" &&
+ git diff > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Abort clean merge with matching dirty index' '
+ git add bar &&
+ git diff --staged > expect &&
+ git merge --no-commit clean_branch &&
+ test -f .git/MERGE_HEAD &&
+ ### When aborting the merge, git will discard all staged changes,
+ ### including those that were staged pre-merge. In other words,
+ ### --abort will LOSE any staged changes (the staged changes that
+ ### are lost must match the merge result, or the merge would not
+ ### have been allowed to start). Change expectations accordingly:
+ rm expect &&
+ touch expect &&
+ # Abort merge
+ git merge --abort &&
+ test ! -f .git/MERGE_HEAD &&
+ test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+ git diff --staged > actual &&
+ test_cmp expect actual &&
+ test -z "$(git diff)"
+'
+
+test_expect_success 'Reset worktree changes' '
+ git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Fail conflicting merge with matching dirty worktree' '
+ echo barf > bar &&
+ git diff > expect &&
+ test_must_fail git merge conflict_branch &&
+ test ! -f .git/MERGE_HEAD &&
+ test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+ test -z "$(git diff --staged)" &&
+ git diff > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Abort conflicting merge with matching dirty index' '
+ git add bar &&
+ git diff --staged > expect &&
+ test_must_fail git merge conflict_branch &&
+ test -f .git/MERGE_HEAD &&
+ ### When aborting the merge, git will discard all staged changes,
+ ### including those that were staged pre-merge. In other words,
+ ### --abort will LOSE any staged changes (the staged changes that
+ ### are lost must match the merge result, or the merge would not
+ ### have been allowed to start). Change expectations accordingly:
+ rm expect &&
+ touch expect &&
+ # Abort merge
+ git merge --abort &&
+ test ! -f .git/MERGE_HEAD &&
+ test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+ git diff --staged > actual &&
+ test_cmp expect actual &&
+ test -z "$(git diff)"
+'
+
+test_expect_success 'Reset worktree changes' '
+ git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Abort merge with pre- and post-merge worktree changes' '
+ # Pre-merge worktree changes
+ echo xyzzy > foo &&
+ echo barf > bar &&
+ git add bar &&
+ git diff > expect &&
+ git diff --staged > expect-staged &&
+ # Perform merge
+ test_must_fail git merge conflict_branch &&
+ test -f .git/MERGE_HEAD &&
+ # Post-merge worktree changes
+ echo yzxxz > foo &&
+ echo blech > baz &&
+ ### When aborting the merge, git will discard staged changes (bar)
+ ### and unmerged changes (baz). Other changes that are neither
+ ### staged nor marked as unmerged (foo), will be preserved. For
+ ### these changed, git cannot tell pre-merge changes apart from
+ ### post-merge changes, so the post-merge changes will be
+ ### preserved. Change expectations accordingly:
+ git diff -- foo > expect &&
+ rm expect-staged &&
+ touch expect-staged &&
+ # Abort merge
+ git merge --abort &&
+ test ! -f .git/MERGE_HEAD &&
+ test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+ git diff > actual &&
+ test_cmp expect actual &&
+ git diff --staged > actual-staged &&
+ test_cmp expect-staged actual-staged
+'
+
+test_expect_success 'Reset worktree changes' '
+ git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Abort merge with pre- and post-merge index changes' '
+ # Pre-merge worktree changes
+ echo xyzzy > foo &&
+ echo barf > bar &&
+ git add bar &&
+ git diff > expect &&
+ git diff --staged > expect-staged &&
+ # Perform merge
+ test_must_fail git merge conflict_branch &&
+ test -f .git/MERGE_HEAD &&
+ # Post-merge worktree changes
+ echo yzxxz > foo &&
+ echo blech > baz &&
+ git add foo bar &&
+ ### When aborting the merge, git will discard all staged changes
+ ### (foo, bar and baz), and no changes will be preserved. Whether
+ ### the changes were staged pre- or post-merge does not matter
+ ### (except for not preventing starting the merge).
+ ### Change expectations accordingly:
+ rm expect expect-staged &&
+ touch expect &&
+ touch expect-staged &&
+ # Abort merge
+ git merge --abort &&
+ test ! -f .git/MERGE_HEAD &&
+ test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+ git diff > actual &&
+ test_cmp expect actual &&
+ git diff --staged > actual-staged &&
+ test_cmp expect-staged actual-staged
+'
+
+test_done
diff --git a/t/t7612-merge-verify-signatures.sh b/t/t7612-merge-verify-signatures.sh
new file mode 100755
index 0000000..8ae69a6
--- /dev/null
+++ b/t/t7612-merge-verify-signatures.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+test_description='merge signature verification tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success GPG 'create signed commits' '
+ echo 1 >file && git add file &&
+ test_tick && git commit -m initial &&
+ git tag initial &&
+
+ git checkout -b side-signed &&
+ echo 3 >elif && git add elif &&
+ test_tick && git commit -S -m "signed on side" &&
+ git checkout initial &&
+
+ git checkout -b side-unsigned &&
+ echo 3 >foo && git add foo &&
+ test_tick && git commit -m "unsigned on side" &&
+ git checkout initial &&
+
+ git checkout -b side-bad &&
+ echo 3 >bar && git add bar &&
+ test_tick && git commit -S -m "bad on side" &&
+ git cat-file commit side-bad >raw &&
+ sed -e "s/bad/forged bad/" raw >forged &&
+ git hash-object -w -t commit forged >forged.commit &&
+ git checkout initial &&
+
+ git checkout -b side-untrusted &&
+ echo 3 >baz && git add baz &&
+ test_tick && git commit -SB7227189 -m "untrusted on side" &&
+
+ git checkout master
+'
+
+test_expect_success GPG 'merge unsigned commit with verification' '
+ test_must_fail git merge --ff-only --verify-signatures side-unsigned 2>mergeerror &&
+ test_i18ngrep "does not have a GPG signature" mergeerror
+'
+
+test_expect_success GPG 'merge commit with bad signature with verification' '
+ test_must_fail git merge --ff-only --verify-signatures $(cat forged.commit) 2>mergeerror &&
+ test_i18ngrep "has a bad GPG signature" mergeerror
+'
+
+test_expect_success GPG 'merge commit with untrusted signature with verification' '
+ test_must_fail git merge --ff-only --verify-signatures side-untrusted 2>mergeerror &&
+ test_i18ngrep "has an untrusted GPG signature" mergeerror
+'
+
+test_expect_success GPG 'merge signed commit with verification' '
+ git merge --verbose --ff-only --verify-signatures side-signed >mergeoutput &&
+ test_i18ngrep "has a good GPG signature" mergeoutput
+'
+
+test_expect_success GPG 'merge commit with bad signature without verification' '
+ git merge $(cat forged.commit)
+'
+
+test_done
diff --git a/t/t7613-merge-submodule.sh b/t/t7613-merge-submodule.sh
new file mode 100755
index 0000000..d1e9fcc
--- /dev/null
+++ b/t/t7613-merge-submodule.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_description='merge can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+# merges without conflicts
+test_submodule_switch "git merge"
+
+test_submodule_switch "git merge --ff"
+
+test_submodule_switch "git merge --ff-only"
+
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+test_submodule_switch "git merge --no-ff"
+
+test_done
diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
index c2f66ff..6061a04 100755
--- a/t/t7700-repack.sh
+++ b/t/t7700-repack.sh
@@ -17,7 +17,7 @@ test_expect_success 'objects in packs marked .keep are not repacked' '
# 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 &&
+ >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/ &&
@@ -35,9 +35,41 @@ test_expect_success 'objects in packs marked .keep are not repacked' '
test -z "$found_duplicate_object"
'
+test_expect_success 'writing bitmaps via command-line can duplicate .keep objects' '
+ # build on $objsha1, $packsha1, and .keep state from previous
+ git repack -Adbl &&
+ test_when_finished "found_duplicate_object=" &&
+ 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 "$found_duplicate_object" = 1
+'
+
+test_expect_success 'writing bitmaps via config can duplicate .keep objects' '
+ # build on $objsha1, $packsha1, and .keep state from previous
+ git -c repack.writebitmaps=true repack -Adl &&
+ test_when_finished "found_duplicate_object=" &&
+ 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 "$found_duplicate_object" = 1
+'
+
test_expect_success 'loose objects in alternate ODB are not repacked' '
mkdir alt_objects &&
- echo `pwd`/alt_objects > .git/objects/info/alternates &&
+ 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 &&
@@ -56,7 +88,7 @@ test_expect_success 'loose objects in alternate ODB are not repacked' '
'
test_expect_success 'packed obs in alt ODB are repacked even when local repo is packless' '
- mkdir alt_objects/pack
+ mkdir alt_objects/pack &&
mv .git/objects/pack/* alt_objects/pack &&
git repack -a &&
myidx=$(ls -1 .git/objects/pack/*.idx) &&
@@ -95,14 +127,14 @@ 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)
+ 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
+ done &&
git repack -a -d &&
myidx=$(ls -1 .git/objects/pack/*.idx) &&
test -f "$myidx" &&
@@ -136,7 +168,7 @@ test_expect_success 'packed unreachable obs in alternate ODB are not loosened' '
'
test_expect_success 'local packed unreachable obs that exist in alternate ODB are not loosened' '
- echo `pwd`/alt_objects > .git/objects/info/alternates &&
+ 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/ &&
diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh
index 200ab61..b66e383 100755
--- a/t/t7701-repack-unpack-unreachable.sh
+++ b/t/t7701-repack-unpack-unreachable.sh
@@ -57,7 +57,7 @@ compare_mtimes ()
{
read tref rest &&
while read t rest; do
- test "$tref" = "$t" || break
+ test "$tref" = "$t" || return 1
done
}
@@ -95,4 +95,31 @@ test_expect_success 'unpacked objects receive timestamp of pack file' '
compare_mtimes < mtimes
'
+test_expect_success 'do not bother loosening old objects' '
+ obj1=$(echo one | git hash-object -w --stdin) &&
+ obj2=$(echo two | git hash-object -w --stdin) &&
+ pack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&
+ pack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&
+ git prune-packed &&
+ git cat-file -p $obj1 &&
+ git cat-file -p $obj2 &&
+ test-chmtime =-86400 .git/objects/pack/pack-$pack2.pack &&
+ git repack -A -d --unpack-unreachable=1.hour.ago &&
+ git cat-file -p $obj1 &&
+ test_must_fail git cat-file -p $obj2
+'
+
+test_expect_success 'keep packed objects found only in index' '
+ echo my-unique-content >file &&
+ git add file &&
+ git commit -m "make it reachable" &&
+ git gc &&
+ git reset HEAD^ &&
+ git reflog expire --expire=now --all &&
+ git add file &&
+ test-chmtime =-86400 .git/objects/pack/* &&
+ git gc --prune=1.hour.ago &&
+ git cat-file blob :file
+'
+
test_done
diff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh
new file mode 100755
index 0000000..93b7486
--- /dev/null
+++ b/t/t7702-repack-cyclic-alternate.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 Ephrim Khong
+#
+
+test_description='repack involving cyclic alternate'
+. ./test-lib.sh
+
+test_expect_success setup '
+ GIT_OBJECT_DIRECTORY=.git//../.git/objects &&
+ export GIT_OBJECT_DIRECTORY &&
+ touch a &&
+ git add a &&
+ git commit -m 1 &&
+ git repack -adl &&
+ echo "$(pwd)"/.git/objects/../objects >.git/objects/info/alternates
+'
+
+test_expect_success 're-packing repository with itsself as alternate' '
+ git repack -adl &&
+ git fsck
+'
+
+test_done
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index 196827e..42a2929 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -1,6 +1,6 @@
#!/bin/sh
#
-# Copyright (c) 2009, 2010 David Aguilar
+# Copyright (c) 2009, 2010, 2012, 2013 David Aguilar
#
test_description='git-difftool
@@ -10,47 +10,21 @@ 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()
+difftool_test_setup ()
{
- # Unset all config variables used by git-difftool
- git config --unset diff.tool
- git config --unset diff.guitool
- git config --unset difftool.test-tool.cmd
- git config --unset difftool.prompt
- git config --unset merge.tool
- git config --unset mergetool.test-tool.cmd
- git config --unset mergetool.prompt
- return 0
+ test_config diff.tool test-tool &&
+ test_config difftool.test-tool.cmd 'cat "$LOCAL"' &&
+ test_config difftool.bogus-tool.cmd false
}
-restore_test_defaults()
-{
- # Restores the test defaults used by several tests
- remove_config_vars
- unset GIT_DIFF_TOOL
- unset GIT_DIFFTOOL_PROMPT
- unset GIT_DIFFTOOL_NO_PROMPT
- git config diff.tool test-tool &&
- git config difftool.test-tool.cmd 'cat $LOCAL'
- git config difftool.bogus-tool.cmd false
-}
-
-prompt_given()
+prompt_given ()
{
prompt="$1"
- test "$prompt" = "Hit return to launch 'test-tool': branch"
+ test "$prompt" = "Launch 'test-tool' [Y/n]? branch"
}
# Create a file on master and change it on branch
-test_expect_success 'setup' '
+test_expect_success PERL 'setup' '
echo master >file &&
git add file &&
git commit -m "added file" &&
@@ -62,215 +36,514 @@ test_expect_success 'setup' '
'
# 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"
+test_expect_success PERL 'custom commands' '
+ difftool_test_setup &&
+ test_config difftool.test-tool.cmd "cat \"\$REMOTE\"" &&
+ echo master >expect &&
+ git difftool --no-prompt branch >actual &&
+ test_cmp expect actual &&
+
+ test_config difftool.test-tool.cmd "cat \"\$LOCAL\"" &&
+ echo branch >expect &&
+ git difftool --no-prompt branch >actual &&
+ test_cmp expect actual
'
-# 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 PERL 'custom tool commands override built-ins' '
+ test_config difftool.vimdiff.cmd "cat \"\$REMOTE\"" &&
+ echo master >expect &&
+ git difftool --tool vimdiff --no-prompt branch >actual &&
+ test_cmp expect actual
'
-test_expect_success 'difftool honors --gui' '
- git config merge.tool bogus-tool &&
- git config diff.tool bogus-tool &&
- git config diff.guitool test-tool &&
+test_expect_success PERL 'difftool ignores bad --tool values' '
+ : >expect &&
+ test_must_fail \
+ git difftool --no-prompt --tool=bad-tool branch >actual &&
+ test_cmp expect actual
+'
- diff=$(git difftool --no-prompt --gui branch) &&
- test "$diff" = "branch" &&
+test_expect_success PERL 'difftool forwards arguments to diff' '
+ difftool_test_setup &&
+ >for-diff &&
+ git add for-diff &&
+ echo changes>for-diff &&
+ git add for-diff &&
+ : >expect &&
+ git difftool --cached --no-prompt -- for-diff >actual &&
+ test_cmp expect actual &&
+ git reset -- for-diff &&
+ rm for-diff
+'
- restore_test_defaults
+test_expect_success PERL 'difftool ignores exit code' '
+ test_config difftool.error.cmd false &&
+ git difftool -y -t error branch
'
-test_expect_success 'difftool --gui works without configured diff.guitool' '
- git config diff.tool test-tool &&
+test_expect_success PERL 'difftool forwards exit code with --trust-exit-code' '
+ test_config difftool.error.cmd false &&
+ test_must_fail git difftool -y --trust-exit-code -t error branch
+'
- diff=$(git difftool --no-prompt --gui branch) &&
- test "$diff" = "branch" &&
+test_expect_success PERL 'difftool forwards exit code with --trust-exit-code for built-ins' '
+ test_config difftool.vimdiff.path false &&
+ test_must_fail git difftool -y --trust-exit-code -t vimdiff branch
+'
- restore_test_defaults
+test_expect_success PERL 'difftool honors difftool.trustExitCode = true' '
+ test_config difftool.error.cmd false &&
+ test_config difftool.trustExitCode true &&
+ test_must_fail git difftool -y -t error branch
'
-# 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 &&
+test_expect_success PERL 'difftool honors difftool.trustExitCode = false' '
+ test_config difftool.error.cmd false &&
+ test_config difftool.trustExitCode false &&
+ git difftool -y -t error branch
+'
- diff=$(git difftool --no-prompt branch) &&
- test "$diff" = "branch" &&
+test_expect_success PERL 'difftool ignores exit code with --no-trust-exit-code' '
+ test_config difftool.error.cmd false &&
+ test_config difftool.trustExitCode true &&
+ git difftool -y --no-trust-exit-code -t error branch
+'
- restore_test_defaults
+test_expect_success PERL 'difftool stops on error with --trust-exit-code' '
+ test_when_finished "rm -f for-diff .git/fail-right-file" &&
+ test_when_finished "git reset -- for-diff" &&
+ write_script .git/fail-right-file <<-\EOF &&
+ echo "$2"
+ exit 1
+ EOF
+ >for-diff &&
+ git add for-diff &&
+ echo file >expect &&
+ test_must_fail git difftool -y --trust-exit-code \
+ --extcmd .git/fail-right-file branch >actual &&
+ test_cmp expect actual
'
-# Test the $GIT_*_TOOL variables and ensure
-# that $GIT_DIFF_TOOL always wins unless --tool is specified
-test_expect_success 'GIT_DIFF_TOOL overrides' '
- git config diff.tool bogus-tool &&
- git config merge.tool bogus-tool &&
+test_expect_success PERL 'difftool honors --gui' '
+ difftool_test_setup &&
+ test_config merge.tool bogus-tool &&
+ test_config diff.tool bogus-tool &&
+ test_config diff.guitool test-tool &&
- GIT_DIFF_TOOL=test-tool &&
- export GIT_DIFF_TOOL &&
+ echo branch >expect &&
+ git difftool --no-prompt --gui branch >actual &&
+ test_cmp expect actual
+'
- diff=$(git difftool --no-prompt branch) &&
- test "$diff" = "branch" &&
+test_expect_success PERL 'difftool --gui last setting wins' '
+ difftool_test_setup &&
+ : >expect &&
+ git difftool --no-prompt --gui --no-gui >actual &&
+ test_cmp expect actual &&
+
+ test_config merge.tool bogus-tool &&
+ test_config diff.tool bogus-tool &&
+ test_config diff.guitool test-tool &&
+ echo branch >expect &&
+ git difftool --no-prompt --no-gui --gui branch >actual &&
+ test_cmp expect actual
+'
- GIT_DIFF_TOOL=bogus-tool &&
- export GIT_DIFF_TOOL &&
+test_expect_success PERL 'difftool --gui works without configured diff.guitool' '
+ difftool_test_setup &&
+ echo branch >expect &&
+ git difftool --no-prompt --gui branch >actual &&
+ test_cmp expect actual
+'
- diff=$(git difftool --no-prompt --tool=test-tool branch) &&
- test "$diff" = "branch" &&
+# Specify the diff tool using $GIT_DIFF_TOOL
+test_expect_success PERL 'GIT_DIFF_TOOL variable' '
+ difftool_test_setup &&
+ git config --unset diff.tool &&
+ echo branch >expect &&
+ GIT_DIFF_TOOL=test-tool git difftool --no-prompt branch >actual &&
+ test_cmp expect actual
+'
- restore_test_defaults
+# Test the $GIT_*_TOOL variables and ensure
+# that $GIT_DIFF_TOOL always wins unless --tool is specified
+test_expect_success PERL 'GIT_DIFF_TOOL overrides' '
+ difftool_test_setup &&
+ test_config diff.tool bogus-tool &&
+ test_config merge.tool bogus-tool &&
+
+ echo branch >expect &&
+ GIT_DIFF_TOOL=test-tool git difftool --no-prompt branch >actual &&
+ test_cmp expect actual &&
+
+ test_config diff.tool bogus-tool &&
+ test_config merge.tool bogus-tool &&
+ GIT_DIFF_TOOL=bogus-tool \
+ git difftool --no-prompt --tool=test-tool branch >actual &&
+ test_cmp expect actual
'
# Test that we don't have to pass --no-prompt to difftool
# when $GIT_DIFFTOOL_NO_PROMPT is true
-test_expect_success '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
+test_expect_success PERL 'GIT_DIFFTOOL_NO_PROMPT variable' '
+ difftool_test_setup &&
+ echo branch >expect &&
+ GIT_DIFFTOOL_NO_PROMPT=true git difftool branch >actual &&
+ test_cmp expect actual
'
# git-difftool supports the difftool.prompt variable.
# Test that GIT_DIFFTOOL_PROMPT can override difftool.prompt = false
-test_expect_success 'GIT_DIFFTOOL_PROMPT variable' '
- git config difftool.prompt false &&
- GIT_DIFFTOOL_PROMPT=true &&
- export GIT_DIFFTOOL_PROMPT &&
+test_expect_success PERL 'GIT_DIFFTOOL_PROMPT variable' '
+ difftool_test_setup &&
+ test_config difftool.prompt false &&
+ echo >input &&
+ GIT_DIFFTOOL_PROMPT=true git difftool branch <input >output &&
+ prompt=$(tail -1 <output) &&
+ prompt_given "$prompt"
+'
- prompt=$(echo | git difftool branch | tail -1) &&
- prompt_given "$prompt" &&
+# Test that we don't have to pass --no-prompt when difftool.prompt is false
+test_expect_success PERL 'difftool.prompt config variable is false' '
+ difftool_test_setup &&
+ test_config difftool.prompt false &&
+ echo branch >expect &&
+ git difftool branch >actual &&
+ test_cmp expect actual
+'
- restore_test_defaults
+# Test that we don't have to pass --no-prompt when mergetool.prompt is false
+test_expect_success PERL 'difftool merge.prompt = false' '
+ difftool_test_setup &&
+ test_might_fail git config --unset difftool.prompt &&
+ test_config mergetool.prompt false &&
+ echo branch >expect &&
+ git difftool branch >actual &&
+ test_cmp expect actual
'
-# 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 &&
+# Test that the -y flag can override difftool.prompt = true
+test_expect_success PERL 'difftool.prompt can overridden with -y' '
+ difftool_test_setup &&
+ test_config difftool.prompt true &&
+ echo branch >expect &&
+ git difftool -y branch >actual &&
+ test_cmp expect actual
+'
- diff=$(git difftool branch) &&
- test "$diff" = "branch" &&
+# Test that the --prompt flag can override difftool.prompt = false
+test_expect_success PERL 'difftool.prompt can overridden with --prompt' '
+ difftool_test_setup &&
+ test_config difftool.prompt false &&
+ echo >input &&
+ git difftool --prompt branch <input >output &&
+ prompt=$(tail -1 <output) &&
+ prompt_given "$prompt"
+'
- restore_test_defaults
+# Test that the last flag passed on the command-line wins
+test_expect_success PERL 'difftool last flag wins' '
+ difftool_test_setup &&
+ echo branch >expect &&
+ git difftool --prompt --no-prompt branch >actual &&
+ test_cmp expect actual &&
+ echo >input &&
+ git difftool --no-prompt --prompt branch <input >output &&
+ prompt=$(tail -1 <output) &&
+ prompt_given "$prompt"
'
-# 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 &&
+# git-difftool falls back to git-mergetool config variables
+# so test that behavior here
+test_expect_success PERL 'difftool + mergetool config variables' '
+ test_config merge.tool test-tool &&
+ test_config mergetool.test-tool.cmd "cat \$LOCAL" &&
+ echo branch >expect &&
+ git difftool --no-prompt branch >actual &&
+ test_cmp expect actual &&
- diff=$(git difftool branch) &&
- test "$diff" = "branch" &&
+ # set merge.tool to something bogus, diff.tool to test-tool
+ test_config merge.tool bogus-tool &&
+ test_config diff.tool test-tool &&
+ git difftool --no-prompt branch >actual &&
+ test_cmp expect actual
+'
- restore_test_defaults
+test_expect_success PERL 'difftool.<tool>.path' '
+ test_config difftool.tkdiff.path echo &&
+ git difftool --tool=tkdiff --no-prompt branch >output &&
+ lines=$(grep file output | wc -l) &&
+ test "$lines" -eq 1
'
-# Test that the -y flag can override difftool.prompt = true
-test_expect_success 'difftool.prompt can overridden with -y' '
- git config difftool.prompt true &&
+test_expect_success PERL 'difftool --extcmd=cat' '
+ echo branch >expect &&
+ echo master >>expect &&
+ git difftool --no-prompt --extcmd=cat branch >actual &&
+ test_cmp expect actual
+'
- diff=$(git difftool -y branch) &&
- test "$diff" = "branch" &&
+test_expect_success PERL 'difftool --extcmd cat' '
+ echo branch >expect &&
+ echo master >>expect &&
+ git difftool --no-prompt --extcmd=cat branch >actual &&
+ test_cmp expect actual
+'
- restore_test_defaults
+test_expect_success PERL 'difftool -x cat' '
+ echo branch >expect &&
+ echo master >>expect &&
+ git difftool --no-prompt -x cat branch >actual &&
+ test_cmp expect actual
'
-# Test that the --prompt flag can override difftool.prompt = false
-test_expect_success 'difftool.prompt can overridden with --prompt' '
- git config difftool.prompt false &&
+test_expect_success PERL 'difftool --extcmd echo arg1' '
+ echo file >expect &&
+ git difftool --no-prompt \
+ --extcmd sh\ -c\ \"echo\ \$1\" branch >actual &&
+ test_cmp expect actual
+'
- prompt=$(echo | git difftool --prompt branch | tail -1) &&
- prompt_given "$prompt" &&
+test_expect_success PERL 'difftool --extcmd cat arg1' '
+ echo master >expect &&
+ git difftool --no-prompt \
+ --extcmd sh\ -c\ \"cat\ \$1\" branch >actual &&
+ test_cmp expect actual
+'
- restore_test_defaults
+test_expect_success PERL 'difftool --extcmd cat arg2' '
+ echo branch >expect &&
+ git difftool --no-prompt \
+ --extcmd sh\ -c\ \"cat\ \$2\" branch >actual &&
+ test_cmp expect actual
'
-# 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" &&
+# Create a second file on master and a different version on branch
+test_expect_success PERL 'setup with 2 files different' '
+ echo m2 >file2 &&
+ git add file2 &&
+ git commit -m "added file2" &&
- restore_test_defaults &&
+ git checkout branch &&
+ echo br2 >file2 &&
+ git add file2 &&
+ git commit -a -m "branch changed file2" &&
+ git checkout master
+'
- prompt=$(echo | git difftool --no-prompt --prompt branch | tail -1) &&
- prompt_given "$prompt" &&
+test_expect_success PERL 'say no to the first file' '
+ (echo n && echo) >input &&
+ git difftool -x cat branch <input >output &&
+ grep m2 output &&
+ grep br2 output &&
+ ! grep master output &&
+ ! grep branch output
+'
- restore_test_defaults
+test_expect_success PERL 'say no to the second file' '
+ (echo && echo n) >input &&
+ git difftool -x cat branch <input >output &&
+ grep master output &&
+ grep branch output &&
+ ! grep m2 output &&
+ ! grep br2 output
'
-# 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" &&
+test_expect_success PERL 'ending prompt input with EOF' '
+ git difftool -x cat branch </dev/null >output &&
+ ! grep master output &&
+ ! grep branch output &&
+ ! grep m2 output &&
+ ! grep br2 output
+'
- diff=$(git difftool --no-prompt branch) &&
- test "$diff" = "branch" &&
+test_expect_success PERL 'difftool --tool-help' '
+ git difftool --tool-help >output &&
+ grep tool output
+'
- # set merge.tool to something bogus, diff.tool to test-tool
- git config merge.tool bogus-tool &&
- git config diff.tool test-tool &&
+test_expect_success PERL 'setup change in subdirectory' '
+ git checkout master &&
+ mkdir sub &&
+ echo master >sub/sub &&
+ git add sub/sub &&
+ git commit -m "added sub/sub" &&
+ echo test >>file &&
+ echo test >>sub/sub &&
+ git add file sub/sub &&
+ git commit -m "modified both"
+'
- diff=$(git difftool --no-prompt branch) &&
- test "$diff" = "branch" &&
+run_dir_diff_test () {
+ test_expect_success PERL "$1 --no-symlinks" "
+ symlinks=--no-symlinks &&
+ $2
+ "
+ test_expect_success PERL,SYMLINKS "$1 --symlinks" "
+ symlinks=--symlinks &&
+ $2
+ "
+}
- restore_test_defaults
+run_dir_diff_test 'difftool -d' '
+ git difftool -d $symlinks --extcmd ls branch >output &&
+ grep sub output &&
+ grep file output
'
-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 &&
+run_dir_diff_test 'difftool --dir-diff' '
+ git difftool --dir-diff $symlinks --extcmd ls branch >output &&
+ grep sub output &&
+ grep file output
+'
- restore_test_defaults
+run_dir_diff_test 'difftool --dir-diff ignores --prompt' '
+ git difftool --dir-diff $symlinks --prompt --extcmd ls branch >output &&
+ grep sub output &&
+ grep file output
'
-test_expect_success 'difftool --extcmd=cat' '
- diff=$(git difftool --no-prompt --extcmd=cat branch) &&
- test "$diff" = branch"$LF"master
+run_dir_diff_test 'difftool --dir-diff from subdirectory' '
+ (
+ cd sub &&
+ git difftool --dir-diff $symlinks --extcmd ls branch >output &&
+ grep sub output &&
+ grep file output
+ )
'
-test_expect_success 'difftool --extcmd cat' '
- diff=$(git difftool --no-prompt --extcmd cat branch) &&
- test "$diff" = branch"$LF"master
+run_dir_diff_test 'difftool --dir-diff when worktree file is missing' '
+ test_when_finished git reset --hard &&
+ rm file2 &&
+ git difftool --dir-diff $symlinks --extcmd ls branch master >output &&
+ grep file2 output
+'
+
+run_dir_diff_test 'difftool --dir-diff with unmerged files' '
+ test_when_finished git reset --hard &&
+ test_config difftool.echo.cmd "echo ok" &&
+ git checkout -B conflict-a &&
+ git checkout -B conflict-b &&
+ git checkout conflict-a &&
+ echo a >>file &&
+ git add file &&
+ git commit -m conflict-a &&
+ git checkout conflict-b &&
+ echo b >>file &&
+ git add file &&
+ git commit -m conflict-b &&
+ git checkout master &&
+ git merge conflict-a &&
+ test_must_fail git merge conflict-b &&
+ cat >expect <<-EOF &&
+ ok
+ EOF
+ git difftool --dir-diff $symlinks -t echo >actual &&
+ test_cmp expect actual
'
-test_expect_success 'difftool -x cat' '
- diff=$(git difftool --no-prompt -x cat branch) &&
- test "$diff" = branch"$LF"master
+write_script .git/CHECK_SYMLINKS <<\EOF
+for f in file file2 sub/sub
+do
+ echo "$f"
+ ls -ld "$2/$f" | sed -e 's/.* -> //'
+done >actual
+EOF
+
+test_expect_success PERL,SYMLINKS 'difftool --dir-diff --symlink without unstaged changes' '
+ cat >expect <<-EOF &&
+ file
+ $PWD/file
+ file2
+ $PWD/file2
+ sub/sub
+ $PWD/sub/sub
+ EOF
+ git difftool --dir-diff --symlink \
+ --extcmd "./.git/CHECK_SYMLINKS" branch HEAD &&
+ test_cmp actual expect
+'
+
+write_script modify-right-file <<\EOF
+echo "new content" >"$2/file"
+EOF
+
+run_dir_diff_test 'difftool --dir-diff syncs worktree with unstaged change' '
+ test_when_finished git reset --hard &&
+ echo "orig content" >file &&
+ git difftool -d $symlinks --extcmd "$PWD/modify-right-file" branch &&
+ echo "new content" >expect &&
+ test_cmp expect file
+'
+
+run_dir_diff_test 'difftool --dir-diff syncs worktree without unstaged change' '
+ test_when_finished git reset --hard &&
+ git difftool -d $symlinks --extcmd "$PWD/modify-right-file" branch &&
+ echo "new content" >expect &&
+ test_cmp expect file
+'
+
+write_script modify-file <<\EOF
+echo "new content" >file
+EOF
+
+test_expect_success PERL 'difftool --no-symlinks does not overwrite working tree file ' '
+ echo "orig content" >file &&
+ git difftool --dir-diff --no-symlinks --extcmd "$PWD/modify-file" branch &&
+ echo "new content" >expect &&
+ test_cmp expect file
'
-test_expect_success 'difftool --extcmd echo arg1' '
- diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"echo\ \$1\" branch)
- test "$diff" = file
+write_script modify-both-files <<\EOF
+echo "wt content" >file &&
+echo "tmp content" >"$2/file" &&
+echo "$2" >tmpdir
+EOF
+
+test_expect_success PERL 'difftool --no-symlinks detects conflict ' '
+ (
+ TMPDIR=$TRASH_DIRECTORY &&
+ export TMPDIR &&
+ echo "orig content" >file &&
+ test_must_fail git difftool --dir-diff --no-symlinks --extcmd "$PWD/modify-both-files" branch &&
+ echo "wt content" >expect &&
+ test_cmp expect file &&
+ echo "tmp content" >expect &&
+ test_cmp expect "$(cat tmpdir)/file"
+ )
'
-test_expect_success 'difftool --extcmd cat arg1' '
- diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"cat\ \$1\" branch)
- test "$diff" = master
+test_expect_success PERL 'difftool properly honors gitlink and core.worktree' '
+ git submodule add ./. submod/ule &&
+ test_config -C submod/ule diff.tool checktrees &&
+ test_config -C submod/ule difftool.checktrees.cmd '\''
+ test -d "$LOCAL" && test -d "$REMOTE" && echo good
+ '\'' &&
+ (
+ cd submod/ule &&
+ echo good >expect &&
+ git difftool --tool=checktrees --dir-diff HEAD~ >actual &&
+ test_cmp expect actual
+ )
'
-test_expect_success 'difftool --extcmd cat arg2' '
- diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"cat\ \$2\" branch)
- test "$diff" = branch
+test_expect_success PERL,SYMLINKS 'difftool --dir-diff symlinked directories' '
+ git init dirlinks &&
+ (
+ cd dirlinks &&
+ git config diff.tool checktrees &&
+ git config difftool.checktrees.cmd "echo good" &&
+ mkdir foo &&
+ : >foo/bar &&
+ git add foo/bar &&
+ test_commit symlink-one &&
+ ln -s foo link &&
+ git add link &&
+ test_commit symlink-two &&
+ echo good >expect &&
+ git difftool --tool=checktrees --dir-diff HEAD~ >actual &&
+ test_cmp expect actual
+ )
'
test_done
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index 8a63227..cf3f9ec 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -9,7 +9,9 @@ test_description='git grep various.
. ./test-lib.sh
cat >hello.c <<EOF
+#include <assert.h>
#include <stdio.h>
+
int main(int argc, const char **argv)
{
printf("Hello world.\n");
@@ -26,6 +28,17 @@ test_expect_success setup '
echo foo mmap bar_mmap
echo foo_mmap bar mmap baz
} >file &&
+ {
+ echo Hello world
+ echo HeLLo world
+ echo Hello_world
+ echo HeLLo_world
+ } >hello_world &&
+ {
+ echo "a+b*c"
+ echo "a+bc"
+ echo "abc"
+ } >ab &&
echo vvv >v &&
echo ww w >w &&
echo x x xx x >x &&
@@ -36,6 +49,13 @@ test_expect_success setup '
echo vvv >t/v &&
mkdir t/a &&
echo vvv >t/a/v &&
+ {
+ echo "line without leading space1"
+ echo " line with leading space1"
+ echo " line with leading space2"
+ echo " line with leading space3"
+ echo "line without leading space2"
+ } >space &&
git add . &&
test_tick &&
git commit -m initial
@@ -59,13 +79,35 @@ do
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 &&
+ git -c grep.linenumber=false grep -n -w -e mmap $H >actual &&
+ test_cmp expected actual
+ '
+
+ 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 -c grep.linenumber=true grep -w -e mmap $H >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep -w $L" '
+ {
+ echo ${HC}file:foo mmap bar
+ echo ${HC}file:foo_mmap bar mmap
+ echo ${HC}file:foo mmap bar_mmap
+ echo ${HC}file:foo_mmap bar mmap baz
+ } >expected &&
+ git -c grep.linenumber=true grep --no-line-number -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_must_fail git grep -n -w -e "^w" $H >actual &&
test_cmp expected actual
'
@@ -135,7 +177,7 @@ do
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" '
{
@@ -182,9 +224,149 @@ do
test_cmp expected actual
'
+ test_expect_success "grep --max-depth 0 -- . t $L" '
+ {
+ echo ${HC}t/v:1:vvv
+ echo ${HC}v:1:vvv
+ } >expected &&
+ git grep --max-depth 0 -n -e vvv $H -- . t >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep --max-depth 0 -- t . $L" '
+ {
+ echo ${HC}t/v:1:vvv
+ echo ${HC}v:1:vvv
+ } >expected &&
+ git grep --max-depth 0 -n -e vvv $H -- t . >actual &&
+ test_cmp expected actual
+ '
+ test_expect_success "grep $L with grep.extendedRegexp=false" '
+ echo "${HC}ab:a+bc" >expected &&
+ git -c grep.extendedRegexp=false grep "a+b*c" $H ab >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep $L with grep.extendedRegexp=true" '
+ echo "${HC}ab:abc" >expected &&
+ git -c grep.extendedRegexp=true grep "a+b*c" $H ab >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep $L with grep.patterntype=basic" '
+ echo "${HC}ab:a+bc" >expected &&
+ git -c grep.patterntype=basic grep "a+b*c" $H ab >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep $L with grep.patterntype=extended" '
+ echo "${HC}ab:abc" >expected &&
+ git -c grep.patterntype=extended grep "a+b*c" $H ab >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep $L with grep.patterntype=fixed" '
+ echo "${HC}ab:a+b*c" >expected &&
+ git -c grep.patterntype=fixed grep "a+b*c" $H ab >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success LIBPCRE "grep $L with grep.patterntype=perl" '
+ echo "${HC}ab:a+b*c" >expected &&
+ git -c grep.patterntype=perl grep "a\x{2b}b\x{2a}c" $H ab >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep $L with grep.patternType=default and grep.extendedRegexp=true" '
+ echo "${HC}ab:abc" >expected &&
+ git \
+ -c grep.patternType=default \
+ -c grep.extendedRegexp=true \
+ grep "a+b*c" $H ab >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep $L with grep.extendedRegexp=true and grep.patternType=default" '
+ echo "${HC}ab:abc" >expected &&
+ git \
+ -c grep.extendedRegexp=true \
+ -c grep.patternType=default \
+ grep "a+b*c" $H ab >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep $L with grep.patternType=extended and grep.extendedRegexp=false" '
+ echo "${HC}ab:abc" >expected &&
+ git \
+ -c grep.patternType=extended \
+ -c grep.extendedRegexp=false \
+ grep "a+b*c" $H ab >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep $L with grep.patternType=basic and grep.extendedRegexp=true" '
+ echo "${HC}ab:a+bc" >expected &&
+ git \
+ -c grep.patternType=basic \
+ -c grep.extendedRegexp=true \
+ grep "a+b*c" $H ab >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep $L with grep.extendedRegexp=false and grep.patternType=extended" '
+ echo "${HC}ab:abc" >expected &&
+ git \
+ -c grep.extendedRegexp=false \
+ -c grep.patternType=extended \
+ grep "a+b*c" $H ab >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep $L with grep.extendedRegexp=true and grep.patternType=basic" '
+ echo "${HC}ab:a+bc" >expected &&
+ git \
+ -c grep.extendedRegexp=true \
+ -c grep.patternType=basic \
+ grep "a+b*c" $H ab >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep --count $L" '
+ echo ${HC}ab:3 >expected &&
+ git grep --count -e b $H -- ab >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep --count -h $L" '
+ echo 3 >expected &&
+ git grep --count -h -e b $H -- ab >actual &&
+ test_cmp expected actual
+ '
done
cat >expected <<EOF
+file
+EOF
+test_expect_success 'grep -l -C' '
+ git grep -l -C1 foo >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:5
+EOF
+test_expect_success 'grep -c -C' '
+ git grep -c -C1 foo >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep -L -C' '
+ git ls-files >expected &&
+ git grep -L -C1 nonexistent_string >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
file:foo mmap bar_mmap
EOF
@@ -261,6 +443,11 @@ test_expect_success 'grep -f, multiple patterns' '
test_cmp expected actual
'
+test_expect_success 'grep, multiple patterns' '
+ git grep "$(cat patterns)" >actual &&
+ test_cmp expected actual
+'
+
cat >expected <<EOF
file:foo mmap bar
file:foo_mmap bar
@@ -285,6 +472,11 @@ test_expect_success 'grep -f, ignore empty lines' '
test_cmp expected actual
'
+test_expect_success 'grep -f, ignore empty lines, read patterns from stdin' '
+ git grep -f - <patterns >actual &&
+ test_cmp expected actual
+'
+
cat >expected <<EOF
y:y yy
--
@@ -299,17 +491,6 @@ test_expect_success 'grep -q, silently report matches' '
test_cmp empty actual
'
-# Create 1024 file names that sort between "y" and "z" to make sure
-# the two files are handled by different calls to an external grep.
-# This depends on MAXARGS in builtin-grep.c being 1024 or less.
-c32="0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v"
-test_expect_success 'grep -C1, hunk mark between files' '
- for a in $c32; do for b in $c32; do : >y-$a$b; done; done &&
- git add y-?? &&
- git grep -C1 "^[yz]" >actual &&
- test_cmp expected actual
-'
-
test_expect_success 'grep -C1 hunk mark between files' '
git grep -C1 "^[yz]" >actual &&
test_cmp expected actual
@@ -324,37 +505,52 @@ test_expect_success 'log grep setup' '
echo a >>file &&
test_tick &&
- git commit -a -m "third"
+ git commit -a -m "third" &&
+ echo a >>file &&
+ test_tick &&
+ GIT_AUTHOR_NAME="Night Fall" \
+ GIT_AUTHOR_EMAIL="nitfol@frobozz.com" \
+ git commit -a -m "fourth"
'
test_expect_success 'log grep (1)' '
git log --author=author --pretty=tformat:%s >actual &&
- ( echo third ; echo initial ) >expect &&
+ {
+ echo third && echo initial
+ } >expect &&
test_cmp expect actual
'
test_expect_success 'log grep (2)' '
git log --author=" * " -F --pretty=tformat:%s >actual &&
- ( echo second ) >expect &&
+ {
+ echo second
+ } >expect &&
test_cmp expect actual
'
test_expect_success 'log grep (3)' '
git log --author="^A U" --pretty=tformat:%s >actual &&
- ( echo third ; echo initial ) >expect &&
+ {
+ echo third && echo initial
+ } >expect &&
test_cmp expect actual
'
test_expect_success 'log grep (4)' '
git log --author="frotz\.com>$" --pretty=tformat:%s >actual &&
- ( echo second ) >expect &&
+ {
+ echo second
+ } >expect &&
test_cmp expect actual
'
test_expect_success 'log grep (5)' '
git log --author=Thor -F --pretty=tformat:%s >actual &&
- ( echo third ; echo initial ) >expect &&
+ {
+ echo third && echo initial
+ } >expect &&
test_cmp expect actual
'
@@ -364,11 +560,127 @@ test_expect_success 'log grep (6)' '
test_cmp expect actual
'
-test_expect_success 'log --grep --author implicitly uses all-match' '
+test_expect_success 'log grep (7)' '
+ git log -g --grep-reflog="commit: third" --pretty=tformat:%s >actual &&
+ echo third >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log grep (8)' '
+ git log -g --grep-reflog="commit: third" --grep-reflog="commit: second" --pretty=tformat:%s >actual &&
+ {
+ echo third && echo second
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log grep (9)' '
+ git log -g --grep-reflog="commit: third" --author="Thor" --pretty=tformat:%s >actual &&
+ echo third >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log grep (9)' '
+ git log -g --grep-reflog="commit: third" --author="non-existant" --pretty=tformat:%s >actual &&
+ : >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --grep-reflog can only be used under -g' '
+ test_must_fail git log --grep-reflog="commit: third"
+'
+
+test_expect_success 'log with multiple --grep uses union' '
+ git log --grep=i --grep=r --format=%s >actual &&
+ {
+ echo fourth && echo third && echo initial
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --all-match with multiple --grep uses intersection' '
+ git log --all-match --grep=i --grep=r --format=%s >actual &&
+ {
+ echo third
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log with multiple --author uses union' '
+ git log --author="Thor" --author="Aster" --format=%s >actual &&
+ {
+ echo third && echo second && echo initial
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --all-match with multiple --author still uses union' '
+ git log --all-match --author="Thor" --author="Aster" --format=%s >actual &&
+ {
+ echo third && echo second && echo initial
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --grep --author uses intersection' '
+ # grep matches only third and fourth
+ # author matches only initial and third
+ git log --author="A U Thor" --grep=r --format=%s >actual &&
+ {
+ echo third
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --grep --grep --author takes union of greps and intersects with author' '
# grep matches initial and second but not third
# author matches only initial and third
git log --author="A U Thor" --grep=s --grep=l --format=%s >actual &&
- echo initial >expect &&
+ {
+ echo initial
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log ---all-match -grep --author --author still takes union of authors and intersects with grep' '
+ # grep matches only initial and third
+ # author matches all but second
+ git log --all-match --author="Thor" --author="Night" --grep=i --format=%s >actual &&
+ {
+ echo third && echo initial
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --grep --author --author takes union of authors and intersects with grep' '
+ # grep matches only initial and third
+ # author matches all but second
+ git log --author="Thor" --author="Night" --grep=i --format=%s >actual &&
+ {
+ echo third && echo initial
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --all-match --grep --grep --author takes intersection' '
+ # grep matches only third
+ # author matches only initial and third
+ git log --all-match --author="A U Thor" --grep=i --grep=r --format=%s >actual &&
+ {
+ echo third
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --author does not search in timestamp' '
+ : >expect &&
+ git log --author="$GIT_AUTHOR_DATE" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --committer does not search in timestamp' '
+ : >expect &&
+ git log --committer="$GIT_COMMITTER_DATE" >actual &&
test_cmp expect actual
'
@@ -405,6 +717,7 @@ test_expect_success 'grep -p' '
cat >expected <<EOF
hello.c-#include <stdio.h>
+hello.c-
hello.c=int main(int argc, const char **argv)
hello.c-{
hello.c- printf("Hello world.\n");
@@ -416,6 +729,44 @@ test_expect_success 'grep -p -B5' '
test_cmp expected actual
'
+cat >expected <<EOF
+hello.c=int main(int argc, const char **argv)
+hello.c-{
+hello.c- printf("Hello world.\n");
+hello.c: return 0;
+hello.c- /* char ?? */
+hello.c-}
+EOF
+
+test_expect_success 'grep -W' '
+ git grep -W return >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c-#include <assert.h>
+hello.c:#include <stdio.h>
+EOF
+
+test_expect_success 'grep -W shows no trailing empty lines' '
+ git grep -W stdio >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c= printf("Hello world.\n");
+hello.c: return 0;
+hello.c- /* char ?? */
+EOF
+
+test_expect_success 'grep -W with userdiff' '
+ test_when_finished "rm -f .gitattributes" &&
+ git config diff.custom.xfuncname "(printf.*|})$" &&
+ echo "hello.c diff=custom" >.gitattributes &&
+ git grep -W return >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'grep from a subdirectory to search wider area (1)' '
mkdir -p s &&
(
@@ -447,23 +798,80 @@ test_expect_success 'outside of git repository' '
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
+ echo file2:world >non/expect.sub &&
(
- GIT_CEILING_DIRECTORIES="$(pwd)/non/git" &&
+ GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
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
+ 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
+ ) &&
+
+ echo ".*o*" >non/git/.gitignore &&
+ (
+ GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
+ export GIT_CEILING_DIRECTORIES &&
+ cd non/git &&
+ test_must_fail git grep o &&
+ git grep --no-index --exclude-standard o >../actual.full &&
+ test_cmp ../expect.full ../actual.full &&
+
+ {
+ echo ".gitignore:.*o*" &&
+ cat ../expect.full
+ } >../expect.with.ignored &&
+ git grep --no-index --no-exclude o >../actual.full &&
+ test_cmp ../expect.with.ignored ../actual.full
+ )
+'
+
+test_expect_success 'outside of git repository with fallbackToNoIndex' '
+ rm -fr non &&
+ mkdir -p non/git/sub &&
+ echo hello >non/git/file1 &&
+ echo world >non/git/sub/file2 &&
+ cat <<-\EOF >non/expect.full &&
+ file1:hello
+ sub/file2:world
+ EOF
+ echo file2:world >non/expect.sub &&
+ (
+ GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
+ export GIT_CEILING_DIRECTORIES &&
+ cd non/git &&
+ test_must_fail git -c grep.fallbackToNoIndex=false grep o &&
+ git -c grep.fallbackToNoIndex=true grep o >../actual.full &&
+ test_cmp ../expect.full ../actual.full &&
+ cd sub &&
+ test_must_fail git -c grep.fallbackToNoIndex=false grep o &&
+ git -c grep.fallbackToNoIndex=true grep o >../../actual.sub &&
+ test_cmp ../../expect.sub ../../actual.sub
+ ) &&
+
+ echo ".*o*" >non/git/.gitignore &&
+ (
+ GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
+ export GIT_CEILING_DIRECTORIES &&
+ cd non/git &&
+ test_must_fail git -c grep.fallbackToNoIndex=false grep o &&
+ git -c grep.fallbackToNoIndex=true grep --exclude-standard o >../actual.full &&
+ test_cmp ../expect.full ../actual.full &&
+
+ {
+ echo ".gitignore:.*o*" &&
+ cat ../expect.full
+ } >../expect.with.ignored &&
+ git -c grep.fallbackToNoIndex grep --no-exclude o >../actual.full &&
+ test_cmp ../expect.with.ignored ../actual.full
)
'
@@ -476,24 +884,67 @@ test_expect_success 'inside git repository but with --no-index' '
{
echo file1:hello &&
echo sub/file2:world
+ } >is/expect.unignored &&
+ {
+ echo ".gitignore:.*o*" &&
+ cat is/expect.unignored
} >is/expect.full &&
: >is/expect.empty &&
- echo file2:world >is/expect.sub
+ 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 --untracked o >../actual.unignored &&
+ test_cmp ../expect.unignored ../actual.unignored &&
+
git grep --no-index o >../actual.full &&
test_cmp ../expect.full ../actual.full &&
+
+ git grep --no-index --exclude-standard o >../actual.unignored &&
+ test_cmp ../expect.unignored ../actual.unignored &&
+
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 &&
+
+ git grep --untracked o >../../actual.sub &&
test_cmp ../../expect.sub ../../actual.sub
)
'
+test_expect_success 'grep --no-index descends into repos, but not .git' '
+ rm -fr non &&
+ mkdir -p non/git &&
+ (
+ GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
+ export GIT_CEILING_DIRECTORIES &&
+ cd non/git &&
+
+ echo magic >file &&
+ git init repo &&
+ (
+ cd repo &&
+ echo magic >file &&
+ git add file &&
+ git commit -m foo &&
+ echo magic >.git/file
+ ) &&
+
+ cat >expect <<-\EOF &&
+ file
+ repo/file
+ EOF
+ git grep -l --no-index magic >actual &&
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'setup double-dash tests' '
cat >double-dash <<EOF &&
--
@@ -527,4 +978,461 @@ test_expect_success 'grep -e -- -- path' '
test_cmp expected actual
'
+cat >expected <<EOF
+hello.c:int main(int argc, const char **argv)
+hello.c: printf("Hello world.\n");
+EOF
+
+test_expect_success LIBPCRE 'grep --perl-regexp pattern' '
+ git grep --perl-regexp "\p{Ps}.*?\p{Pe}" hello.c >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep -P pattern' '
+ git grep -P "\p{Ps}.*?\p{Pe}" hello.c >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep pattern with grep.extendedRegexp=true' '
+ >empty &&
+ test_must_fail git -c grep.extendedregexp=true \
+ grep "\p{Ps}.*?\p{Pe}" hello.c >actual &&
+ test_cmp empty actual
+'
+
+test_expect_success LIBPCRE 'grep -P pattern with grep.extendedRegexp=true' '
+ git -c grep.extendedregexp=true \
+ grep -P "\p{Ps}.*?\p{Pe}" hello.c >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep -P -v pattern' '
+ {
+ echo "ab:a+b*c"
+ echo "ab:a+bc"
+ } >expected &&
+ git grep -P -v "abc" ab >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep -P -i pattern' '
+ cat >expected <<-EOF &&
+ hello.c: printf("Hello world.\n");
+ EOF
+ git grep -P -i "PRINTF\([^\d]+\)" hello.c >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep -P -w pattern' '
+ {
+ echo "hello_world:Hello world"
+ echo "hello_world:HeLLo world"
+ } >expected &&
+ git grep -P -w "He((?i)ll)o" hello_world >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep -G invalidpattern properly dies ' '
+ test_must_fail git grep -G "a["
+'
+
+test_expect_success 'grep invalidpattern properly dies with grep.patternType=basic' '
+ test_must_fail git -c grep.patterntype=basic grep "a["
+'
+
+test_expect_success 'grep -E invalidpattern properly dies ' '
+ test_must_fail git grep -E "a["
+'
+
+test_expect_success 'grep invalidpattern properly dies with grep.patternType=extended' '
+ test_must_fail git -c grep.patterntype=extended grep "a["
+'
+
+test_expect_success LIBPCRE 'grep -P invalidpattern properly dies ' '
+ test_must_fail git grep -P "a["
+'
+
+test_expect_success LIBPCRE 'grep invalidpattern properly dies with grep.patternType=perl' '
+ test_must_fail git -c grep.patterntype=perl grep "a["
+'
+
+test_expect_success 'grep -G -E -F pattern' '
+ echo "ab:a+b*c" >expected &&
+ git grep -G -E -F "a+b*c" ab >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep pattern with grep.patternType=basic, =extended, =fixed' '
+ echo "ab:a+b*c" >expected &&
+ git \
+ -c grep.patterntype=basic \
+ -c grep.patterntype=extended \
+ -c grep.patterntype=fixed \
+ grep "a+b*c" ab >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep -E -F -G pattern' '
+ echo "ab:a+bc" >expected &&
+ git grep -E -F -G "a+b*c" ab >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep pattern with grep.patternType=extended, =fixed, =basic' '
+ echo "ab:a+bc" >expected &&
+ git \
+ -c grep.patterntype=extended \
+ -c grep.patterntype=fixed \
+ -c grep.patterntype=basic \
+ grep "a+b*c" ab >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep -F -G -E pattern' '
+ echo "ab:abc" >expected &&
+ git grep -F -G -E "a+b*c" ab >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep pattern with grep.patternType=fixed, =basic, =extended' '
+ echo "ab:abc" >expected &&
+ git \
+ -c grep.patterntype=fixed \
+ -c grep.patterntype=basic \
+ -c grep.patterntype=extended \
+ grep "a+b*c" ab >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep -G -F -P -E pattern' '
+ >empty &&
+ test_must_fail git grep -G -F -P -E "a\x{2b}b\x{2a}c" ab >actual &&
+ test_cmp empty actual
+'
+
+test_expect_success 'grep pattern with grep.patternType=fixed, =basic, =perl, =extended' '
+ >empty &&
+ test_must_fail git \
+ -c grep.patterntype=fixed \
+ -c grep.patterntype=basic \
+ -c grep.patterntype=perl \
+ -c grep.patterntype=extended \
+ grep "a\x{2b}b\x{2a}c" ab >actual &&
+ test_cmp empty actual
+'
+
+test_expect_success LIBPCRE 'grep -G -F -E -P pattern' '
+ echo "ab:a+b*c" >expected &&
+ git grep -G -F -E -P "a\x{2b}b\x{2a}c" ab >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep pattern with grep.patternType=fixed, =basic, =extended, =perl' '
+ echo "ab:a+b*c" >expected &&
+ git \
+ -c grep.patterntype=fixed \
+ -c grep.patterntype=basic \
+ -c grep.patterntype=extended \
+ -c grep.patterntype=perl \
+ grep "a\x{2b}b\x{2a}c" ab >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep -P pattern with grep.patternType=fixed' '
+ echo "ab:a+b*c" >expected &&
+ git \
+ -c grep.patterntype=fixed \
+ grep -P "a\x{2b}b\x{2a}c" ab >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep -F pattern with grep.patternType=basic' '
+ echo "ab:a+b*c" >expected &&
+ git \
+ -c grep.patterntype=basic \
+ grep -F "*c" ab >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep -G pattern with grep.patternType=fixed' '
+ {
+ echo "ab:a+b*c"
+ echo "ab:a+bc"
+ } >expected &&
+ git \
+ -c grep.patterntype=fixed \
+ grep -G "a+b" ab >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep -E pattern with grep.patternType=fixed' '
+ {
+ echo "ab:a+b*c"
+ echo "ab:a+bc"
+ echo "ab:abc"
+ } >expected &&
+ git \
+ -c grep.patterntype=fixed \
+ grep -E "a+" ab >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c<RED>:<RESET>int main(int argc, const char **argv)
+hello.c<RED>-<RESET>{
+<RED>--<RESET>
+hello.c<RED>:<RESET> /* char ?? */
+hello.c<RED>-<RESET>}
+<RED>--<RESET>
+hello_world<RED>:<RESET>Hello_world
+hello_world<RED>-<RESET>HeLLo_world
+EOF
+
+test_expect_success 'grep --color, separator' '
+ test_config color.grep.context normal &&
+ test_config color.grep.filename normal &&
+ test_config color.grep.function normal &&
+ test_config color.grep.linenumber normal &&
+ test_config color.grep.match normal &&
+ test_config color.grep.selected normal &&
+ test_config color.grep.separator red &&
+
+ git grep --color=always -A1 -e char -e lo_w hello.c hello_world |
+ test_decode_color >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c:int main(int argc, const char **argv)
+hello.c: /* char ?? */
+
+hello_world:Hello_world
+EOF
+
+test_expect_success 'grep --break' '
+ git grep --break -e char -e lo_w hello.c hello_world >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c:int main(int argc, const char **argv)
+hello.c-{
+--
+hello.c: /* char ?? */
+hello.c-}
+
+hello_world:Hello_world
+hello_world-HeLLo_world
+EOF
+
+test_expect_success 'grep --break with context' '
+ git grep --break -A1 -e char -e lo_w hello.c hello_world >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c
+int main(int argc, const char **argv)
+ /* char ?? */
+hello_world
+Hello_world
+EOF
+
+test_expect_success 'grep --heading' '
+ git grep --heading -e char -e lo_w hello.c hello_world >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+<BOLD;GREEN>hello.c<RESET>
+4:int main(int argc, const <BLACK;BYELLOW>char<RESET> **argv)
+8: /* <BLACK;BYELLOW>char<RESET> ?? */
+
+<BOLD;GREEN>hello_world<RESET>
+3:Hel<BLACK;BYELLOW>lo_w<RESET>orld
+EOF
+
+test_expect_success 'mimic ack-grep --group' '
+ test_config color.grep.context normal &&
+ test_config color.grep.filename "bold green" &&
+ test_config color.grep.function normal &&
+ test_config color.grep.linenumber normal &&
+ test_config color.grep.match "black yellow" &&
+ test_config color.grep.selected normal &&
+ test_config color.grep.separator normal &&
+
+ git grep --break --heading -n --color \
+ -e char -e lo_w hello.c hello_world |
+ test_decode_color >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+space: line with leading space1
+space: line with leading space2
+space: line with leading space3
+EOF
+
+test_expect_success LIBPCRE 'grep -E "^ "' '
+ git grep -E "^ " space >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep -P "^ "' '
+ git grep -P "^ " space >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+space-line without leading space1
+space: line <RED>with <RESET>leading space1
+space: line <RED>with <RESET>leading <RED>space2<RESET>
+space: line <RED>with <RESET>leading space3
+space:line without leading <RED>space2<RESET>
+EOF
+
+test_expect_success 'grep --color -e A -e B with context' '
+ test_config color.grep.context normal &&
+ test_config color.grep.filename normal &&
+ test_config color.grep.function normal &&
+ test_config color.grep.linenumber normal &&
+ test_config color.grep.matchContext normal &&
+ test_config color.grep.matchSelected red &&
+ test_config color.grep.selected normal &&
+ test_config color.grep.separator normal &&
+
+ git grep --color=always -C2 -e "with " -e space2 space |
+ test_decode_color >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+space-line without leading space1
+space- line with leading space1
+space: line <RED>with <RESET>leading <RED>space2<RESET>
+space- line with leading space3
+space-line without leading space2
+EOF
+
+test_expect_success 'grep --color -e A --and -e B with context' '
+ test_config color.grep.context normal &&
+ test_config color.grep.filename normal &&
+ test_config color.grep.function normal &&
+ test_config color.grep.linenumber normal &&
+ test_config color.grep.matchContext normal &&
+ test_config color.grep.matchSelected red &&
+ test_config color.grep.selected normal &&
+ test_config color.grep.separator normal &&
+
+ git grep --color=always -C2 -e "with " --and -e space2 space |
+ test_decode_color >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+space-line without leading space1
+space: line <RED>with <RESET>leading space1
+space- line with leading space2
+space: line <RED>with <RESET>leading space3
+space-line without leading space2
+EOF
+
+test_expect_success 'grep --color -e A --and --not -e B with context' '
+ test_config color.grep.context normal &&
+ test_config color.grep.filename normal &&
+ test_config color.grep.function normal &&
+ test_config color.grep.linenumber normal &&
+ test_config color.grep.matchContext normal &&
+ test_config color.grep.matchSelected red &&
+ test_config color.grep.selected normal &&
+ test_config color.grep.separator normal &&
+
+ git grep --color=always -C2 -e "with " --and --not -e space2 space |
+ test_decode_color >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c-
+hello.c=int main(int argc, const char **argv)
+hello.c-{
+hello.c: pr<RED>int<RESET>f("<RED>Hello<RESET> world.\n");
+hello.c- return 0;
+hello.c- /* char ?? */
+hello.c-}
+EOF
+
+test_expect_success 'grep --color -e A --and -e B -p with context' '
+ test_config color.grep.context normal &&
+ test_config color.grep.filename normal &&
+ test_config color.grep.function normal &&
+ test_config color.grep.linenumber normal &&
+ test_config color.grep.matchContext normal &&
+ test_config color.grep.matchSelected red &&
+ test_config color.grep.selected normal &&
+ test_config color.grep.separator normal &&
+
+ git grep --color=always -p -C3 -e int --and -e Hello --no-index hello.c |
+ test_decode_color >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep can find things only in the work tree' '
+ : >work-tree-only &&
+ git add work-tree-only &&
+ test_when_finished "git rm -f work-tree-only" &&
+ echo "find in work tree" >work-tree-only &&
+ git grep --quiet "find in work tree" &&
+ test_must_fail git grep --quiet --cached "find in work tree" &&
+ test_must_fail git grep --quiet "find in work tree" HEAD
+'
+
+test_expect_success 'grep can find things only in the work tree (i-t-a)' '
+ echo "intend to add this" >intend-to-add &&
+ git add -N intend-to-add &&
+ test_when_finished "git rm -f intend-to-add" &&
+ git grep --quiet "intend to add this" &&
+ test_must_fail git grep --quiet --cached "intend to add this" &&
+ test_must_fail git grep --quiet "intend to add this" HEAD
+'
+
+test_expect_success 'grep does not search work tree with assume unchanged' '
+ echo "intend to add this" >intend-to-add &&
+ git add -N intend-to-add &&
+ git update-index --assume-unchanged intend-to-add &&
+ test_when_finished "git rm -f intend-to-add" &&
+ test_must_fail git grep --quiet "intend to add this" &&
+ test_must_fail git grep --quiet --cached "intend to add this" &&
+ test_must_fail git grep --quiet "intend to add this" HEAD
+'
+
+test_expect_success 'grep can find things only in the index' '
+ echo "only in the index" >cache-this &&
+ git add cache-this &&
+ rm cache-this &&
+ test_when_finished "git rm --cached cache-this" &&
+ test_must_fail git grep --quiet "only in the index" &&
+ git grep --quiet --cached "only in the index" &&
+ test_must_fail git grep --quiet "only in the index" HEAD
+'
+
+test_expect_success 'grep does not report i-t-a with -L --cached' '
+ echo "intend to add this" >intend-to-add &&
+ git add -N intend-to-add &&
+ test_when_finished "git rm -f intend-to-add" &&
+ git ls-files | grep -v "^intend-to-add\$" >expected &&
+ git grep -L --cached "nonexistent_string" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep does not report i-t-a and assume unchanged with -L' '
+ echo "intend to add this" >intend-to-add-assume-unchanged &&
+ git add -N intend-to-add-assume-unchanged &&
+ test_when_finished "git rm -f intend-to-add-assume-unchanged" &&
+ git update-index --assume-unchanged intend-to-add-assume-unchanged &&
+ git ls-files | grep -v "^intend-to-add-assume-unchanged\$" >expected &&
+ git grep -L "nonexistent_string" >actual &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t7811-grep-open.sh b/t/t7811-grep-open.sh
index 568a6f2..e1951a5 100755
--- a/t/t7811-grep-open.sh
+++ b/t/t7811-grep-open.sh
@@ -63,7 +63,7 @@ test_expect_success SIMPLEPAGER 'git grep -O' '
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_i18ngrep open-files-in-pager msg
'
test_expect_success 'git grep -O --no-index' '
@@ -125,11 +125,6 @@ test_expect_success 'modified file' '
test_cmp empty out
'
-test_config() {
- git config "$1" "$2" &&
- test_when_finished "git config --unset $1"
-}
-
test_expect_success 'copes with color settings' '
rm -f actual &&
echo grep.h >expect &&
diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh
index 45cb60e..72176e4 100755
--- a/t/t8001-annotate.sh
+++ b/t/t8001-annotate.sh
@@ -6,10 +6,11 @@ test_description='git annotate'
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_expect_success 'annotate old revision' '
+ git annotate file master >actual &&
+ awk "{ print \$3; }" <actual >authors &&
+ test 2 = $(grep A <authors | wc -l) &&
+ test 2 = $(grep B <authors | wc -l)
+'
test_done
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh
index 597cf04..ff09ace 100755
--- a/t/t8002-blame.sh
+++ b/t/t8002-blame.sh
@@ -6,4 +6,79 @@ test_description='git blame'
PROG='git blame -c'
. "$TEST_DIRECTORY"/annotate-tests.sh
+PROG='git blame -c -e'
+test_expect_success 'blame --show-email' '
+ check_count \
+ "<A@test.git>" 1 \
+ "<B@test.git>" 1 \
+ "<B1@test.git>" 1 \
+ "<B2@test.git>" 1 \
+ "<author@example.com>" 1 \
+ "<C@test.git>" 1 \
+ "<D@test.git>" 1 \
+ "<E at test dot git>" 1
+'
+
+test_expect_success 'setup showEmail tests' '
+ echo "bin: test number 1" >one &&
+ git add one &&
+ GIT_AUTHOR_NAME=name1 \
+ GIT_AUTHOR_EMAIL=email1@test.git \
+ git commit -m First --date="2010-01-01 01:00:00" &&
+ cat >expected_n <<-\EOF &&
+ (name1 2010-01-01 01:00:00 +0000 1) bin: test number 1
+ EOF
+ cat >expected_e <<-\EOF
+ (<email1@test.git> 2010-01-01 01:00:00 +0000 1) bin: test number 1
+ EOF
+'
+
+find_blame () {
+ sed -e 's/^[^(]*//'
+}
+
+test_expect_success 'blame with no options and no config' '
+ git blame one >blame &&
+ find_blame <blame >result &&
+ test_cmp expected_n result
+'
+
+test_expect_success 'blame with showemail options' '
+ git blame --show-email one >blame1 &&
+ find_blame <blame1 >result &&
+ test_cmp expected_e result &&
+ git blame -e one >blame2 &&
+ find_blame <blame2 >result &&
+ test_cmp expected_e result &&
+ git blame --no-show-email one >blame3 &&
+ find_blame <blame3 >result &&
+ test_cmp expected_n result
+'
+
+test_expect_success 'blame with showEmail config false' '
+ git config blame.showEmail false &&
+ git blame one >blame1 &&
+ find_blame <blame1 >result &&
+ test_cmp expected_n result &&
+ git blame --show-email one >blame2 &&
+ find_blame <blame2 >result &&
+ test_cmp expected_e result &&
+ git blame -e one >blame3 &&
+ find_blame <blame3 >result &&
+ test_cmp expected_e result &&
+ git blame --no-show-email one >blame4 &&
+ find_blame <blame4 >result &&
+ test_cmp expected_n result
+'
+
+test_expect_success 'blame with showEmail config true' '
+ git config blame.showEmail true &&
+ git blame one >blame1 &&
+ find_blame <blame1 >result &&
+ test_cmp expected_e result &&
+ git blame --no-show-email one >blame2 &&
+ find_blame <blame2 >result &&
+ test_cmp expected_n result
+'
+
test_done
diff --git a/t/t8003-blame.sh b/t/t8003-blame-corner-cases.sh
index 230143c..a9b266f 100755
--- a/t/t8003-blame.sh
+++ b/t/t8003-blame-corner-cases.sh
@@ -26,7 +26,7 @@ test_expect_success setup '
cat one >uno &&
mv two dos &&
cat one >>tres &&
- echo DEF >>mouse
+ echo DEF >>mouse &&
git add uno dos tres mouse &&
test_tick &&
GIT_AUTHOR_NAME=Second git commit -a -m Second &&
@@ -153,15 +153,15 @@ test_expect_success 'blame path that used to be a directory' '
'
test_expect_success 'blame to a commit with no author name' '
- TREE=`git rev-parse HEAD:`
- cat >badcommit <<EOF
+ 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`
+ COMMIT=$(git hash-object -t commit -w badcommit) &&
git --no-pager blame $COMMIT -- uno >/dev/null
'
@@ -175,6 +175,12 @@ test_expect_success 'blame -L with invalid end' '
grep "has only 2 lines" errors
'
+test_expect_success 'blame parses <end> part of -L' '
+ git blame -L1,1 tres >out &&
+ cat out &&
+ test $(wc -l < out) -eq 1
+'
+
test_expect_success 'indent of line numbers, nine lines' '
git blame nine_lines >actual &&
test $(grep -c " " actual) = 0
@@ -185,4 +191,39 @@ test_expect_success 'indent of line numbers, ten lines' '
test $(grep -c " " actual) = 9
'
+test_expect_success 'setup file with CRLF newlines' '
+ git config core.autocrlf false &&
+ printf "testcase\n" >crlffile &&
+ git add crlffile &&
+ git commit -m testcase &&
+ printf "testcase\r\n" >crlffile
+'
+
+test_expect_success 'blame file with CRLF core.autocrlf true' '
+ git config core.autocrlf true &&
+ git blame crlffile >actual &&
+ grep "A U Thor" actual
+'
+
+test_expect_success 'blame file with CRLF attributes text' '
+ git config core.autocrlf false &&
+ echo "crlffile text" >.gitattributes &&
+ git blame crlffile >actual &&
+ grep "A U Thor" actual
+'
+
+test_expect_success 'blame file with CRLF core.autocrlf=true' '
+ git config core.autocrlf false &&
+ printf "testcase\r\n" >crlfinrepo &&
+ >.gitattributes &&
+ git add crlfinrepo &&
+ git commit -m "add crlfinrepo" &&
+ git config core.autocrlf true &&
+ mv crlfinrepo tmp &&
+ git checkout crlfinrepo &&
+ rm tmp &&
+ git blame crlfinrepo >actual &&
+ grep "A U Thor" actual
+'
+
test_done
diff --git a/t/t8004-blame.sh b/t/t8004-blame-with-conflicts.sh
index ba19ac1..9c353ab 100755
--- a/t/t8004-blame.sh
+++ b/t/t8004-blame-with-conflicts.sh
@@ -66,7 +66,7 @@ test_expect_success \
git blame file2
'
-test_expect_success 'blame runs on conflicted file in stages 1,3' '
+test_expect_success 'blame does not crash with conflicted file in stages 1,3' '
git blame file1
'
diff --git a/t/t8005-blame-i18n.sh b/t/t8005-blame-i18n.sh
index cb39055..75da219 100755
--- a/t/t8005-blame-i18n.sh
+++ b/t/t8005-blame-i18n.sh
@@ -33,11 +33,15 @@ author $SJIS_NAME
summary $SJIS_MSG
EOF
-test_expect_success \
+filter_author_summary () {
+ sed -n -e '/^author /p' -e '/^summary /p' "$@"
+}
+
+test_expect_success !MINGW \
'blame respects i18n.commitencoding' '
- git blame --incremental file | \
- egrep "^(author|summary) " > actual &&
- test_cmp actual expected
+ git blame --incremental file >output &&
+ filter_author_summary output >actual &&
+ test_cmp expected actual
'
cat >expected <<EOF
@@ -49,12 +53,12 @@ author $EUC_JAPAN_NAME
summary $EUC_JAPAN_MSG
EOF
-test_expect_success \
+test_expect_success !MINGW \
'blame respects i18n.logoutputencoding' '
git config i18n.logoutputencoding eucJP &&
- git blame --incremental file | \
- egrep "^(author|summary) " > actual &&
- test_cmp actual expected
+ git blame --incremental file >output &&
+ filter_author_summary output >actual &&
+ test_cmp expected actual
'
cat >expected <<EOF
@@ -66,11 +70,11 @@ author $UTF8_NAME
summary $UTF8_MSG
EOF
-test_expect_success \
+test_expect_success !MINGW \
'blame respects --encoding=UTF-8' '
- git blame --incremental --encoding=UTF-8 file | \
- egrep "^(author|summary) " > actual &&
- test_cmp actual expected
+ git blame --incremental --encoding=UTF-8 file >output &&
+ filter_author_summary output >actual &&
+ test_cmp expected actual
'
cat >expected <<EOF
@@ -82,11 +86,11 @@ author $UTF8_NAME
summary $UTF8_MSG
EOF
-test_expect_success \
+test_expect_success !MINGW \
'blame respects --encoding=none' '
- git blame --incremental --encoding=none file | \
- egrep "^(author|summary) " > actual &&
- test_cmp actual expected
+ git blame --incremental --encoding=none file >output &&
+ filter_author_summary output >actual &&
+ test_cmp expected actual
'
test_done
diff --git a/t/t8006-blame-textconv.sh b/t/t8006-blame-textconv.sh
index 9ad96d4..7683515 100755
--- a/t/t8006-blame-textconv.sh
+++ b/t/t8006-blame-textconv.sh
@@ -9,22 +9,27 @@ find_blame() {
cat >helper <<'EOF'
#!/bin/sh
-sed 's/^/converted: /' "$@"
+grep -q '^bin: ' "$1" || { echo "E: $1 is not \"binary\" file" 1>&2; exit 1; }
+"$PERL_PATH" -p -e 's/^bin: /converted: /' "$1"
EOF
chmod +x helper
test_expect_success 'setup ' '
- echo test 1 >one.bin &&
- echo test number 2 >two.bin &&
+ echo "bin: test number 0" >zero.bin &&
+ echo "bin: test 1" >one.bin &&
+ echo "bin: test number 2" >two.bin &&
+ test_ln_s_add one.bin symlink.bin &&
git add . &&
GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" &&
- echo test 1 version 2 >one.bin &&
- echo test number 2 version 2 >>two.bin &&
+ echo "bin: test 1 version 2" >one.bin &&
+ echo "bin: test number 2 version 2" >>two.bin &&
+ rm -f symlink.bin &&
+ test_ln_s_add two.bin symlink.bin &&
GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
'
cat >expected <<EOF
-(Number2 2010-01-01 20:00:00 +0000 1) test 1 version 2
+(Number2 2010-01-01 20:00:00 +0000 1) bin: test 1 version 2
EOF
test_expect_success 'no filter specified' '
@@ -35,6 +40,7 @@ test_expect_success 'no filter specified' '
test_expect_success 'setup textconv filters' '
echo "*.bin diff=test" >.gitattributes &&
+ echo "zero.bin eol=crlf" >>.gitattributes &&
git config diff.test.textconv ./helper &&
git config diff.test.cachetextconv false
'
@@ -66,8 +72,38 @@ test_expect_success 'blame --textconv going through revisions' '
test_cmp expected result
'
+test_expect_success 'blame --textconv with local changes' '
+ test_when_finished "git checkout zero.bin" &&
+ printf "bin: updated number 0\015" >zero.bin &&
+ git blame --textconv zero.bin >blame &&
+ expect="(Not Committed Yet ....-..-.. ..:..:.. +0000 1)" &&
+ expect="$expect converted: updated number 0" &&
+ expr "$(find_blame <blame)" : "^$expect"
+'
+
+test_expect_success 'setup +cachetextconv' '
+ git config diff.test.cachetextconv true
+'
+
+cat >expected_one <<EOF
+(Number2 2010-01-01 20:00:00 +0000 1) converted: test 1 version 2
+EOF
+
+test_expect_success 'blame --textconv works with textconvcache' '
+ git blame --textconv two.bin >blame &&
+ find_blame <blame >result &&
+ test_cmp expected result &&
+ git blame --textconv one.bin >blame &&
+ find_blame <blame >result &&
+ test_cmp expected_one result
+'
+
+test_expect_success 'setup -cachetextconv' '
+ git config diff.test.cachetextconv false
+'
+
test_expect_success 'make a new commit' '
- echo "test number 2 version 3" >>two.bin &&
+ echo "bin: test number 2 version 3" >>two.bin &&
GIT_AUTHOR_NAME=Number3 git commit -a -m Third --date="2010-01-01 22:00:00"
'
@@ -77,4 +113,45 @@ test_expect_success 'blame from previous revision' '
test_cmp expected result
'
+cat >expected <<EOF
+(Number2 2010-01-01 20:00:00 +0000 1) two.bin
+EOF
+
+test_expect_success SYMLINKS 'blame with --no-textconv (on symlink)' '
+ git blame --no-textconv symlink.bin >blame &&
+ find_blame <blame >result &&
+ test_cmp expected result
+'
+
+test_expect_success SYMLINKS 'blame --textconv (on symlink)' '
+ git blame --textconv symlink.bin >blame &&
+ find_blame <blame >result &&
+ test_cmp expected result
+'
+
+# cp two.bin three.bin and make small tweak
+# (this will direct blame -C -C three.bin to consider two.bin and symlink.bin)
+test_expect_success 'make another new commit' '
+ cat >three.bin <<\EOF &&
+bin: test number 2
+bin: test number 2 version 2
+bin: test number 2 version 3
+bin: test number 3
+EOF
+ git add three.bin &&
+ GIT_AUTHOR_NAME=Number4 git commit -a -m Fourth --date="2010-01-01 23:00:00"
+'
+
+test_expect_success 'blame on last commit (-C -C, symlink)' '
+ git blame -C -C three.bin >blame &&
+ find_blame <blame >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
+(Number3 2010-01-01 22:00:00 +0000 3) converted: test number 2 version 3
+(Number4 2010-01-01 23:00:00 +0000 4) converted: test number 3
+EOF
+ test_cmp expected result
+'
+
test_done
diff --git a/t/t8007-cat-file-textconv.sh b/t/t8007-cat-file-textconv.sh
index 38ac05e..eacd49a 100755
--- a/t/t8007-cat-file-textconv.sh
+++ b/t/t8007-cat-file-textconv.sh
@@ -5,24 +5,26 @@ test_description='git cat-file textconv support'
cat >helper <<'EOF'
#!/bin/sh
-sed 's/^/converted: /' "$@"
+grep -q '^bin: ' "$1" || { echo "E: $1 is not \"binary\" file" 1>&2; exit 1; }
+sed 's/^bin: /converted: /' "$1"
EOF
chmod +x helper
test_expect_success 'setup ' '
- echo test >one.bin &&
+ echo "bin: test" >one.bin &&
+ test_ln_s_add one.bin symlink.bin &&
git add . &&
GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" &&
- echo test version 2 >one.bin &&
+ echo "bin: 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
+bin: test version 2
EOF
test_expect_success 'no filter specified' '
- git cat-file --textconv :one.bin 2>result
+ git cat-file --textconv :one.bin >result &&
test_cmp expected result
'
@@ -32,17 +34,13 @@ test_expect_success 'setup textconv filters' '
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
+bin: test
EOF
test_expect_success 'cat-file without --textconv on previous commit' '
@@ -67,4 +65,22 @@ test_expect_success 'cat-file --textconv on previous commit' '
git cat-file --textconv HEAD^:one.bin >result &&
test_cmp expected result
'
+
+test_expect_success 'cat-file without --textconv (symlink)' '
+ printf "%s" "one.bin" >expected &&
+ git cat-file blob :symlink.bin >result &&
+ test_cmp expected result
+'
+
+
+test_expect_success 'cat-file --textconv on index (symlink)' '
+ git cat-file --textconv :symlink.bin >result &&
+ test_cmp expected result
+'
+
+test_expect_success 'cat-file --textconv on HEAD (symlink)' '
+ git cat-file --textconv HEAD:symlink.bin >result &&
+ test_cmp expected result
+'
+
test_done
diff --git a/t/t8008-blame-formats.sh b/t/t8008-blame-formats.sh
new file mode 100755
index 0000000..92c8e79
--- /dev/null
+++ b/t/t8008-blame-formats.sh
@@ -0,0 +1,107 @@
+#!/bin/sh
+
+test_description='blame output in various formats on a simple case'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo a >file &&
+ git add file &&
+ test_tick &&
+ git commit -m one &&
+ echo b >>file &&
+ echo c >>file &&
+ echo d >>file &&
+ test_tick &&
+ git commit -a -m two
+'
+
+cat >expect <<'EOF'
+^baf5e0b (A U Thor 2005-04-07 15:13:13 -0700 1) a
+8825379d (A U Thor 2005-04-07 15:14:13 -0700 2) b
+8825379d (A U Thor 2005-04-07 15:14:13 -0700 3) c
+8825379d (A U Thor 2005-04-07 15:14:13 -0700 4) d
+EOF
+test_expect_success 'normal blame output' '
+ git blame file >actual &&
+ test_cmp expect actual
+'
+
+ID1=baf5e0b3869e0b2b2beb395a3720c7b51eac94fc
+COMMIT1='author A U Thor
+author-mail <author@example.com>
+author-time 1112911993
+author-tz -0700
+committer C O Mitter
+committer-mail <committer@example.com>
+committer-time 1112911993
+committer-tz -0700
+summary one
+boundary
+filename file'
+ID2=8825379dfb8a1267b58e8e5bcf69eec838f685ec
+COMMIT2='author A U Thor
+author-mail <author@example.com>
+author-time 1112912053
+author-tz -0700
+committer C O Mitter
+committer-mail <committer@example.com>
+committer-time 1112912053
+committer-tz -0700
+summary two
+previous baf5e0b3869e0b2b2beb395a3720c7b51eac94fc file
+filename file'
+
+cat >expect <<EOF
+$ID1 1 1 1
+$COMMIT1
+ a
+$ID2 2 2 3
+$COMMIT2
+ b
+$ID2 3 3
+ c
+$ID2 4 4
+ d
+EOF
+test_expect_success 'blame --porcelain output' '
+ git blame --porcelain file >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+$ID1 1 1 1
+$COMMIT1
+ a
+$ID2 2 2 3
+$COMMIT2
+ b
+$ID2 3 3
+$COMMIT2
+ c
+$ID2 4 4
+$COMMIT2
+ d
+EOF
+test_expect_success 'blame --line-porcelain output' '
+ git blame --line-porcelain file >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--porcelain detects first non-blank line as subject' '
+ (
+ GIT_INDEX_FILE=.git/tmp-index &&
+ export GIT_INDEX_FILE &&
+ echo "This is it" >single-file &&
+ git add single-file &&
+ tree=$(git write-tree) &&
+ commit=$(printf "%s\n%s\n%s\n\n\n \noneline\n\nbody\n" \
+ "tree $tree" \
+ "author A <a@b.c> 123456789 +0000" \
+ "committer C <c@d.e> 123456789 +0000" |
+ git hash-object -w -t commit --stdin) &&
+ git blame --porcelain $commit -- single-file >output &&
+ grep "^summary oneline$" output
+ )
+'
+
+test_done
diff --git a/t/t8009-blame-vs-topicbranches.sh b/t/t8009-blame-vs-topicbranches.sh
new file mode 100755
index 0000000..72596e3
--- /dev/null
+++ b/t/t8009-blame-vs-topicbranches.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='blaming trough history with topic branches'
+. ./test-lib.sh
+
+# Creates the history shown below. '*'s mark the first parent in the merges.
+# The only line of file.t is changed in commit B2
+#
+# +---C1
+# / \
+# A0--A1--*A2--*A3
+# \ /
+# B1-B2
+#
+test_expect_success setup '
+ test_commit A0 file.t line0 &&
+ test_commit A1 &&
+ git reset --hard A0 &&
+ test_commit B1 &&
+ test_commit B2 file.t line0changed &&
+ git reset --hard A1 &&
+ test_merge A2 B2 &&
+ git reset --hard A1 &&
+ test_commit C1 &&
+ git reset --hard A2 &&
+ test_merge A3 C1
+ '
+
+test_expect_success 'blame --reverse --first-parent finds A1' '
+ git blame --porcelain --reverse --first-parent A0..A3 -- file.t >actual_full &&
+ head -n 1 <actual_full | sed -e "s/ .*//" >actual &&
+ git rev-parse A1 >expect &&
+ test_cmp expect actual
+ '
+
+test_done
diff --git a/t/t9000-addresses.sh b/t/t9000-addresses.sh
new file mode 100755
index 0000000..a1ebef6
--- /dev/null
+++ b/t/t9000-addresses.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+test_description='compare address parsing with and without Mail::Address'
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+ skip_all='skipping perl interface tests, perl not available'
+ test_done
+fi
+
+perl -MTest::More -e 0 2>/dev/null || {
+ skip_all="Perl Test::More unavailable, skipping test"
+ test_done
+}
+
+perl -MMail::Address -e 0 2>/dev/null || {
+ skip_all="Perl Mail::Address unavailable, skipping test"
+ test_done
+}
+
+test_external_has_tap=1
+
+test_external_without_stderr \
+ 'Perl address parsing function' \
+ perl "$TEST_DIRECTORY"/t9000/test.pl
+
+test_done
diff --git a/t/t9000/test.pl b/t/t9000/test.pl
new file mode 100755
index 0000000..2d05d3e
--- /dev/null
+++ b/t/t9000/test.pl
@@ -0,0 +1,67 @@
+#!/usr/bin/perl
+use lib (split(/:/, $ENV{GITPERLLIB}));
+
+use 5.008;
+use warnings;
+use strict;
+
+use Test::More qw(no_plan);
+use Mail::Address;
+
+BEGIN { use_ok('Git') }
+
+my @success_list = (q[Jane],
+ q[jdoe@example.com],
+ q[<jdoe@example.com>],
+ q[Jane <jdoe@example.com>],
+ q[Jane Doe <jdoe@example.com>],
+ q["Jane" <jdoe@example.com>],
+ q["Doe, Jane" <jdoe@example.com>],
+ q["Jane@:;\>.,()<Doe" <jdoe@example.com>],
+ q[Jane!#$%&'*+-/=?^_{|}~Doe' <jdoe@example.com>],
+ q["<jdoe@example.com>"],
+ q["Jane jdoe@example.com"],
+ q[Jane Doe <jdoe @ example.com >],
+ q[Jane Doe < jdoe@example.com >],
+ q[Jane @ Doe @ Jane @ Doe],
+ q["Jane, 'Doe'" <jdoe@example.com>],
+ q['Doe, "Jane' <jdoe@example.com>],
+ q["Jane" "Do"e <jdoe@example.com>],
+ q["Jane' Doe" <jdoe@example.com>],
+ q["Jane Doe <jdoe@example.com>" <jdoe@example.com>],
+ q["Jane\" Doe" <jdoe@example.com>],
+ q[Doe, jane <jdoe@example.com>],
+ q["Jane Doe <jdoe@example.com>],
+ q['Jane 'Doe' <jdoe@example.com>]);
+
+my @known_failure_list = (q[Jane\ Doe <jdoe@example.com>],
+ q["Doe, Ja"ne <jdoe@example.com>],
+ q["Doe, Katarina" Jane <jdoe@example.com>],
+ q[Jane@:;\.,()<>Doe <jdoe@example.com>],
+ q[Jane jdoe@example.com],
+ q[<jdoe@example.com> Jane Doe],
+ q[Jane <jdoe@example.com> Doe],
+ q["Jane "Kat"a" ri"na" ",Doe" <jdoe@example.com>],
+ q[Jane Doe],
+ q[Jane "Doe <jdoe@example.com>"],
+ q[\"Jane Doe <jdoe@example.com>],
+ q[Jane\"\" Doe <jdoe@example.com>],
+ q['Jane "Katarina\" \' Doe' <jdoe@example.com>]);
+
+foreach my $str (@success_list) {
+ my @expected = map { $_->format } Mail::Address->parse("$str");
+ my @actual = Git::parse_mailboxes("$str");
+ is_deeply(\@expected, \@actual, qq[same output : $str]);
+}
+
+TODO: {
+ local $TODO = "known breakage";
+ foreach my $str (@known_failure_list) {
+ my @expected = map { $_->format } Mail::Address->parse("$str");
+ my @actual = Git::parse_mailboxes("$str");
+ is_deeply(\@expected, \@actual, qq[same output : $str]);
+ }
+}
+
+my $is_passing = eval { Test::More->is_passing };
+exit($is_passing ? 0 : 1) unless $@ =~ /Can't locate object method/;
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 23597cc..b3355d2 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -3,41 +3,40 @@
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() {
+# May be altered later in the test
+PREREQ="PERL"
+
+test_expect_success $PREREQ '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 $PREREQ 'Setup helper tool' '
+ write_script fake.sendmail <<-\EOF &&
+ shift
+ output=1
+ while test -f commandline$output
+ do
+ output=$(($output+1))
+ done
+ for a
+ do
+ echo "!$a!"
+ done >commandline$output
+ cat >"msgtxt$output"
+ EOF
+ 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_expect_success $PREREQ '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
@@ -50,85 +49,98 @@ test_no_confirm () {
--to=nobody@example.com \
--smtp-server="$(pwd)/fake.sendmail" \
$@ \
- $patches > stdout &&
+ $patches >stdout &&
test_must_fail grep "Send this email" stdout &&
- > no_confirm_okay
+ >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
- }
+ if ! test -f no_confirm_okay
+ then
+ say 'confirm test failed; skipping remaining tests to prevent hanging'
+ PREREQ="$PREREQ,CHECK_NO_CONFIRM"
+ fi
+ return 0
}
-test_expect_success 'No confirm with --suppress-cc' '
- test_no_confirm --suppress-cc=sob
+test_expect_success $PREREQ 'No confirm with --suppress-cc' '
+ test_no_confirm --suppress-cc=sob &&
+ check_no_confirm
'
-check_no_confirm
-test_expect_success 'No confirm with --confirm=never' '
- test_no_confirm --confirm=never
+
+test_expect_success $PREREQ 'No confirm with --confirm=never' '
+ test_no_confirm --confirm=never &&
+ check_no_confirm
'
-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' '
+test_expect_success $PREREQ 'No confirm with sendemail.confirm=never' '
git config sendemail.confirm never &&
- test_no_confirm --compose --subject=foo
+ test_no_confirm --compose --subject=foo &&
+ check_no_confirm
'
-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
+test_expect_success $PREREQ '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'
+test_expect_success $PREREQ 'setup expect' '
+ cat >expected <<-\EOF
+ !nobody@example.com!
+ !author@example.com!
+ !one@example.com!
+ !two@example.com!
+ EOF
+'
+
+test_expect_success $PREREQ 'Verify commandline' '
+ test_cmp expected commandline1
+'
+
+test_expect_success $PREREQ 'Send patches with --envelope-sender' '
+ clean_fake_sendmail &&
+ git send-email --envelope-sender="Patch Contributor <patch@example.com>" --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
+'
+
+test_expect_success $PREREQ 'setup expect' '
+ cat >expected <<-\EOF
+ !patch@example.com!
+ !-i!
+ !nobody@example.com!
+ !author@example.com!
+ !one@example.com!
+ !two@example.com!
+ EOF
+'
+test_expect_success $PREREQ 'Verify commandline' '
+ test_cmp expected commandline1
+'
+
+test_expect_success $PREREQ '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
+'
+
+test_expect_success $PREREQ 'setup expect' '
+ cat >expected <<-\EOF
+ !nobody@example.com!
+ !-i!
+ !nobody@example.com!
+ !author@example.com!
+ !one@example.com!
+ !two@example.com!
+ EOF
+'
+
+test_expect_success $PREREQ 'Verify commandline' '
+ test_cmp expected commandline1
+'
+
+test_expect_success $PREREQ 'setup expect' "
cat >expected-show-all-headers <<\EOF
0001-Second.patch
(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -158,8 +170,91 @@ References: <unique-message-id@example.com>
Result: OK
EOF
+"
+
+test_suppress_self () {
+ test_commit $3 &&
+ test_when_finished "git reset --hard HEAD^" &&
-test_expect_success 'Show all headers' '
+ write_script cccmd-sed <<-EOF &&
+ sed -n -e s/^cccmd--//p "\$1"
+ EOF
+
+ git commit --amend --author="$1 <$2>" -F - &&
+ clean_fake_sendmail &&
+ git format-patch --stdout -1 >"suppress-self-$3.patch" &&
+
+ git send-email --from="$1 <$2>" \
+ --to=nobody@example.com \
+ --cc-cmd=./cccmd-sed \
+ --suppress-cc=self \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ suppress-self-$3.patch &&
+
+ mv msgtxt1 msgtxt1-$3 &&
+ sed -e '/^$/q' msgtxt1-$3 >"msghdr1-$3" &&
+ >"expected-no-cc-$3" &&
+
+ (grep '^Cc:' msghdr1-$3 >"actual-no-cc-$3";
+ test_cmp expected-no-cc-$3 actual-no-cc-$3)
+}
+
+test_suppress_self_unquoted () {
+ test_suppress_self "$1" "$2" "unquoted-$3" <<-EOF
+ test suppress-cc.self unquoted-$3 with name $1 email $2
+
+ unquoted-$3
+
+ cccmd--$1 <$2>
+
+ Cc: $1 <$2>
+ Signed-off-by: $1 <$2>
+ EOF
+}
+
+test_suppress_self_quoted () {
+ test_suppress_self "$1" "$2" "quoted-$3" <<-EOF
+ test suppress-cc.self quoted-$3 with name $1 email $2
+
+ quoted-$3
+
+ cccmd--"$1" <$2>
+
+ Cc: $1 <$2>
+ Cc: "$1" <$2>
+ Signed-off-by: $1 <$2>
+ Signed-off-by: "$1" <$2>
+ EOF
+}
+
+test_expect_success $PREREQ 'self name is suppressed' "
+ test_suppress_self_unquoted 'A U Thor' 'author@example.com' \
+ 'self_name_suppressed'
+"
+
+test_expect_success $PREREQ 'self name with dot is suppressed' "
+ test_suppress_self_quoted 'A U. Thor' 'author@example.com' \
+ 'self_name_dot_suppressed'
+"
+
+test_expect_success $PREREQ 'non-ascii self name is suppressed' "
+ test_suppress_self_quoted 'Füñný Nâmé' 'odd_?=mail@example.com' \
+ 'non_ascii_self_suppressed'
+"
+
+# This name is long enough to force format-patch to split it into multiple
+# encoded-words, assuming it uses UTF-8 with the "Q" encoding.
+test_expect_success $PREREQ 'long non-ascii self name is suppressed' "
+ test_suppress_self_quoted 'Ƒüñníęř €. Nâá¹Ã©' 'odd_?=mail@example.com' \
+ 'long_non_ascii_self_suppressed'
+"
+
+test_expect_success $PREREQ 'sanitized self name is suppressed' "
+ test_suppress_self_unquoted '\"A U. Thor\"' 'author@example.com' \
+ 'self_name_sanitized_suppressed'
+"
+
+test_expect_success $PREREQ 'Show all headers' '
git send-email \
--dry-run \
--suppress-cc=sob \
@@ -177,28 +272,72 @@ test_expect_success 'Show all headers' '
test_cmp expected-show-all-headers actual-show-all-headers
'
-test_expect_success 'Prompting works' '
+test_expect_success $PREREQ 'Prompting works' '
clean_fake_sendmail &&
- (echo "Example <from@example.com>"
- echo "to@example.com"
+ (echo "to@example.com"
echo ""
) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
--smtp-server="$(pwd)/fake.sendmail" \
$patches \
2>errors &&
- grep "^From: Example <from@example.com>\$" msgtxt1 &&
+ grep "^From: A U Thor <author@example.com>\$" msgtxt1 &&
grep "^To: to@example.com\$" msgtxt1
'
-test_expect_success 'cccmd works' '
+test_expect_success $PREREQ,AUTOIDENT 'implicit ident is allowed' '
+ clean_fake_sendmail &&
+ (sane_unset GIT_AUTHOR_NAME &&
+ sane_unset GIT_AUTHOR_EMAIL &&
+ sane_unset GIT_COMMITTER_NAME &&
+ sane_unset GIT_COMMITTER_EMAIL &&
+ GIT_SEND_EMAIL_NOTTY=1 git send-email \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --to=to@example.com \
+ $patches </dev/null 2>errors
+ )
+'
+
+test_expect_success $PREREQ,!AUTOIDENT 'broken implicit ident aborts send-email' '
+ clean_fake_sendmail &&
+ (sane_unset GIT_AUTHOR_NAME &&
+ sane_unset GIT_AUTHOR_EMAIL &&
+ sane_unset GIT_COMMITTER_NAME &&
+ sane_unset GIT_COMMITTER_EMAIL &&
+ GIT_SEND_EMAIL_NOTTY=1 && export GIT_SEND_EMAIL_NOTTY &&
+ test_must_fail git send-email \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --to=to@example.com \
+ $patches </dev/null 2>errors &&
+ test_i18ngrep "tell me who you are" errors
+ )
+'
+
+test_expect_success $PREREQ 'setup tocmd and cccmd scripts' '
+ write_script tocmd-sed <<-\EOF &&
+ sed -n -e "s/^tocmd--//p" "$1"
+ EOF
+ write_script cccmd-sed <<-\EOF
+ sed -n -e "s/^cccmd--//p" "$1"
+ EOF
+'
+
+test_expect_success $PREREQ 'tocmd works' '
+ clean_fake_sendmail &&
+ cp $patches tocmd.patch &&
+ echo tocmd--tocmd@example.com >>tocmd.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to-cmd=./tocmd-sed \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ tocmd.patch \
+ &&
+ grep "^To: tocmd@example.com" msgtxt1
+'
+
+test_expect_success $PREREQ '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 &&
+ echo "cccmd-- cccmd@example.com" >>cccmd.patch &&
git send-email \
--from="Example <nobody@example.com>" \
--to=nobody@example.com \
@@ -209,10 +348,10 @@ test_expect_success 'cccmd works' '
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' '
+test_expect_success $PREREQ 'reject long lines' '
+ z8=zzzzzzzz &&
+ z64=$z8$z8$z8$z8$z8$z8$z8$z8 &&
+ z512=$z64$z64$z64$z64$z64$z64$z64$z64 &&
clean_fake_sendmail &&
cp $patches longline.patch &&
echo $z512$z512 >>longline.patch &&
@@ -225,75 +364,117 @@ test_expect_success 'reject long lines' '
grep longline.patch errors
'
-test_expect_success 'no patch was sent' '
+test_expect_success $PREREQ 'no patch was sent' '
! test -e commandline1
'
-test_expect_success 'Author From: in message body' '
+test_expect_success $PREREQ '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
+ sed "1,/^\$/d" <msgtxt1 >msgbody1 &&
grep "From: A <author@example.com>" msgbody1
'
-test_expect_success 'Author From: not in message body' '
+test_expect_success $PREREQ '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
+ sed "1,/^\$/d" <msgtxt1 >msgbody1 &&
! grep "From: A <author@example.com>" msgbody1
'
-test_expect_success 'allow long lines with --no-validate' '
+test_expect_success $PREREQ 'allow long lines with --no-validate' '
git send-email \
--from="Example <nobody@example.com>" \
--to=nobody@example.com \
--smtp-server="$(pwd)/fake.sendmail" \
- --novalidate \
+ --no-validate \
$patches longline.patch \
2>errors
'
-test_expect_success 'Invalid In-Reply-To' '
+test_expect_success $PREREQ '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
+ $patches \
+ 2>errors &&
! grep "^In-Reply-To: < *>" msgtxt1
'
-test_expect_success 'Valid In-Reply-To when prompting' '
+test_expect_success $PREREQ '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 \
+ ) | 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_expect_success $PREREQ 'In-Reply-To without --chain-reply-to' '
+ clean_fake_sendmail &&
+ echo "<unique-message-id@example.com>" >expect &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --no-chain-reply-to \
+ --in-reply-to="$(cat expect)" \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches $patches $patches \
+ 2>errors &&
+ # The first message is a reply to --in-reply-to
+ sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt1 >actual &&
+ test_cmp expect actual &&
+ # Second and subsequent messages are replies to the first one
+ sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt1 >expect &&
+ sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt2 >actual &&
+ test_cmp expect actual &&
+ sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt3 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success $PREREQ 'In-Reply-To with --chain-reply-to' '
+ clean_fake_sendmail &&
+ echo "<unique-message-id@example.com>" >expect &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --chain-reply-to \
+ --in-reply-to="$(cat expect)" \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches $patches $patches \
+ 2>errors &&
+ sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt1 >actual &&
+ test_cmp expect actual &&
+ sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt1 >expect &&
+ sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt2 >actual &&
+ test_cmp expect actual &&
+ sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt2 >expect &&
+ sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt3 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success $PREREQ 'setup fake editor' '
+ write_script fake-editor <<-\EOF
+ echo fake edit >>"$1"
+ EOF
'
test_set_editor "$(pwd)/fake-editor"
-test_expect_success '--compose works' '
+test_expect_success $PREREQ '--compose works' '
clean_fake_sendmail &&
git send-email \
--compose --subject foo \
@@ -304,14 +485,15 @@ test_expect_success '--compose works' '
2>errors
'
-test_expect_success 'first message is compose text' '
+test_expect_success $PREREQ 'first message is compose text' '
grep "^fake edit" msgtxt1
'
-test_expect_success 'second message is patch' '
+test_expect_success $PREREQ 'second message is patch' '
grep "Subject:.*Second" msgtxt2
'
+test_expect_success $PREREQ 'setup expect' "
cat >expected-suppress-sob <<\EOF
0001-Second.patch
(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -338,6 +520,13 @@ X-Mailer: X-MAILER-STRING
Result: OK
EOF
+"
+
+replace_variable_fields () {
+ sed -e "s/^\(Date:\).*/\1 DATE-STRING/" \
+ -e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
+ -e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/"
+}
test_suppression () {
git send-email \
@@ -346,19 +535,17 @@ test_suppression () {
--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/" \
+ $patches | replace_variable_fields \
>actual-suppress-$1${2+"-$2"} &&
test_cmp expected-suppress-$1${2+"-$2"} actual-suppress-$1${2+"-$2"}
}
-test_expect_success 'sendemail.cc set' '
+test_expect_success $PREREQ 'sendemail.cc set' '
git config sendemail.cc cc@example.com &&
test_suppression sob
'
+test_expect_success $PREREQ 'setup expect' "
cat >expected-suppress-sob <<\EOF
0001-Second.patch
(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -383,12 +570,14 @@ X-Mailer: X-MAILER-STRING
Result: OK
EOF
+"
-test_expect_success 'sendemail.cc unset' '
+test_expect_success $PREREQ 'sendemail.cc unset' '
git config --unset sendemail.cc &&
test_suppression sob
'
+test_expect_success $PREREQ 'setup expect' "
cat >expected-suppress-cccmd <<\EOF
0001-Second.patch
(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -416,14 +605,17 @@ X-Mailer: X-MAILER-STRING
Result: OK
EOF
+"
-test_expect_success 'sendemail.cccmd' '
- echo echo cc-cmd@example.com > cccmd &&
- chmod +x cccmd &&
+test_expect_success $PREREQ 'sendemail.cccmd' '
+ write_script cccmd <<-\EOF &&
+ echo cc-cmd@example.com
+ EOF
git config sendemail.cccmd ./cccmd &&
test_suppression cccmd
'
+test_expect_success $PREREQ 'setup expect' '
cat >expected-suppress-all <<\EOF
0001-Second.patch
Dry-OK. Log says:
@@ -439,11 +631,13 @@ X-Mailer: X-MAILER-STRING
Result: OK
EOF
+'
-test_expect_success '--suppress-cc=all' '
+test_expect_success $PREREQ '--suppress-cc=all' '
test_suppression all
'
+test_expect_success $PREREQ 'setup expect' "
cat >expected-suppress-body <<\EOF
0001-Second.patch
(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -471,11 +665,13 @@ X-Mailer: X-MAILER-STRING
Result: OK
EOF
+"
-test_expect_success '--suppress-cc=body' '
+test_expect_success $PREREQ '--suppress-cc=body' '
test_suppression body
'
+test_expect_success $PREREQ 'setup expect' "
cat >expected-suppress-body-cccmd <<\EOF
0001-Second.patch
(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -500,11 +696,13 @@ X-Mailer: X-MAILER-STRING
Result: OK
EOF
+"
-test_expect_success '--suppress-cc=body --suppress-cc=cccmd' '
+test_expect_success $PREREQ '--suppress-cc=body --suppress-cc=cccmd' '
test_suppression body cccmd
'
+test_expect_success $PREREQ 'setup expect' "
cat >expected-suppress-sob <<\EOF
0001-Second.patch
(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -529,12 +727,14 @@ X-Mailer: X-MAILER-STRING
Result: OK
EOF
+"
-test_expect_success '--suppress-cc=sob' '
- git config --unset sendemail.cccmd
+test_expect_success $PREREQ '--suppress-cc=sob' '
+ test_might_fail git config --unset sendemail.cccmd &&
test_suppression sob
'
+test_expect_success $PREREQ 'setup expect' "
cat >expected-suppress-bodycc <<\EOF
0001-Second.patch
(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -562,11 +762,13 @@ X-Mailer: X-MAILER-STRING
Result: OK
EOF
+"
-test_expect_success '--suppress-cc=bodycc' '
+test_expect_success $PREREQ '--suppress-cc=bodycc' '
test_suppression bodycc
'
+test_expect_success $PREREQ 'setup expect' "
cat >expected-suppress-cc <<\EOF
0001-Second.patch
(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -588,8 +790,9 @@ X-Mailer: X-MAILER-STRING
Result: OK
EOF
+"
-test_expect_success '--suppress-cc=cc' '
+test_expect_success $PREREQ '--suppress-cc=cc' '
test_suppression cc
'
@@ -600,46 +803,40 @@ test_confirm () {
--from="Example <nobody@example.com>" \
--to=nobody@example.com \
--smtp-server="$(pwd)/fake.sendmail" \
- $@ $patches > stdout &&
+ $@ $patches >stdout &&
grep "Send this email" stdout
}
-test_expect_success '--confirm=always' '
+test_expect_success $PREREQ '--confirm=always' '
test_confirm --confirm=always --suppress-cc=all
'
-test_expect_success '--confirm=auto' '
+test_expect_success $PREREQ '--confirm=auto' '
test_confirm --confirm=auto
'
-test_expect_success '--confirm=cc' '
+test_expect_success $PREREQ '--confirm=cc' '
test_confirm --confirm=cc
'
-test_expect_success '--confirm=compose' '
+test_expect_success $PREREQ '--confirm=compose' '
test_confirm --confirm=compose --compose
'
-test_expect_success 'confirm by default (due to cc)' '
- CONFIRM=$(git config --get sendemail.confirm) &&
+test_expect_success $PREREQ 'confirm by default (due to cc)' '
+ test_when_finished git config sendemail.confirm never &&
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) &&
+test_expect_success $PREREQ 'confirm by default (due to --compose)' '
+ test_when_finished git config sendemail.confirm never &&
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) &&
+test_expect_success $PREREQ 'confirm detects EOF (inform assumes y)' '
+ test_when_finished git config sendemail.confirm never &&
git config --unset sendemail.confirm &&
rm -fr outdir &&
git format-patch -2 -o outdir &&
@@ -648,14 +845,11 @@ test_expect_success 'confirm detects EOF (inform assumes y)' '
--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"
+ outdir/*.patch </dev/null
'
-test_expect_success 'confirm detects EOF (auto causes failure)' '
- CONFIRM=$(git config --get sendemail.confirm) &&
+test_expect_success $PREREQ 'confirm detects EOF (auto causes failure)' '
+ test_when_finished git config sendemail.confirm never &&
git config sendemail.confirm auto &&
GIT_SEND_EMAIL_NOTTY=1 &&
export GIT_SEND_EMAIL_NOTTY &&
@@ -663,14 +857,11 @@ test_expect_success 'confirm detects EOF (auto causes failure)' '
--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"
+ $patches </dev/null
'
-test_expect_success 'confirm doesnt loop forever' '
- CONFIRM=$(git config --get sendemail.confirm) &&
+test_expect_success $PREREQ 'confirm does not loop forever' '
+ test_when_finished git config sendemail.confirm never &&
git config sendemail.confirm auto &&
GIT_SEND_EMAIL_NOTTY=1 &&
export GIT_SEND_EMAIL_NOTTY &&
@@ -679,12 +870,9 @@ test_expect_success 'confirm doesnt loop forever' '
--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' '
+test_expect_success $PREREQ 'utf8 Cc is rfc2047 encoded' '
clean_fake_sendmail &&
rm -fr outdir &&
git format-patch -1 -o outdir --cc="àéìöú <utf8@example.com>" &&
@@ -697,162 +885,206 @@ test_expect_success 'utf8 Cc is rfc2047 encoded' '
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' '
+test_expect_success $PREREQ '--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 &&
+ write_script fake-editor-utf8 <<-\EOF &&
+ echo "utf8 body: àéìöú" >>"$1"
+ EOF
+ 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 &&
+test_expect_success $PREREQ '--compose respects user mime type' '
+ clean_fake_sendmail &&
+ write_script fake-editor-utf8-mime <<-\EOF &&
+ cat >"$1" <<-\EOM
+ MIME-Version: 1.0
+ Content-Type: text/plain; charset=iso-8859-1
+ Content-Transfer-Encoding: 8bit
+ Subject: foo
+
+ utf8 body: àéìöú
+ EOM
+ EOF
+ 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' '
+test_expect_success $PREREQ '--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 &&
+ 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 &&
+test_expect_success $PREREQ 'utf8 author is correctly passed on' '
+ clean_fake_sendmail &&
+ test_commit weird_author &&
+ test_when_finished "git reset --hard HEAD^" &&
+ git commit --amend --author "Füñný Nâmé <odd_?=mail@example.com>" &&
+ git format-patch --stdout -1 >funny_name.patch &&
+ git send-email --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ funny_name.patch &&
+ grep "^From: Füñný Nâmé <odd_?=mail@example.com>" msgtxt1
+'
+
+test_expect_success $PREREQ 'utf8 sender is not duplicated' '
+ clean_fake_sendmail &&
+ test_commit weird_sender &&
+ test_when_finished "git reset --hard HEAD^" &&
+ git commit --amend --author "Füñný Nâmé <odd_?=mail@example.com>" &&
+ git format-patch --stdout -1 >funny_name.patch &&
+ git send-email --from="Füñný Nâmé <odd_?=mail@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ funny_name.patch &&
+ grep "^From: " msgtxt1 >msgfrom &&
+ test_line_count = 1 msgfrom
+'
+
+test_expect_success $PREREQ 'sendemail.composeencoding works' '
+ clean_fake_sendmail &&
+ git config sendemail.composeencoding iso-8859-1 &&
+ write_script fake-editor-utf8 <<-\EOF &&
+ echo "utf8 body: àéìöú" >>"$1"
+ EOF
+ GIT_EDITOR="\"$(pwd)/fake-editor-utf8\"" \
+ git send-email \
+ --compose --subject foo \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches &&
+ grep "^utf8 body" msgtxt1 &&
+ grep "^Content-Type: text/plain; charset=iso-8859-1" msgtxt1
+'
+
+test_expect_success $PREREQ '--compose-encoding works' '
+ clean_fake_sendmail &&
+ write_script fake-editor-utf8 <<-\EOF &&
+ echo "utf8 body: àéìöú" >>"$1"
+ EOF
+ GIT_EDITOR="\"$(pwd)/fake-editor-utf8\"" \
+ git send-email \
+ --compose-encoding iso-8859-1 \
+ --compose --subject foo \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches &&
+ grep "^utf8 body" msgtxt1 &&
+ grep "^Content-Type: text/plain; charset=iso-8859-1" msgtxt1
+'
+
+test_expect_success $PREREQ '--compose-encoding overrides sendemail.composeencoding' '
+ clean_fake_sendmail &&
+ git config sendemail.composeencoding iso-8859-1 &&
+ write_script fake-editor-utf8 <<-\EOF &&
+ echo "utf8 body: àéìöú" >>"$1"
+ EOF
+ GIT_EDITOR="\"$(pwd)/fake-editor-utf8\"" \
+ git send-email \
+ --compose-encoding iso-8859-2 \
+ --compose --subject foo \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches &&
+ grep "^utf8 body" msgtxt1 &&
+ grep "^Content-Type: text/plain; charset=iso-8859-2" msgtxt1
+'
+
+test_expect_success $PREREQ '--compose-encoding adds correct MIME for subject' '
+ clean_fake_sendmail &&
+ GIT_EDITOR="\"$(pwd)/fake-editor\"" \
+ git send-email \
+ --compose-encoding iso-8859-2 \
+ --compose --subject utf8-sübjëct \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches &&
+ grep "^fake edit" msgtxt1 &&
+ grep "^Subject: =?iso-8859-2?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1
+'
+
+test_expect_success $PREREQ 'detects ambiguous reference/file conflict' '
+ echo master >master &&
git add master &&
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' '
+test_expect_success $PREREQ '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 &&
+ --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' '
+test_expect_success $PREREQ '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 \
+ --no-thread \
$patches |
grep "In-Reply-To: <in-reply-id@example.com>"
'
-test_expect_success 'no in-reply-to and no threading' '
+test_expect_success $PREREQ 'no in-reply-to and no threading' '
git send-email \
--dry-run \
--from="Example <nobody@example.com>" \
--to=nobody@example.com \
- --nothread \
+ --no-thread \
$patches $patches >stdout &&
! grep "In-Reply-To: " stdout
'
-test_expect_success 'threading but no chain-reply-to' '
+test_expect_success $PREREQ 'threading but no chain-reply-to' '
git send-email \
--dry-run \
--from="Example <nobody@example.com>" \
--to=nobody@example.com \
--thread \
- --nochain-reply-to \
+ --no-chain-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' '
+test_expect_success $PREREQ 'sendemail.to works' '
git config --replace-all sendemail.to "Somebody <somebody@ex.com>" &&
git send-email \
--dry-run \
@@ -861,7 +1093,7 @@ test_expect_success 'sendemail.to works' '
grep "To: Somebody <somebody@ex.com>" stdout
'
-test_expect_success '--no-to overrides sendemail.to' '
+test_expect_success $PREREQ '--no-to overrides sendemail.to' '
git send-email \
--dry-run \
--from="Example <nobody@example.com>" \
@@ -872,7 +1104,7 @@ test_expect_success '--no-to overrides sendemail.to' '
! grep "To: Somebody <somebody@ex.com>" stdout
'
-test_expect_success 'sendemail.cc works' '
+test_expect_success $PREREQ 'sendemail.cc works' '
git config --replace-all sendemail.cc "Somebody <somebody@ex.com>" &&
git send-email \
--dry-run \
@@ -882,7 +1114,7 @@ test_expect_success 'sendemail.cc works' '
grep "Cc: Somebody <somebody@ex.com>" stdout
'
-test_expect_success '--no-cc overrides sendemail.cc' '
+test_expect_success $PREREQ '--no-cc overrides sendemail.cc' '
git send-email \
--dry-run \
--from="Example <nobody@example.com>" \
@@ -894,7 +1126,7 @@ test_expect_success '--no-cc overrides sendemail.cc' '
! grep "Cc: Somebody <somebody@ex.com>" stdout
'
-test_expect_success 'sendemail.bcc works' '
+test_expect_success $PREREQ 'sendemail.bcc works' '
git config --replace-all sendemail.bcc "Other <other@ex.com>" &&
git send-email \
--dry-run \
@@ -905,7 +1137,7 @@ test_expect_success 'sendemail.bcc works' '
grep "RCPT TO:<other@ex.com>" stdout
'
-test_expect_success '--no-bcc overrides sendemail.bcc' '
+test_expect_success $PREREQ '--no-bcc overrides sendemail.bcc' '
git send-email \
--dry-run \
--from="Example <nobody@example.com>" \
@@ -918,7 +1150,47 @@ test_expect_success '--no-bcc overrides sendemail.bcc' '
! grep "RCPT TO:<other@ex.com>" stdout
'
-cat >email-using-8bit <<EOF
+test_expect_success $PREREQ 'patches To headers are used by default' '
+ patch=$(git format-patch -1 --to="bodies@example.com") &&
+ test_when_finished "rm $patch" &&
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --smtp-server relay.example.com \
+ $patch >stdout &&
+ grep "RCPT TO:<bodies@example.com>" stdout
+'
+
+test_expect_success $PREREQ 'patches To headers are appended to' '
+ patch=$(git format-patch -1 --to="bodies@example.com") &&
+ test_when_finished "rm $patch" &&
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server relay.example.com \
+ $patch >stdout &&
+ grep "RCPT TO:<bodies@example.com>" stdout &&
+ grep "RCPT TO:<nobody@example.com>" stdout
+'
+
+test_expect_success $PREREQ 'To headers from files reset each patch' '
+ patch1=$(git format-patch -1 --to="bodies@example.com") &&
+ patch2=$(git format-patch -1 --to="other@example.com" HEAD~) &&
+ test_when_finished "rm $patch1 && rm $patch2" &&
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --to="nobody@example.com" \
+ --smtp-server relay.example.com \
+ $patch1 $patch2 >stdout &&
+ test $(grep -c "RCPT TO:<bodies@example.com>" stdout) = 1 &&
+ test $(grep -c "RCPT TO:<nobody@example.com>" stdout) = 2 &&
+ test $(grep -c "RCPT TO:<other@example.com>" stdout) = 1
+'
+
+test_expect_success $PREREQ 'setup expect' '
+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
@@ -927,14 +1199,32 @@ 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 $PREREQ 'setup expect' '
+ echo "Subject: subject goes here" >expected
+'
+
+test_expect_success $PREREQ 'ASCII subject is not RFC2047 quoted' '
+ clean_fake_sendmail &&
+ echo bogus |
+ git send-email --from=author@example.com --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --8bit-encoding=UTF-8 \
+ email-using-8bit >stdout &&
+ grep "Subject" msgtxt1 >actual &&
+ test_cmp expected actual
+'
-test_expect_success 'asks about and fixes 8bit encodings' '
+test_expect_success $PREREQ 'setup expect' '
+ cat >content-type-decl <<-\EOF
+ MIME-Version: 1.0
+ Content-Type: text/plain; charset=UTF-8
+ Content-Transfer-Encoding: 8bit
+ EOF
+'
+
+test_expect_success $PREREQ 'asks about and fixes 8bit encodings' '
clean_fake_sendmail &&
echo |
git send-email --from=author@example.com --to=nobody@example.com \
@@ -947,7 +1237,7 @@ test_expect_success 'asks about and fixes 8bit encodings' '
test_cmp actual content-type-decl
'
-test_expect_success 'sendemail.8bitEncoding works' '
+test_expect_success $PREREQ 'sendemail.8bitEncoding works' '
clean_fake_sendmail &&
git config sendemail.assume8bitEncoding UTF-8 &&
echo bogus |
@@ -958,7 +1248,7 @@ test_expect_success 'sendemail.8bitEncoding works' '
test_cmp actual content-type-decl
'
-test_expect_success '--8bit-encoding overrides sendemail.8bitEncoding' '
+test_expect_success $PREREQ '--8bit-encoding overrides sendemail.8bitEncoding' '
clean_fake_sendmail &&
git config sendemail.assume8bitEncoding "bogus too" &&
echo bogus |
@@ -970,21 +1260,25 @@ test_expect_success '--8bit-encoding overrides sendemail.8bitEncoding' '
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!
+test_expect_success $PREREQ 'setup expect' '
+ 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
+ Nothing to see here.
+ EOF
+'
-cat >expected <<EOF
-Subject: =?UTF-8?q?Dieser=20Betreff=20enth=C3=A4lt=20auch=20einen=20Umlaut!?=
-EOF
+test_expect_success $PREREQ 'setup expect' '
+ cat >expected <<-\EOF
+ Subject: =?UTF-8?q?Dieser=20Betreff=20enth=C3=A4lt=20auch=20einen=20Umlaut!?=
+ EOF
+'
-test_expect_success '--8bit-encoding also treats subject' '
+test_expect_success $PREREQ '--8bit-encoding also treats subject' '
clean_fake_sendmail &&
echo bogus |
git send-email --from=author@example.com --to=nobody@example.com \
@@ -995,4 +1289,600 @@ test_expect_success '--8bit-encoding also treats subject' '
test_cmp expected actual
'
+test_expect_success $PREREQ 'setup expect' '
+ cat >email-using-8bit <<-\EOF
+ From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
+ Message-Id: <bogus-message-id@example.com>
+ From: A U Thor <author@example.com>
+ Date: Sat, 12 Jun 2010 15:53:58 +0200
+ Content-Type: text/plain; charset=UTF-8
+ Subject: Nothing to see here.
+
+ Dieser Betreff enthält auch einen Umlaut!
+ EOF
+'
+
+test_expect_success $PREREQ 'sendemail.transferencoding=7bit fails on 8bit data' '
+ clean_fake_sendmail &&
+ git config sendemail.transferEncoding 7bit &&
+ test_must_fail git send-email \
+ --transfer-encoding=7bit \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ email-using-8bit \
+ 2>errors >out &&
+ grep "cannot send message as 7bit" errors &&
+ test -z "$(ls msgtxt*)"
+'
+
+test_expect_success $PREREQ '--transfer-encoding overrides sendemail.transferEncoding' '
+ clean_fake_sendmail &&
+ git config sendemail.transferEncoding 8bit &&
+ test_must_fail git send-email \
+ --transfer-encoding=7bit \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ email-using-8bit \
+ 2>errors >out &&
+ grep "cannot send message as 7bit" errors &&
+ test -z "$(ls msgtxt*)"
+'
+
+test_expect_success $PREREQ 'sendemail.transferencoding=8bit' '
+ clean_fake_sendmail &&
+ git send-email \
+ --transfer-encoding=8bit \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ email-using-8bit \
+ 2>errors >out &&
+ sed '1,/^$/d' msgtxt1 >actual &&
+ sed '1,/^$/d' email-using-8bit >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success $PREREQ 'setup expect' '
+ cat >expected <<-\EOF
+ Dieser Betreff enth=C3=A4lt auch einen Umlaut!
+ EOF
+'
+
+test_expect_success $PREREQ '8-bit and sendemail.transferencoding=quoted-printable' '
+ clean_fake_sendmail &&
+ git send-email \
+ --transfer-encoding=quoted-printable \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ email-using-8bit \
+ 2>errors >out &&
+ sed '1,/^$/d' msgtxt1 >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success $PREREQ 'setup expect' '
+ cat >expected <<-\EOF
+ RGllc2VyIEJldHJlZmYgZW50aMOkbHQgYXVjaCBlaW5lbiBVbWxhdXQhCg==
+ EOF
+'
+
+test_expect_success $PREREQ '8-bit and sendemail.transferencoding=base64' '
+ clean_fake_sendmail &&
+ git send-email \
+ --transfer-encoding=base64 \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ email-using-8bit \
+ 2>errors >out &&
+ sed '1,/^$/d' msgtxt1 >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success $PREREQ 'setup expect' '
+ cat >email-using-qp <<-\EOF
+ From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
+ Message-Id: <bogus-message-id@example.com>
+ From: A U Thor <author@example.com>
+ Date: Sat, 12 Jun 2010 15:53:58 +0200
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: quoted-printable
+ Content-Type: text/plain; charset=UTF-8
+ Subject: Nothing to see here.
+
+ Dieser Betreff enth=C3=A4lt auch einen Umlaut!
+ EOF
+'
+
+test_expect_success $PREREQ 'convert from quoted-printable to base64' '
+ clean_fake_sendmail &&
+ git send-email \
+ --transfer-encoding=base64 \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ email-using-qp \
+ 2>errors >out &&
+ sed '1,/^$/d' msgtxt1 >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success $PREREQ 'setup expect' "
+tr -d '\\015' | tr '%' '\\015' >email-using-crlf <<EOF
+From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
+Message-Id: <bogus-message-id@example.com>
+From: A U Thor <author@example.com>
+Date: Sat, 12 Jun 2010 15:53:58 +0200
+Content-Type: text/plain; charset=UTF-8
+Subject: Nothing to see here.
+
+Look, I have a CRLF and an = sign!%
+EOF
+"
+
+test_expect_success $PREREQ 'setup expect' '
+ cat >expected <<-\EOF
+ Look, I have a CRLF and an =3D sign!=0D
+ EOF
+'
+
+test_expect_success $PREREQ 'CRLF and sendemail.transferencoding=quoted-printable' '
+ clean_fake_sendmail &&
+ git send-email \
+ --transfer-encoding=quoted-printable \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ email-using-crlf \
+ 2>errors >out &&
+ sed '1,/^$/d' msgtxt1 >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success $PREREQ 'setup expect' '
+ cat >expected <<-\EOF
+ TG9vaywgSSBoYXZlIGEgQ1JMRiBhbmQgYW4gPSBzaWduIQ0K
+ EOF
+'
+
+test_expect_success $PREREQ 'CRLF and sendemail.transferencoding=base64' '
+ clean_fake_sendmail &&
+ git send-email \
+ --transfer-encoding=base64 \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ email-using-crlf \
+ 2>errors >out &&
+ sed '1,/^$/d' msgtxt1 >actual &&
+ test_cmp expected actual
+'
+
+
+# Note that the patches in this test are deliberately out of order; we
+# want to make sure it works even if the cover-letter is not in the
+# first mail.
+test_expect_success $PREREQ 'refusing to send cover letter template' '
+ clean_fake_sendmail &&
+ rm -fr outdir &&
+ git format-patch --cover-letter -2 -o outdir &&
+ test_must_fail git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ outdir/0002-*.patch \
+ outdir/0000-*.patch \
+ outdir/0001-*.patch \
+ 2>errors >out &&
+ grep "SUBJECT HERE" errors &&
+ test -z "$(ls msgtxt*)"
+'
+
+test_expect_success $PREREQ '--force sends cover letter template anyway' '
+ clean_fake_sendmail &&
+ rm -fr outdir &&
+ git format-patch --cover-letter -2 -o outdir &&
+ git send-email \
+ --force \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ outdir/0002-*.patch \
+ outdir/0000-*.patch \
+ outdir/0001-*.patch \
+ 2>errors >out &&
+ ! grep "SUBJECT HERE" errors &&
+ test -n "$(ls msgtxt*)"
+'
+
+test_cover_addresses () {
+ header="$1"
+ shift
+ clean_fake_sendmail &&
+ rm -fr outdir &&
+ git format-patch --cover-letter -2 -o outdir &&
+ cover=$(echo outdir/0000-*.patch) &&
+ mv $cover cover-to-edit.patch &&
+ perl -pe "s/^From:/$header: extra\@address.com\nFrom:/" cover-to-edit.patch >"$cover" &&
+ git send-email \
+ --force \
+ --from="Example <nobody@example.com>" \
+ --no-to --no-cc \
+ "$@" \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ outdir/0000-*.patch \
+ outdir/0001-*.patch \
+ outdir/0002-*.patch \
+ 2>errors >out &&
+ grep "^$header: extra@address.com" msgtxt1 >to1 &&
+ grep "^$header: extra@address.com" msgtxt2 >to2 &&
+ grep "^$header: extra@address.com" msgtxt3 >to3 &&
+ test_line_count = 1 to1 &&
+ test_line_count = 1 to2 &&
+ test_line_count = 1 to3
+}
+
+test_expect_success $PREREQ 'to-cover adds To to all mail' '
+ test_cover_addresses "To" --to-cover
+'
+
+test_expect_success $PREREQ 'cc-cover adds Cc to all mail' '
+ test_cover_addresses "Cc" --cc-cover
+'
+
+test_expect_success $PREREQ 'tocover adds To to all mail' '
+ test_config sendemail.tocover true &&
+ test_cover_addresses "To"
+'
+
+test_expect_success $PREREQ 'cccover adds Cc to all mail' '
+ test_config sendemail.cccover true &&
+ test_cover_addresses "Cc"
+'
+
+test_expect_success $PREREQ 'escaped quotes in sendemail.aliasfiletype=mutt' '
+ clean_fake_sendmail &&
+ echo "alias sbd \\\"Dot U. Sir\\\" <somebody@example.org>" >.mutt &&
+ git config --replace-all sendemail.aliasesfile "$(pwd)/.mutt" &&
+ git config sendemail.aliasfiletype mutt &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=sbd \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ outdir/0001-*.patch \
+ 2>errors >out &&
+ grep "^!somebody@example\.org!$" commandline1 &&
+ grep -F "To: \"Dot U. Sir\" <somebody@example.org>" out
+'
+
+test_expect_success $PREREQ 'sendemail.aliasfiletype=mailrc' '
+ clean_fake_sendmail &&
+ echo "alias sbd somebody@example.org" >.mailrc &&
+ git config --replace-all sendemail.aliasesfile "$(pwd)/.mailrc" &&
+ git config sendemail.aliasfiletype mailrc &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=sbd \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ outdir/0001-*.patch \
+ 2>errors >out &&
+ grep "^!somebody@example\.org!$" commandline1
+'
+
+test_expect_success $PREREQ 'sendemail.aliasfile=~/.mailrc' '
+ clean_fake_sendmail &&
+ echo "alias sbd someone@example.org" >"$HOME/.mailrc" &&
+ git config --replace-all sendemail.aliasesfile "~/.mailrc" &&
+ git config sendemail.aliasfiletype mailrc &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=sbd \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ outdir/0001-*.patch \
+ 2>errors >out &&
+ grep "^!someone@example\.org!$" commandline1
+'
+
+test_dump_aliases () {
+ msg="$1" && shift &&
+ filetype="$1" && shift &&
+ printf '%s\n' "$@" >expect &&
+ cat >.tmp-email-aliases &&
+
+ test_expect_success $PREREQ "$msg" '
+ clean_fake_sendmail && rm -fr outdir &&
+ git config --replace-all sendemail.aliasesfile \
+ "$(pwd)/.tmp-email-aliases" &&
+ git config sendemail.aliasfiletype "$filetype" &&
+ git send-email --dump-aliases 2>errors >actual &&
+ test_cmp expect actual
+ '
+}
+
+test_dump_aliases '--dump-aliases sendmail format' \
+ 'sendmail' \
+ 'abgroup' \
+ 'alice' \
+ 'bcgrp' \
+ 'bob' \
+ 'chloe' <<-\EOF
+ alice: Alice W Land <awol@example.com>
+ bob: Robert Bobbyton <bob@example.com>
+ chloe: chloe@example.com
+ abgroup: alice, bob
+ bcgrp: bob, chloe, Other <o@example.com>
+ EOF
+
+test_dump_aliases '--dump-aliases mutt format' \
+ 'mutt' \
+ 'alice' \
+ 'bob' \
+ 'chloe' \
+ 'donald' <<-\EOF
+ alias alice Alice W Land <awol@example.com>
+ alias donald Donald C Carlton <donc@example.com>
+ alias bob Robert Bobbyton <bob@example.com>
+ alias chloe chloe@example.com
+ EOF
+
+test_dump_aliases '--dump-aliases mailrc format' \
+ 'mailrc' \
+ 'alice' \
+ 'bob' \
+ 'chloe' \
+ 'eve' <<-\EOF
+ alias alice Alice W Land <awol@example.com>
+ alias eve Eve <eve@example.com>
+ alias bob Robert Bobbyton <bob@example.com>
+ alias chloe chloe@example.com
+ EOF
+
+test_dump_aliases '--dump-aliases pine format' \
+ 'pine' \
+ 'alice' \
+ 'bob' \
+ 'chloe' \
+ 'eve' <<-\EOF
+ alice Alice W Land <awol@example.com>
+ eve Eve <eve@example.com>
+ bob Robert Bobbyton <bob@example.com>
+ chloe chloe@example.com
+ EOF
+
+test_dump_aliases '--dump-aliases gnus format' \
+ 'gnus' \
+ 'alice' \
+ 'bob' \
+ 'chloe' \
+ 'eve' <<-\EOF
+ (define-mail-alias "alice" "awol@example.com")
+ (define-mail-alias "eve" "eve@example.com")
+ (define-mail-alias "bob" "bob@example.com")
+ (define-mail-alias "chloe" "chloe@example.com")
+ EOF
+
+test_expect_success '--dump-aliases must be used alone' '
+ test_must_fail git send-email --dump-aliases --to=janice@example.com -1 refs/heads/accounting
+'
+
+test_sendmail_aliases () {
+ msg="$1" && shift &&
+ expect="$@" &&
+ cat >.tmp-email-aliases &&
+
+ test_expect_success $PREREQ "$msg" '
+ clean_fake_sendmail && rm -fr outdir &&
+ git format-patch -1 -o outdir &&
+ git config --replace-all sendemail.aliasesfile \
+ "$(pwd)/.tmp-email-aliases" &&
+ git config sendemail.aliasfiletype sendmail &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=alice --to=bcgrp \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ outdir/0001-*.patch \
+ 2>errors >out &&
+ for i in $expect
+ do
+ grep "^!$i!$" commandline1 || return 1
+ done
+ '
+}
+
+test_sendmail_aliases 'sendemail.aliasfiletype=sendmail' \
+ 'awol@example\.com' \
+ 'bob@example\.com' \
+ 'chloe@example\.com' \
+ 'o@example\.com' <<-\EOF
+ alice: Alice W Land <awol@example.com>
+ bob: Robert Bobbyton <bob@example.com>
+ # this is a comment
+ # this is also a comment
+ chloe: chloe@example.com
+ abgroup: alice, bob
+ bcgrp: bob, chloe, Other <o@example.com>
+ EOF
+
+test_sendmail_aliases 'sendmail aliases line folding' \
+ alice1 \
+ bob1 bob2 \
+ chuck1 chuck2 \
+ darla1 darla2 darla3 \
+ elton1 elton2 elton3 \
+ fred1 fred2 \
+ greg1 <<-\EOF
+ alice: alice1
+ bob: bob1,\
+ bob2
+ chuck: chuck1,
+ chuck2
+ darla: darla1,\
+ darla2,
+ darla3
+ elton: elton1,
+ elton2,\
+ elton3
+ fred: fred1,\
+ fred2
+ greg: greg1
+ bcgrp: bob, chuck, darla, elton, fred, greg
+ EOF
+
+test_sendmail_aliases 'sendmail aliases tolerate bogus line folding' \
+ alice1 bob1 <<-\EOF
+ alice: alice1
+ bcgrp: bob1\
+ EOF
+
+test_sendmail_aliases 'sendmail aliases empty' alice bcgrp <<-\EOF
+ EOF
+
+test_expect_success $PREREQ 'alias support in To header' '
+ clean_fake_sendmail &&
+ echo "alias sbd someone@example.org" >.mailrc &&
+ test_config sendemail.aliasesfile ".mailrc" &&
+ test_config sendemail.aliasfiletype mailrc &&
+ git format-patch --stdout -1 --to=sbd >aliased.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ aliased.patch \
+ 2>errors >out &&
+ grep "^!someone@example\.org!$" commandline1
+'
+
+test_expect_success $PREREQ 'alias support in Cc header' '
+ clean_fake_sendmail &&
+ echo "alias sbd someone@example.org" >.mailrc &&
+ test_config sendemail.aliasesfile ".mailrc" &&
+ test_config sendemail.aliasfiletype mailrc &&
+ git format-patch --stdout -1 --cc=sbd >aliased.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ aliased.patch \
+ 2>errors >out &&
+ grep "^!someone@example\.org!$" commandline1
+'
+
+test_expect_success $PREREQ 'tocmd works with aliases' '
+ clean_fake_sendmail &&
+ echo "alias sbd someone@example.org" >.mailrc &&
+ test_config sendemail.aliasesfile ".mailrc" &&
+ test_config sendemail.aliasfiletype mailrc &&
+ git format-patch --stdout -1 >tocmd.patch &&
+ echo tocmd--sbd >>tocmd.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to-cmd=./tocmd-sed \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ tocmd.patch \
+ 2>errors >out &&
+ grep "^!someone@example\.org!$" commandline1
+'
+
+test_expect_success $PREREQ 'cccmd works with aliases' '
+ clean_fake_sendmail &&
+ echo "alias sbd someone@example.org" >.mailrc &&
+ test_config sendemail.aliasesfile ".mailrc" &&
+ test_config sendemail.aliasfiletype mailrc &&
+ git format-patch --stdout -1 >cccmd.patch &&
+ echo cccmd--sbd >>cccmd.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --cc-cmd=./cccmd-sed \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ cccmd.patch \
+ 2>errors >out &&
+ grep "^!someone@example\.org!$" commandline1
+'
+
+do_xmailer_test () {
+ expected=$1 params=$2 &&
+ git format-patch -1 &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=someone@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $params \
+ 0001-*.patch \
+ 2>errors >out &&
+ { grep '^X-Mailer:' out || :; } >mailer &&
+ test_line_count = $expected mailer
+}
+
+test_expect_success $PREREQ '--[no-]xmailer without any configuration' '
+ do_xmailer_test 1 "--xmailer" &&
+ do_xmailer_test 0 "--no-xmailer"
+'
+
+test_expect_success $PREREQ '--[no-]xmailer with sendemail.xmailer=true' '
+ test_config sendemail.xmailer true &&
+ do_xmailer_test 1 "" &&
+ do_xmailer_test 0 "--no-xmailer" &&
+ do_xmailer_test 1 "--xmailer"
+'
+
+test_expect_success $PREREQ '--[no-]xmailer with sendemail.xmailer=false' '
+ test_config sendemail.xmailer false &&
+ do_xmailer_test 0 "" &&
+ do_xmailer_test 0 "--no-xmailer" &&
+ do_xmailer_test 1 "--xmailer"
+'
+
+test_expect_success $PREREQ 'setup expected-list' '
+ git send-email \
+ --dry-run \
+ --from="Example <from@example.com>" \
+ --to="To 1 <to1@example.com>" \
+ --to="to2@example.com" \
+ --to="to3@example.com" \
+ --cc="Cc 1 <cc1@example.com>" \
+ --cc="Cc2 <cc2@example.com>" \
+ --bcc="bcc1@example.com" \
+ --bcc="bcc2@example.com" \
+ 0001-add-master.patch | replace_variable_fields \
+ >expected-list
+'
+
+test_expect_success $PREREQ 'use email list in --cc --to and --bcc' '
+ git send-email \
+ --dry-run \
+ --from="Example <from@example.com>" \
+ --to="To 1 <to1@example.com>, to2@example.com" \
+ --to="to3@example.com" \
+ --cc="Cc 1 <cc1@example.com>, Cc2 <cc2@example.com>" \
+ --bcc="bcc1@example.com, bcc2@example.com" \
+ 0001-add-master.patch | replace_variable_fields \
+ >actual-list &&
+ test_cmp expected-list actual-list
+'
+
+test_expect_success $PREREQ 'aliases work with email list' '
+ echo "alias to2 to2@example.com" >.mutt &&
+ echo "alias cc1 Cc 1 <cc1@example.com>" >>.mutt &&
+ test_config sendemail.aliasesfile ".mutt" &&
+ test_config sendemail.aliasfiletype mutt &&
+ git send-email \
+ --dry-run \
+ --from="Example <from@example.com>" \
+ --to="To 1 <to1@example.com>, to2, to3@example.com" \
+ --cc="cc1, Cc2 <cc2@example.com>" \
+ --bcc="bcc1@example.com, bcc2@example.com" \
+ 0001-add-master.patch | replace_variable_fields \
+ >actual-list &&
+ test_cmp expected-list actual-list
+'
+
+test_expect_success $PREREQ 'leading and trailing whitespaces are removed' '
+ echo "alias to2 to2@example.com" >.mutt &&
+ echo "alias cc1 Cc 1 <cc1@example.com>" >>.mutt &&
+ test_config sendemail.aliasesfile ".mutt" &&
+ test_config sendemail.aliasfiletype mutt &&
+ TO1=$(echo "QTo 1 <to1@example.com>" | q_to_tab) &&
+ TO2=$(echo "QZto2" | qz_to_tab_space) &&
+ CC1=$(echo "cc1" | append_cr) &&
+ BCC1=$(echo "Q bcc1@example.com Q" | q_to_nul) &&
+ git send-email \
+ --dry-run \
+ --from=" Example <from@example.com>" \
+ --to="$TO1" \
+ --to="$TO2" \
+ --to=" to3@example.com " \
+ --cc="$CC1" \
+ --cc="Cc2 <cc2@example.com>" \
+ --bcc="$BCC1" \
+ --bcc="bcc2@example.com" \
+ 0001-add-master.patch | replace_variable_fields \
+ >actual-list &&
+ test_cmp expected-list actual-list
+'
+
test_done
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
new file mode 100755
index 0000000..8998352
--- /dev/null
+++ b/t/t9002-column.sh
@@ -0,0 +1,180 @@
+#!/bin/sh
+
+test_description='git column'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ cat >lista <<\EOF
+one
+two
+three
+four
+five
+six
+seven
+eight
+nine
+ten
+eleven
+EOF
+'
+
+test_expect_success 'never' '
+ git column --indent=Z --mode=never <lista >actual &&
+ test_cmp lista actual
+'
+
+test_expect_success 'always' '
+ cat >expected <<\EOF &&
+Zone
+Ztwo
+Zthree
+Zfour
+Zfive
+Zsix
+Zseven
+Zeight
+Znine
+Zten
+Zeleven
+EOF
+ git column --indent=Z --mode=plain <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '80 columns' '
+ cat >expected <<\EOF &&
+one two three four five six seven eight nine ten eleven
+EOF
+ COLUMNS=80 git column --mode=column <lista >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+one
+two
+three
+four
+five
+six
+seven
+eight
+nine
+ten
+eleven
+EOF
+
+test_expect_success COLUMNS_CAN_BE_1 'COLUMNS = 1' '
+ COLUMNS=1 git column --mode=column <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'width = 1' '
+ git column --mode=column --width=1 <lista >actual &&
+ test_cmp expected actual
+'
+
+COLUMNS=20
+export COLUMNS
+
+test_expect_success '20 columns' '
+ cat >expected <<\EOF &&
+one seven
+two eight
+three nine
+four ten
+five eleven
+six
+EOF
+ git column --mode=column <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '20 columns, nodense' '
+ cat >expected <<\EOF &&
+one seven
+two eight
+three nine
+four ten
+five eleven
+six
+EOF
+ git column --mode=column,nodense < lista > actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '20 columns, dense' '
+ cat >expected <<\EOF &&
+one five nine
+two six ten
+three seven eleven
+four eight
+EOF
+ git column --mode=column,dense < lista > actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '20 columns, padding 2' '
+ cat >expected <<\EOF &&
+one seven
+two eight
+three nine
+four ten
+five eleven
+six
+EOF
+ git column --mode=column --padding 2 <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '20 columns, indented' '
+ cat >expected <<\EOF &&
+ one seven
+ two eight
+ three nine
+ four ten
+ five eleven
+ six
+EOF
+ git column --mode=column --indent=" " <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '20 columns, row first' '
+ cat >expected <<\EOF &&
+one two
+three four
+five six
+seven eight
+nine ten
+eleven
+EOF
+ git column --mode=row <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '20 columns, row first, nodense' '
+ cat >expected <<\EOF &&
+one two
+three four
+five six
+seven eight
+nine ten
+eleven
+EOF
+ git column --mode=row,nodense <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '20 columns, row first, dense' '
+ cat >expected <<\EOF &&
+one two three
+four five six
+seven eight nine
+ten eleven
+EOF
+ git column --mode=row,dense <lista >actual &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t9003-help-autocorrect.sh b/t/t9003-help-autocorrect.sh
new file mode 100755
index 0000000..dfe95c9
--- /dev/null
+++ b/t/t9003-help-autocorrect.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+test_description='help.autocorrect finding a match'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ # An alias
+ git config alias.lgf "log --format=%s --first-parent" &&
+
+ # A random user-defined command
+ write_script git-distimdistim <<-EOF &&
+ echo distimdistim was called
+ EOF
+
+ PATH="$PATH:." &&
+ export PATH &&
+
+ git commit --allow-empty -m "a single log entry" &&
+
+ # Sanity check
+ git lgf >actual &&
+ echo "a single log entry" >expect &&
+ test_cmp expect actual &&
+
+ git distimdistim >actual &&
+ echo "distimdistim was called" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'autocorrect showing candidates' '
+ git config help.autocorrect 0 &&
+
+ test_must_fail git lfg 2>actual &&
+ sed -e "1,/^Did you mean this/d" actual | grep lgf &&
+
+ test_must_fail git distimdist 2>actual &&
+ sed -e "1,/^Did you mean this/d" actual | grep distimdistim
+'
+
+test_expect_success 'autocorrect running commands' '
+ git config help.autocorrect -1 &&
+
+ git lfg >actual &&
+ echo "a single log entry" >expect &&
+ test_cmp expect actual &&
+
+ git distimdist >actual &&
+ echo "distimdistim was called" >expect &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t9010-svn-fe.sh b/t/t9010-svn-fe.sh
new file mode 100755
index 0000000..6dafe7e
--- /dev/null
+++ b/t/t9010-svn-fe.sh
@@ -0,0 +1,1106 @@
+#!/bin/sh
+
+test_description='check svn dumpfile importer'
+
+. ./test-lib.sh
+
+reinit_git () {
+ if ! test_declared_prereq PIPE
+ then
+ echo >&4 "reinit_git: need to declare PIPE prerequisite"
+ return 127
+ fi
+ rm -fr .git &&
+ rm -f stream backflow &&
+ git init &&
+ mkfifo stream backflow
+}
+
+try_dump () {
+ input=$1 &&
+ maybe_fail_svnfe=${2:+test_$2} &&
+ maybe_fail_fi=${3:+test_$3} &&
+
+ {
+ $maybe_fail_svnfe test-svn-fe "$input" >stream 3<backflow &
+ } &&
+ $maybe_fail_fi git fast-import --cat-blob-fd=3 <stream 3>backflow &&
+ wait $!
+}
+
+properties () {
+ while test "$#" -ne 0
+ do
+ property="$1" &&
+ value="$2" &&
+ printf "%s\n" "K ${#property}" &&
+ printf "%s\n" "$property" &&
+ printf "%s\n" "V ${#value}" &&
+ printf "%s\n" "$value" &&
+ shift 2 ||
+ return 1
+ done
+}
+
+text_no_props () {
+ text="$1
+" &&
+ printf "%s\n" "Prop-content-length: 10" &&
+ printf "%s\n" "Text-content-length: ${#text}" &&
+ printf "%s\n" "Content-length: $((${#text} + 10))" &&
+ printf "%s\n" "" "PROPS-END" &&
+ printf "%s\n" "$text"
+}
+
+>empty
+
+test_expect_success PIPE 'empty dump' '
+ reinit_git &&
+ echo "SVN-fs-dump-format-version: 2" >input &&
+ try_dump input
+'
+
+test_expect_success PIPE 'v4 dumps not supported' '
+ reinit_git &&
+ echo "SVN-fs-dump-format-version: 4" >v4.dump &&
+ try_dump v4.dump must_fail
+'
+
+test_expect_failure PIPE 'empty revision' '
+ reinit_git &&
+ printf "rev <nobody, nobody@local>: %s\n" "" "" >expect &&
+ cat >emptyrev.dump <<-\EOF &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ Prop-content-length: 0
+ Content-length: 0
+
+ Revision-number: 2
+ Prop-content-length: 0
+ Content-length: 0
+
+ EOF
+ try_dump emptyrev.dump &&
+ git log -p --format="rev <%an, %ae>: %s" HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success PIPE 'empty properties' '
+ reinit_git &&
+ printf "rev <nobody, nobody@local>: %s\n" "" "" >expect &&
+ cat >emptyprop.dump <<-\EOF &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Revision-number: 2
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+ EOF
+ try_dump emptyprop.dump &&
+ git log -p --format="rev <%an, %ae>: %s" HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success PIPE 'author name and commit message' '
+ reinit_git &&
+ echo "<author@example.com, author@example.com@local>" >expect.author &&
+ cat >message <<-\EOF &&
+ A concise summary of the change
+
+ A detailed description of the change, why it is needed, what
+ was broken and why applying this is the best course of action.
+
+ * file.c
+ Details pertaining to an individual file.
+ EOF
+ {
+ properties \
+ svn:author author@example.com \
+ svn:log "$(cat message)" &&
+ echo PROPS-END
+ } >props &&
+ {
+ echo "SVN-fs-dump-format-version: 3" &&
+ echo &&
+ echo "Revision-number: 1" &&
+ echo Prop-content-length: $(wc -c <props) &&
+ echo Content-length: $(wc -c <props) &&
+ echo &&
+ cat props
+ } >log.dump &&
+ try_dump log.dump &&
+ git log -p --format="%B" HEAD >actual.log &&
+ git log --format="<%an, %ae>" >actual.author &&
+ test_cmp message actual.log &&
+ test_cmp expect.author actual.author
+'
+
+test_expect_success PIPE 'unsupported properties are ignored' '
+ reinit_git &&
+ echo author >expect &&
+ cat >extraprop.dump <<-\EOF &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ Prop-content-length: 56
+ Content-length: 56
+
+ K 8
+ nonsense
+ V 1
+ y
+ K 10
+ svn:author
+ V 6
+ author
+ PROPS-END
+ EOF
+ try_dump extraprop.dump &&
+ git log -p --format=%an HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure PIPE 'timestamp and empty file' '
+ echo author@example.com >expect.author &&
+ echo 1999-01-01 >expect.date &&
+ echo file >expect.files &&
+ reinit_git &&
+ {
+ properties \
+ svn:author author@example.com \
+ svn:date "1999-01-01T00:01:002.000000Z" \
+ svn:log "add empty file" &&
+ echo PROPS-END
+ } >props &&
+ {
+ cat <<-EOF &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ EOF
+ echo Prop-content-length: $(wc -c <props) &&
+ echo Content-length: $(wc -c <props) &&
+ echo &&
+ cat props &&
+ cat <<-\EOF
+
+ Node-path: empty-file
+ Node-kind: file
+ Node-action: add
+ Content-length: 0
+
+ EOF
+ } >emptyfile.dump &&
+ try_dump emptyfile.dump &&
+ git log --format=%an HEAD >actual.author &&
+ git log --date=short --format=%ad HEAD >actual.date &&
+ git ls-tree -r --name-only HEAD >actual.files &&
+ test_cmp expect.author actual.author &&
+ test_cmp expect.date actual.date &&
+ test_cmp expect.files actual.files &&
+ git checkout HEAD empty-file &&
+ test_cmp empty file
+'
+
+test_expect_success PIPE 'directory with files' '
+ reinit_git &&
+ printf "%s\n" directory/file1 directory/file2 >expect.files &&
+ echo hi >hi &&
+ echo hello >hello &&
+ {
+ properties \
+ svn:author author@example.com \
+ svn:date "1999-02-01T00:01:002.000000Z" \
+ svn:log "add directory with some files in it" &&
+ echo PROPS-END
+ } >props &&
+ {
+ cat <<-EOF &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ EOF
+ echo Prop-content-length: $(wc -c <props) &&
+ echo Content-length: $(wc -c <props) &&
+ echo &&
+ cat props &&
+ cat <<-\EOF &&
+
+ Node-path: directory
+ Node-kind: dir
+ Node-action: add
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: directory/file1
+ Node-kind: file
+ Node-action: add
+ EOF
+ text_no_props hello &&
+ cat <<-\EOF &&
+ Node-path: directory/file2
+ Node-kind: file
+ Node-action: add
+ EOF
+ text_no_props hi
+ } >directory.dump &&
+ try_dump directory.dump &&
+
+ git ls-tree -r --name-only HEAD >actual.files &&
+ git checkout HEAD directory &&
+ test_cmp expect.files actual.files &&
+ test_cmp hello directory/file1 &&
+ test_cmp hi directory/file2
+'
+
+test_expect_success PIPE 'branch name with backslash' '
+ reinit_git &&
+ sort <<-\EOF >expect.branch-files &&
+ trunk/file1
+ trunk/file2
+ "branches/UpdateFOPto094\\/file1"
+ "branches/UpdateFOPto094\\/file2"
+ EOF
+
+ echo hi >hi &&
+ echo hello >hello &&
+ {
+ properties \
+ svn:author author@example.com \
+ svn:date "1999-02-02T00:01:02.000000Z" \
+ svn:log "add directory with some files in it" &&
+ echo PROPS-END
+ } >props.setup &&
+ {
+ properties \
+ svn:author brancher@example.com \
+ svn:date "2007-12-06T21:38:34.000000Z" \
+ svn:log "Updating fop to .94 and adjust fo-stylesheets" &&
+ echo PROPS-END
+ } >props.branch &&
+ {
+ cat <<-EOF &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ EOF
+ echo Prop-content-length: $(wc -c <props.setup) &&
+ echo Content-length: $(wc -c <props.setup) &&
+ echo &&
+ cat props.setup &&
+ cat <<-\EOF &&
+
+ Node-path: trunk
+ Node-kind: dir
+ Node-action: add
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: branches
+ Node-kind: dir
+ Node-action: add
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: trunk/file1
+ Node-kind: file
+ Node-action: add
+ EOF
+ text_no_props hello &&
+ cat <<-\EOF &&
+ Node-path: trunk/file2
+ Node-kind: file
+ Node-action: add
+ EOF
+ text_no_props hi &&
+ cat <<-\EOF &&
+
+ Revision-number: 2
+ EOF
+ echo Prop-content-length: $(wc -c <props.branch) &&
+ echo Content-length: $(wc -c <props.branch) &&
+ echo &&
+ cat props.branch &&
+ cat <<-\EOF
+
+ Node-path: branches/UpdateFOPto094\
+ Node-kind: dir
+ Node-action: add
+ Node-copyfrom-rev: 1
+ Node-copyfrom-path: trunk
+
+ Node-kind: dir
+ Node-action: add
+ Prop-content-length: 34
+ Content-length: 34
+
+ K 13
+ svn:mergeinfo
+ V 0
+
+ PROPS-END
+ EOF
+ } >branch.dump &&
+ try_dump branch.dump &&
+
+ git ls-tree -r --name-only HEAD |
+ sort >actual.branch-files &&
+ test_cmp expect.branch-files actual.branch-files
+'
+
+test_expect_success PIPE 'node without action' '
+ reinit_git &&
+ cat >inaction.dump <<-\EOF &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: directory
+ Node-kind: dir
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+ EOF
+ try_dump inaction.dump must_fail
+'
+
+test_expect_success PIPE 'action: add node without text' '
+ reinit_git &&
+ cat >textless.dump <<-\EOF &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: textless
+ Node-kind: file
+ Node-action: add
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+ EOF
+ try_dump textless.dump must_fail
+'
+
+test_expect_failure PIPE 'change file mode but keep old content' '
+ reinit_git &&
+ cat >expect <<-\EOF &&
+ OBJID
+ :120000 100644 OBJID OBJID T greeting
+ OBJID
+ :100644 120000 OBJID OBJID T greeting
+ OBJID
+ :000000 100644 OBJID OBJID A greeting
+ EOF
+ echo "link hello" >expect.blob &&
+ echo hello >hello &&
+ cat >filemode.dump <<-\EOF &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: greeting
+ Node-kind: file
+ Node-action: add
+ Prop-content-length: 10
+ Text-content-length: 11
+ Content-length: 21
+
+ PROPS-END
+ link hello
+
+ Revision-number: 2
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: greeting
+ Node-kind: file
+ Node-action: change
+ Prop-content-length: 33
+ Content-length: 33
+
+ K 11
+ svn:special
+ V 1
+ *
+ PROPS-END
+
+ Revision-number: 3
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: greeting
+ Node-kind: file
+ Node-action: change
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+ EOF
+ try_dump filemode.dump &&
+ {
+ git rev-list HEAD |
+ git diff-tree --root --stdin |
+ sed "s/$_x40/OBJID/g"
+ } >actual &&
+ git show HEAD:greeting >actual.blob &&
+ git show HEAD^:greeting >actual.target &&
+ test_cmp expect actual &&
+ test_cmp expect.blob actual.blob &&
+ test_cmp hello actual.target
+'
+
+test_expect_success PIPE 'NUL in property value' '
+ reinit_git &&
+ echo "commit message" >expect.message &&
+ {
+ properties \
+ unimportant "something with a NUL (Q)" \
+ svn:log "commit message"&&
+ echo PROPS-END
+ } |
+ q_to_nul >props &&
+ {
+ cat <<-\EOF &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ EOF
+ echo Prop-content-length: $(wc -c <props) &&
+ echo Content-length: $(wc -c <props) &&
+ echo &&
+ cat props
+ } >nulprop.dump &&
+ try_dump nulprop.dump &&
+ git diff-tree --always -s --format=%s HEAD >actual.message &&
+ test_cmp expect.message actual.message
+'
+
+test_expect_success PIPE 'NUL in log message, file content, and property name' '
+ # Caveat: svnadmin 1.6.16 (r1073529) truncates at \0 in the
+ # svn:specialQnotreally example.
+ reinit_git &&
+ cat >expect <<-\EOF &&
+ OBJID
+ :100644 100644 OBJID OBJID M greeting
+ OBJID
+ :000000 100644 OBJID OBJID A greeting
+ EOF
+ printf "\n%s\n" "something with an ASCII NUL (Q)" >expect.message &&
+ printf "%s\n" "helQo" >expect.hello1 &&
+ printf "%s\n" "link hello" >expect.hello2 &&
+ {
+ properties svn:log "something with an ASCII NUL (Q)" &&
+ echo PROPS-END
+ } |
+ q_to_nul >props &&
+ {
+ q_to_nul <<-\EOF &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: greeting
+ Node-kind: file
+ Node-action: add
+ Prop-content-length: 10
+ Text-content-length: 6
+ Content-length: 16
+
+ PROPS-END
+ helQo
+
+ Revision-number: 2
+ EOF
+ echo Prop-content-length: $(wc -c <props) &&
+ echo Content-length: $(wc -c <props) &&
+ echo &&
+ cat props &&
+ q_to_nul <<-\EOF
+
+ Node-path: greeting
+ Node-kind: file
+ Node-action: change
+ Prop-content-length: 43
+ Text-content-length: 11
+ Content-length: 54
+
+ K 21
+ svn:specialQnotreally
+ V 1
+ *
+ PROPS-END
+ link hello
+ EOF
+ } >8bitclean.dump &&
+ try_dump 8bitclean.dump &&
+ {
+ git rev-list HEAD |
+ git diff-tree --root --stdin |
+ sed "s/$_x40/OBJID/g"
+ } >actual &&
+ {
+ git cat-file commit HEAD | nul_to_q &&
+ echo
+ } |
+ sed -ne "/^\$/,\$ p" >actual.message &&
+ git cat-file blob HEAD^:greeting | nul_to_q >actual.hello1 &&
+ git cat-file blob HEAD:greeting | nul_to_q >actual.hello2 &&
+ test_cmp expect actual &&
+ test_cmp expect.message actual.message &&
+ test_cmp expect.hello1 actual.hello1 &&
+ test_cmp expect.hello2 actual.hello2
+'
+
+test_expect_success PIPE 'change file mode and reiterate content' '
+ reinit_git &&
+ cat >expect <<-\EOF &&
+ OBJID
+ :120000 100644 OBJID OBJID T greeting
+ OBJID
+ :100644 120000 OBJID OBJID T greeting
+ OBJID
+ :000000 100644 OBJID OBJID A greeting
+ EOF
+ echo "link hello" >expect.blob &&
+ echo hello >hello &&
+ cat >filemode2.dump <<-\EOF &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: greeting
+ Node-kind: file
+ Node-action: add
+ Prop-content-length: 10
+ Text-content-length: 11
+ Content-length: 21
+
+ PROPS-END
+ link hello
+
+ Revision-number: 2
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: greeting
+ Node-kind: file
+ Node-action: change
+ Prop-content-length: 33
+ Text-content-length: 11
+ Content-length: 44
+
+ K 11
+ svn:special
+ V 1
+ *
+ PROPS-END
+ link hello
+
+ Revision-number: 3
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: greeting
+ Node-kind: file
+ Node-action: change
+ Prop-content-length: 10
+ Text-content-length: 11
+ Content-length: 21
+
+ PROPS-END
+ link hello
+ EOF
+ try_dump filemode2.dump &&
+ {
+ git rev-list HEAD |
+ git diff-tree --root --stdin |
+ sed "s/$_x40/OBJID/g"
+ } >actual &&
+ git show HEAD:greeting >actual.blob &&
+ git show HEAD^:greeting >actual.target &&
+ test_cmp expect actual &&
+ test_cmp expect.blob actual.blob &&
+ test_cmp hello actual.target
+'
+
+test_expect_success PIPE 'deltas supported' '
+ reinit_git &&
+ {
+ # (old) h + (inline) ello + (old) \n
+ printf "SVNQ%b%b%s" "Q\003\006\005\004" "\001Q\0204\001\002" "ello" |
+ q_to_nul
+ } >delta &&
+ {
+ properties \
+ svn:author author@example.com \
+ svn:date "1999-01-05T00:01:002.000000Z" \
+ svn:log "add greeting" &&
+ echo PROPS-END
+ } >props &&
+ {
+ properties \
+ svn:author author@example.com \
+ svn:date "1999-01-06T00:01:002.000000Z" \
+ svn:log "change it" &&
+ echo PROPS-END
+ } >props2 &&
+ {
+ echo SVN-fs-dump-format-version: 3 &&
+ echo &&
+ echo Revision-number: 1 &&
+ echo Prop-content-length: $(wc -c <props) &&
+ echo Content-length: $(wc -c <props) &&
+ echo &&
+ cat props &&
+ cat <<-\EOF &&
+
+ Node-path: hello
+ Node-kind: file
+ Node-action: add
+ Prop-content-length: 10
+ Text-content-length: 3
+ Content-length: 13
+
+ PROPS-END
+ hi
+
+ EOF
+ echo Revision-number: 2 &&
+ echo Prop-content-length: $(wc -c <props2) &&
+ echo Content-length: $(wc -c <props2) &&
+ echo &&
+ cat props2 &&
+ cat <<-\EOF &&
+
+ Node-path: hello
+ Node-kind: file
+ Node-action: change
+ Text-delta: true
+ Prop-content-length: 10
+ EOF
+ echo Text-content-length: $(wc -c <delta) &&
+ echo Content-length: $((10 + $(wc -c <delta))) &&
+ echo &&
+ echo PROPS-END &&
+ cat delta
+ } >delta.dump &&
+ try_dump delta.dump
+'
+
+test_expect_success PIPE 'property deltas supported' '
+ reinit_git &&
+ cat >expect <<-\EOF &&
+ OBJID
+ :100755 100644 OBJID OBJID M script.sh
+ EOF
+ {
+ properties \
+ svn:author author@example.com \
+ svn:date "1999-03-06T00:01:002.000000Z" \
+ svn:log "make an executable, or chmod -x it" &&
+ echo PROPS-END
+ } >revprops &&
+ {
+ echo SVN-fs-dump-format-version: 3 &&
+ echo &&
+ echo Revision-number: 1 &&
+ echo Prop-content-length: $(wc -c <revprops) &&
+ echo Content-length: $(wc -c <revprops) &&
+ echo &&
+ cat revprops &&
+ echo &&
+ cat <<-\EOF &&
+ Node-path: script.sh
+ Node-kind: file
+ Node-action: add
+ Text-content-length: 0
+ Prop-content-length: 39
+ Content-length: 39
+
+ K 14
+ svn:executable
+ V 4
+ true
+ PROPS-END
+
+ EOF
+ echo Revision-number: 2 &&
+ echo Prop-content-length: $(wc -c <revprops) &&
+ echo Content-length: $(wc -c <revprops) &&
+ echo &&
+ cat revprops &&
+ echo &&
+ cat <<-\EOF
+ Node-path: script.sh
+ Node-kind: file
+ Node-action: change
+ Prop-delta: true
+ Prop-content-length: 30
+ Content-length: 30
+
+ D 14
+ svn:executable
+ PROPS-END
+ EOF
+ } >propdelta.dump &&
+ try_dump propdelta.dump &&
+ {
+ git rev-list HEAD |
+ git diff-tree --stdin |
+ sed "s/$_x40/OBJID/g"
+ } >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success PIPE 'properties on /' '
+ reinit_git &&
+ cat <<-\EOF >expect &&
+ OBJID
+ OBJID
+ :000000 100644 OBJID OBJID A greeting
+ EOF
+ sed -e "s/X$//" <<-\EOF >changeroot.dump &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: greeting
+ Node-kind: file
+ Node-action: add
+ Text-content-length: 0
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Revision-number: 2
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: X
+ Node-kind: dir
+ Node-action: change
+ Prop-delta: true
+ Prop-content-length: 43
+ Content-length: 43
+
+ K 10
+ svn:ignore
+ V 11
+ build-area
+
+ PROPS-END
+ EOF
+ try_dump changeroot.dump &&
+ {
+ git rev-list HEAD |
+ git diff-tree --root --always --stdin |
+ sed "s/$_x40/OBJID/g"
+ } >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success PIPE 'deltas for typechange' '
+ reinit_git &&
+ cat >expect <<-\EOF &&
+ OBJID
+ :120000 100644 OBJID OBJID T test-file
+ OBJID
+ :100755 120000 OBJID OBJID T test-file
+ OBJID
+ :000000 100755 OBJID OBJID A test-file
+ EOF
+ cat >deleteprop.dump <<-\EOF &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: test-file
+ Node-kind: file
+ Node-action: add
+ Prop-delta: true
+ Prop-content-length: 35
+ Text-content-length: 17
+ Content-length: 52
+
+ K 14
+ svn:executable
+ V 0
+
+ PROPS-END
+ link testing 123
+
+ Revision-number: 2
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: test-file
+ Node-kind: file
+ Node-action: change
+ Prop-delta: true
+ Prop-content-length: 53
+ Text-content-length: 17
+ Content-length: 70
+
+ K 11
+ svn:special
+ V 1
+ *
+ D 14
+ svn:executable
+ PROPS-END
+ link testing 231
+
+ Revision-number: 3
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: test-file
+ Node-kind: file
+ Node-action: change
+ Prop-delta: true
+ Prop-content-length: 27
+ Text-content-length: 17
+ Content-length: 44
+
+ D 11
+ svn:special
+ PROPS-END
+ link testing 321
+ EOF
+ try_dump deleteprop.dump &&
+ {
+ git rev-list HEAD |
+ git diff-tree --root --stdin |
+ sed "s/$_x40/OBJID/g"
+ } >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success PIPE 'deltas need not consume the whole preimage' '
+ reinit_git &&
+ cat >expect <<-\EOF &&
+ OBJID
+ :120000 100644 OBJID OBJID T postimage
+ OBJID
+ :100644 120000 OBJID OBJID T postimage
+ OBJID
+ :000000 100644 OBJID OBJID A postimage
+ EOF
+ echo "first preimage" >expect.1 &&
+ printf target >expect.2 &&
+ printf lnk >expect.3 &&
+ {
+ printf "SVNQ%b%b%b" "QQ\017\001\017" "\0217" "first preimage\n" |
+ q_to_nul
+ } >delta.1 &&
+ {
+ properties svn:special "*" &&
+ echo PROPS-END
+ } >symlink.props &&
+ {
+ printf "SVNQ%b%b%b" "Q\002\013\004\012" "\0201\001\001\0211" "lnk target" |
+ q_to_nul
+ } >delta.2 &&
+ {
+ printf "SVNQ%b%b" "Q\004\003\004Q" "\001Q\002\002" |
+ q_to_nul
+ } >delta.3 &&
+ {
+ cat <<-\EOF &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: postimage
+ Node-kind: file
+ Node-action: add
+ Text-delta: true
+ Prop-content-length: 10
+ EOF
+ echo Text-content-length: $(wc -c <delta.1) &&
+ echo Content-length: $((10 + $(wc -c <delta.1))) &&
+ echo &&
+ echo PROPS-END &&
+ cat delta.1 &&
+ cat <<-\EOF &&
+
+ Revision-number: 2
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: postimage
+ Node-kind: file
+ Node-action: change
+ Text-delta: true
+ EOF
+ echo Prop-content-length: $(wc -c <symlink.props) &&
+ echo Text-content-length: $(wc -c <delta.2) &&
+ echo Content-length: $(($(wc -c <symlink.props) + $(wc -c <delta.2))) &&
+ echo &&
+ cat symlink.props &&
+ cat delta.2 &&
+ cat <<-\EOF &&
+
+ Revision-number: 3
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: postimage
+ Node-kind: file
+ Node-action: change
+ Text-delta: true
+ Prop-content-length: 10
+ EOF
+ echo Text-content-length: $(wc -c <delta.3) &&
+ echo Content-length: $((10 + $(wc -c <delta.3))) &&
+ echo &&
+ echo PROPS-END &&
+ cat delta.3 &&
+ echo
+ } >deltapartial.dump &&
+ try_dump deltapartial.dump &&
+ {
+ git rev-list HEAD |
+ git diff-tree --root --stdin |
+ sed "s/$_x40/OBJID/g"
+ } >actual &&
+ test_cmp expect actual &&
+ git show HEAD:postimage >actual.3 &&
+ git show HEAD^:postimage >actual.2 &&
+ git show HEAD^^:postimage >actual.1 &&
+ test_cmp expect.1 actual.1 &&
+ test_cmp expect.2 actual.2 &&
+ test_cmp expect.3 actual.3
+'
+
+test_expect_success PIPE 'no hang for delta trying to read past end of preimage' '
+ reinit_git &&
+ {
+ # COPY 1
+ printf "SVNQ%b%b" "Q\001\001\002Q" "\001Q" |
+ q_to_nul
+ } >greedy.delta &&
+ {
+ cat <<-\EOF &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+
+ Node-path: bootstrap
+ Node-kind: file
+ Node-action: add
+ Text-delta: true
+ Prop-content-length: 10
+ EOF
+ echo Text-content-length: $(wc -c <greedy.delta) &&
+ echo Content-length: $((10 + $(wc -c <greedy.delta))) &&
+ echo &&
+ echo PROPS-END &&
+ cat greedy.delta &&
+ echo
+ } >greedydelta.dump &&
+ try_dump greedydelta.dump must_fail might_fail
+'
+
+test_expect_success 'set up svn repo' '
+ svnconf=$PWD/svnconf &&
+ mkdir -p "$svnconf" &&
+
+ if
+ svnadmin -h >/dev/null 2>&1 &&
+ svnadmin create simple-svn &&
+ svnadmin load simple-svn <"$TEST_DIRECTORY/t9135/svn.dump" &&
+ svn export --config-dir "$svnconf" "file://$PWD/simple-svn" simple-svnco
+ then
+ test_set_prereq SVNREPO
+ fi
+'
+
+test_expect_success SVNREPO,PIPE 't9135/svn.dump' '
+ mkdir -p simple-git &&
+ (
+ cd simple-git &&
+ reinit_git &&
+ try_dump "$TEST_DIRECTORY/t9135/svn.dump"
+ ) &&
+ (
+ cd simple-svnco &&
+ git init &&
+ git add . &&
+ git fetch ../simple-git master &&
+ git diff --exit-code FETCH_HEAD
+ )
+'
+
+test_done
diff --git a/t/t9011-svn-da.sh b/t/t9011-svn-da.sh
new file mode 100755
index 0000000..b38d16f
--- /dev/null
+++ b/t/t9011-svn-da.sh
@@ -0,0 +1,248 @@
+#!/bin/sh
+
+test_description='test parsing of svndiff0 files
+
+Using the "test-svn-fe -d" helper, check that svn-fe correctly
+interprets deltas using various facilities (some from the spec,
+some only learned from practice).
+'
+. ./test-lib.sh
+
+>empty
+printf foo >preimage
+
+test_expect_success 'reject empty delta' '
+ test_must_fail test-svn-fe -d preimage empty 0
+'
+
+test_expect_success 'delta can empty file' '
+ printf "SVNQ" | q_to_nul >clear.delta &&
+ test-svn-fe -d preimage clear.delta 4 >actual &&
+ test_cmp empty actual
+'
+
+test_expect_success 'reject svndiff2' '
+ printf "SVN\002" >bad.filetype &&
+ test_must_fail test-svn-fe -d preimage bad.filetype 4
+'
+
+test_expect_success 'one-window empty delta' '
+ printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow &&
+ test-svn-fe -d preimage clear.onewindow 9 >actual &&
+ test_cmp empty actual
+'
+
+test_expect_success 'reject incomplete window header' '
+ printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow &&
+ printf "SVNQ%s" "QQ" | q_to_nul >clear.partialwindow &&
+ test_must_fail test-svn-fe -d preimage clear.onewindow 6 &&
+ test_must_fail test-svn-fe -d preimage clear.partialwindow 6
+'
+
+test_expect_success 'reject declared delta longer than actual delta' '
+ printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow &&
+ printf "SVNQ%s" "QQ" | q_to_nul >clear.partialwindow &&
+ test_must_fail test-svn-fe -d preimage clear.onewindow 14 &&
+ test_must_fail test-svn-fe -d preimage clear.partialwindow 9
+'
+
+test_expect_success 'two-window empty delta' '
+ printf "SVNQ%s%s" "QQQQQ" "QQQQQ" | q_to_nul >clear.twowindow &&
+ test-svn-fe -d preimage clear.twowindow 14 >actual &&
+ test_must_fail test-svn-fe -d preimage clear.twowindow 13 &&
+ test_cmp empty actual
+'
+
+test_expect_success 'noisy zeroes' '
+ printf "SVNQ%s" \
+ "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQ" |
+ tr R "\200" |
+ q_to_nul >clear.noisy &&
+ len=$(wc -c <clear.noisy) &&
+ test-svn-fe -d preimage clear.noisy $len &&
+ test_cmp empty actual
+'
+
+test_expect_success 'reject variable-length int in magic' '
+ printf "SVNRQ" | tr R "\200" | q_to_nul >clear.badmagic &&
+ test_must_fail test-svn-fe -d preimage clear.badmagic 5
+'
+
+test_expect_success 'reject truncated integer' '
+ printf "SVNQ%s%s" "QQQQQ" "QQQQRRQ" |
+ tr R "\200" |
+ q_to_nul >clear.fullint &&
+ printf "SVNQ%s%s" "QQQQQ" "QQQQRR" |
+ tr RT "\201" |
+ q_to_nul >clear.partialint &&
+ test_must_fail test-svn-fe -d preimage clear.fullint 15 &&
+ test-svn-fe -d preimage clear.fullint 16 &&
+ test_must_fail test-svn-fe -d preimage clear.partialint 15
+'
+
+test_expect_success 'nonempty (but unused) preimage view' '
+ printf "SVNQ%b" "Q\003QQQ" | q_to_nul >clear.readpreimage &&
+ test-svn-fe -d preimage clear.readpreimage 9 >actual &&
+ test_cmp empty actual
+'
+
+test_expect_success 'preimage view: right endpoint cannot backtrack' '
+ printf "SVNQ%b%b" "Q\003QQQ" "Q\002QQQ" |
+ q_to_nul >clear.backtrack &&
+ test_must_fail test-svn-fe -d preimage clear.backtrack 14
+'
+
+test_expect_success 'preimage view: left endpoint can advance' '
+ printf "SVNQ%b%b" "Q\003QQQ" "\001\002QQQ" |
+ q_to_nul >clear.preshrink &&
+ printf "SVNQ%b%b" "Q\003QQQ" "\001\001QQQ" |
+ q_to_nul >clear.shrinkbacktrack &&
+ test-svn-fe -d preimage clear.preshrink 14 >actual &&
+ test_must_fail test-svn-fe -d preimage clear.shrinkbacktrack 14 &&
+ test_cmp empty actual
+'
+
+test_expect_success 'preimage view: offsets compared by value' '
+ printf "SVNQ%b%b" "\001\001QQQ" "\0200Q\003QQQ" |
+ q_to_nul >clear.noisybacktrack &&
+ printf "SVNQ%b%b" "\001\001QQQ" "\0200\001\002QQQ" |
+ q_to_nul >clear.noisyadvance &&
+ test_must_fail test-svn-fe -d preimage clear.noisybacktrack 15 &&
+ test-svn-fe -d preimage clear.noisyadvance 15 &&
+ test_cmp empty actual
+'
+
+test_expect_success 'preimage view: reject truncated preimage' '
+ printf "SVNQ%b" "\010QQQQ" | q_to_nul >clear.lateemptyread &&
+ printf "SVNQ%b" "\010\001QQQ" | q_to_nul >clear.latenonemptyread &&
+ printf "SVNQ%b" "\001\010QQQ" | q_to_nul >clear.longread &&
+ test_must_fail test-svn-fe -d preimage clear.lateemptyread 9 &&
+ test_must_fail test-svn-fe -d preimage clear.latenonemptyread 9 &&
+ test_must_fail test-svn-fe -d preimage clear.longread 9
+'
+
+test_expect_success 'forbid unconsumed inline data' '
+ printf "SVNQ%b%s%b%s" "QQQQ\003" "bar" "QQQQ\001" "x" |
+ q_to_nul >inline.clear &&
+ test_must_fail test-svn-fe -d preimage inline.clear 18 >actual
+'
+
+test_expect_success 'reject truncated inline data' '
+ printf "SVNQ%b%s" "QQQQ\003" "b" | q_to_nul >inline.trunc &&
+ test_must_fail test-svn-fe -d preimage inline.trunc 10
+'
+
+test_expect_success 'reject truncated inline data (after instruction section)' '
+ printf "SVNQ%b%b%s" "QQ\001\001\003" "\0201" "b" | q_to_nul >insn.trunc &&
+ test_must_fail test-svn-fe -d preimage insn.trunc 11
+'
+
+test_expect_success 'copyfrom_data' '
+ echo hi >expect &&
+ printf "SVNQ%b%b%b" "QQ\003\001\003" "\0203" "hi\n" | q_to_nul >copydat &&
+ test-svn-fe -d preimage copydat 13 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'multiple copyfrom_data' '
+ echo hi >expect &&
+ printf "SVNQ%b%b%b%b%b" "QQ\003\002\003" "\0201\0202" "hi\n" \
+ "QQQ\002Q" "\0200Q" | q_to_nul >copy.multi &&
+ len=$(wc -c <copy.multi) &&
+ test-svn-fe -d preimage copy.multi $len >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'incomplete multiple insn' '
+ printf "SVNQ%b%b%b" "QQ\003\002\003" "\0203\0200" "hi\n" |
+ q_to_nul >copy.partial &&
+ len=$(wc -c <copy.partial) &&
+ test_must_fail test-svn-fe -d preimage copy.partial $len
+'
+
+test_expect_success 'catch attempt to copy missing data' '
+ printf "SVNQ%b%b%s%b%s" "QQ\002\002\001" "\0201\0201" "X" \
+ "QQQQ\002" "YZ" |
+ q_to_nul >copy.incomplete &&
+ len=$(wc -c <copy.incomplete) &&
+ test_must_fail test-svn-fe -d preimage copy.incomplete $len
+'
+
+test_expect_success 'copyfrom target to repeat data' '
+ printf foofoo >expect &&
+ printf "SVNQ%b%b%s" "QQ\006\004\003" "\0203\0100\003Q" "foo" |
+ q_to_nul >copytarget.repeat &&
+ len=$(wc -c <copytarget.repeat) &&
+ test-svn-fe -d preimage copytarget.repeat $len >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'copyfrom target out of order' '
+ printf foooof >expect &&
+ printf "SVNQ%b%b%s" \
+ "QQ\006\007\003" "\0203\0101\002\0101\001\0101Q" "foo" |
+ q_to_nul >copytarget.reverse &&
+ len=$(wc -c <copytarget.reverse) &&
+ test-svn-fe -d preimage copytarget.reverse $len >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'catch copyfrom future' '
+ printf "SVNQ%b%b%s" "QQ\004\004\003" "\0202\0101\002\0201" "XYZ" |
+ q_to_nul >copytarget.infuture &&
+ len=$(wc -c <copytarget.infuture) &&
+ test_must_fail test-svn-fe -d preimage copytarget.infuture $len
+'
+
+test_expect_success 'copy to sustain' '
+ printf XYXYXYXYXYXZ >expect &&
+ printf "SVNQ%b%b%s" "QQ\014\004\003" "\0202\0111Q\0201" "XYZ" |
+ q_to_nul >copytarget.sustain &&
+ len=$(wc -c <copytarget.sustain) &&
+ test-svn-fe -d preimage copytarget.sustain $len >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'catch copy that overflows' '
+ printf "SVNQ%b%b%s" "QQ\003\003\001" "\0201\0177Q" X |
+ q_to_nul >copytarget.overflow &&
+ len=$(wc -c <copytarget.overflow) &&
+ test_must_fail test-svn-fe -d preimage copytarget.overflow $len
+'
+
+test_expect_success 'copyfrom source' '
+ printf foo >expect &&
+ printf "SVNQ%b%b" "Q\003\003\002Q" "\003Q" | q_to_nul >copysource.all &&
+ test-svn-fe -d preimage copysource.all 11 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'copy backwards' '
+ printf oof >expect &&
+ printf "SVNQ%b%b" "Q\003\003\006Q" "\001\002\001\001\001Q" |
+ q_to_nul >copysource.rev &&
+ test-svn-fe -d preimage copysource.rev 15 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'offsets are relative to window' '
+ printf fo >expect &&
+ printf "SVNQ%b%b%b%b" "Q\003\001\002Q" "\001Q" \
+ "\002\001\001\002Q" "\001Q" |
+ q_to_nul >copysource.two &&
+ test-svn-fe -d preimage copysource.two 18 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'example from notes/svndiff' '
+ printf aaaaccccdddddddd >expect &&
+ printf aaaabbbbcccc >source &&
+ printf "SVNQ%b%b%s" "Q\014\020\007\001" \
+ "\004Q\004\010\0201\0107\010" d |
+ q_to_nul >delta.example &&
+ len=$(wc -c <delta.example) &&
+ test-svn-fe -d source delta.example $len >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t9020-remote-svn.sh b/t/t9020-remote-svn.sh
new file mode 100755
index 0000000..4d81ba1
--- /dev/null
+++ b/t/t9020-remote-svn.sh
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+test_description='tests remote-svn'
+
+. ./test-lib.sh
+
+MARKSPATH=.git/info/fast-import/remote-svn
+
+if ! test_have_prereq PYTHON
+then
+ skip_all='skipping remote-svn tests, python not available'
+ test_done
+fi
+
+# Override svnrdump with our simulator
+PATH="$HOME:$PATH"
+export PATH PYTHON_PATH GIT_BUILD_DIR
+
+write_script "$HOME/svnrdump" <<\EOF
+exec "$PYTHON_PATH" "$GIT_BUILD_DIR/contrib/svn-fe/svnrdump_sim.py" "$@"
+EOF
+
+init_git () {
+ rm -fr .git &&
+ git init &&
+ #git remote add svnsim testsvn::sim:///$TEST_DIRECTORY/t9020/example.svnrdump
+ # let's reuse an existing dump file!?
+ git remote add svnsim testsvn::sim://$TEST_DIRECTORY/t9154/svn.dump
+ git remote add svnfile testsvn::file://$TEST_DIRECTORY/t9154/svn.dump
+}
+
+if test -e "$GIT_BUILD_DIR/git-remote-testsvn"
+then
+ test_set_prereq REMOTE_SVN
+fi
+
+test_debug '
+ git --version
+ type git
+ type svnrdump
+'
+
+test_expect_success REMOTE_SVN 'simple fetch' '
+ init_git &&
+ git fetch svnsim &&
+ test_cmp .git/refs/svn/svnsim/master .git/refs/remotes/svnsim/master &&
+ cp .git/refs/remotes/svnsim/master master.good
+'
+
+test_debug '
+ cat .git/refs/svn/svnsim/master
+ cat .git/refs/remotes/svnsim/master
+'
+
+test_expect_success REMOTE_SVN 'repeated fetch, nothing shall change' '
+ git fetch svnsim &&
+ test_cmp master.good .git/refs/remotes/svnsim/master
+'
+
+test_expect_success REMOTE_SVN 'fetch from a file:// url gives the same result' '
+ git fetch svnfile
+'
+
+test_expect_failure REMOTE_SVN 'the sha1 differ because the git-svn-id line in the commit msg contains the url' '
+ test_cmp .git/refs/remotes/svnfile/master .git/refs/remotes/svnsim/master
+'
+
+test_expect_success REMOTE_SVN 'mark-file regeneration' '
+ # filter out any other marks, that can not be regenerated. Only up to 3 digit revisions are allowed here
+ grep ":[0-9]\{1,3\} " $MARKSPATH/svnsim.marks > $MARKSPATH/svnsim.marks.old &&
+ rm $MARKSPATH/svnsim.marks &&
+ git fetch svnsim &&
+ test_cmp $MARKSPATH/svnsim.marks.old $MARKSPATH/svnsim.marks
+'
+
+test_expect_success REMOTE_SVN 'incremental imports must lead to the same head' '
+ SVNRMAX=3 &&
+ export SVNRMAX &&
+ init_git &&
+ git fetch svnsim &&
+ test_cmp .git/refs/svn/svnsim/master .git/refs/remotes/svnsim/master &&
+ unset SVNRMAX &&
+ git fetch svnsim &&
+ test_cmp master.good .git/refs/remotes/svnsim/master
+'
+
+test_debug 'git branch -a'
+
+test_done
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index 13766ab..28082b1 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -22,17 +22,18 @@ 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 .. &&
+ (
+ 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
+ ) &&
rm -rf import &&
git svn init "$svnrepo"'
@@ -44,13 +45,13 @@ 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} &&
+ 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 &&
+ remotes/git-svn..mybranch &&
svn_cmd up "$SVN_TREE" &&
test -d "$SVN_TREE"/dir && test ! -d "$SVN_TREE"/dir/a'
@@ -64,13 +65,14 @@ test_expect_success "$name" "
git update-index --add dir/file/file &&
git commit -m '$name' &&
test_must_fail git svn set-tree --find-copies-harder --rmdir \
- ${remotes_git_svn}..mybranch" || true
+ remotes/git-svn..mybranch
+"
name='detect node change from directory to file #1'
test_expect_success "$name" '
rm -rf dir "$GIT_DIR"/index &&
- git checkout -f -b mybranch2 ${remotes_git_svn} &&
+ git checkout -f -b mybranch2 remotes/git-svn &&
mv bar/zzz zzz &&
rm -rf bar &&
mv zzz bar &&
@@ -78,27 +80,31 @@ test_expect_success "$name" '
git update-index --add -- bar &&
git commit -m "$name" &&
test_must_fail git svn set-tree --find-copies-harder --rmdir \
- ${remotes_git_svn}..mybranch2' || true
+ remotes/git-svn..mybranch2
+'
name='detect node change from file to directory #2'
test_expect_success "$name" '
rm -f "$GIT_DIR"/index &&
- git checkout -f -b mybranch3 ${remotes_git_svn} &&
+ 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
-
+ git svn set-tree --find-copies-harder --rmdir \
+ remotes/git-svn..mybranch3 &&
+ svn_cmd up "$SVN_TREE" &&
+ test -d "$SVN_TREE"/bar/zzz &&
+ test -e "$SVN_TREE"/bar/zzz/yyy
+'
name='detect node change from directory to file #2'
test_expect_success "$name" '
rm -f "$GIT_DIR"/index &&
- git checkout -f -b mybranch4 ${remotes_git_svn} &&
+ git checkout -f -b mybranch4 remotes/git-svn &&
rm -rf dir &&
git update-index --remove -- dir/file &&
touch dir &&
@@ -106,75 +112,77 @@ test_expect_success "$name" '
git update-index --add -- dir &&
git commit -m "$name" &&
test_must_fail git svn set-tree --find-copies-harder --rmdir \
- ${remotes_git_svn}..mybranch4' || true
+ remotes/git-svn..mybranch4
+'
name='remove executable bit from a file'
-test_expect_success "$name" '
+test_expect_success POSIXPERM "$name" '
rm -f "$GIT_DIR"/index &&
- git checkout -f -b mybranch5 ${remotes_git_svn} &&
+ 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 &&
+ 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" '
+test_expect_success POSIXPERM "$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 &&
+ 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" '
+name='executable file becomes a symlink to file'
+test_expect_success SYMLINKS "$name" '
rm exec.sh &&
- ln -s bar/zzz exec.sh &&
+ ln -s file exec.sh &&
git update-index exec.sh &&
git commit -m "$name" &&
git svn set-tree --find-copies-harder --rmdir \
- ${remotes_git_svn}..mybranch5 &&
+ remotes/git-svn..mybranch5 &&
svn_cmd up "$SVN_TREE" &&
- test -L "$SVN_TREE"/exec.sh'
+ test -h "$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 &&
+test_expect_success POSIXPERM,SYMLINKS "$name" '
+ chmod +x file &&
+ ln -s file exec-2.sh &&
+ git update-index --add file exec-2.sh &&
git commit -m "$name" &&
git svn set-tree --find-copies-harder --rmdir \
- ${remotes_git_svn}..mybranch5 &&
+ remotes/git-svn..mybranch5 &&
svn_cmd up "$SVN_TREE" &&
- test -x "$SVN_TREE"/bar/zzz &&
- test -L "$SVN_TREE"/exec-2.sh'
+ test -x "$SVN_TREE"/file &&
+ test -h "$SVN_TREE"/exec-2.sh'
name='modify a symlink to become a file'
-test_expect_success "$name" '
- echo git help > help || true &&
+test_expect_success POSIXPERM,SYMLINKS "$name" '
+ echo git help >help &&
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 &&
+ remotes/git-svn..mybranch5 &&
svn_cmd up "$SVN_TREE" &&
test -f "$SVN_TREE"/exec-2.sh &&
- test ! -L "$SVN_TREE"/exec-2.sh &&
+ test ! -h "$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" "
+# This test relies on the previous test, hence requires POSIXPERM,SYMLINKS
+test_expect_success UTF8,POSIXPERM,SYMLINKS "$name" "
echo '# hello' >> exec-2.sh &&
git update-index exec-2.sh &&
git commit -m 'éïâˆ' &&
@@ -186,7 +194,7 @@ 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/git-svn | grep ^tree | uniq > a &&
git rev-list --pretty=raw remotes/alt | grep ^tree | uniq > b &&
test_cmp a b'
@@ -194,31 +202,32 @@ name='check imported tree checksums expected tree checksums'
rm -f expected
if test_have_prereq UTF8
then
- echo tree bf522353586b1b883488f2bc73dab0d9f774b9a9 > expected
+ echo tree dc68b14b733e4ec85b04ab6f712340edc5dc936e > expected
fi
cat >> expected <<\EOF
-tree 83654bb36f019ae4fe77a0171f81075972087624
-tree 031b8d557afc6fea52894eaebb45bec52f1ba6d1
-tree 0b094cbff17168f24c302e297f55bfac65eb8bd3
-tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
-tree 56a30b966619b863674f5978696f4a3594f2fca9
+tree c3322890dcf74901f32d216f05c5044f670ce632
+tree d3ccd5035feafd17b030c5732e7808cc49122853
+tree d03e1630363d4881e68929d532746b20b0986b83
+tree 149d63cd5878155c846e8c55d7d8487de283f89e
+tree 312b76e4f64ce14893aeac8591eb3960b065e247
+tree 149d63cd5878155c846e8c55d7d8487de283f89e
tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
tree 8f51f74cf0163afc9ad68a4b1537288c4558b5a4
EOF
-test_expect_success "$name" "test_cmp a expected"
+test_expect_success POSIXPERM,SYMLINKS "$name" "test_cmp a expected"
-test_expect_success 'exit if remote refs are ambigious' "
+test_expect_success 'exit if remote refs are ambigious' '
git config --add svn-remote.svn.fetch \
- bar:refs/${remotes_git_svn} &&
+ 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}$" &&
+ "^bar:refs/remotes/git-svn$" &&
test_must_fail git svn init "${svnrepo}2/bar"
'
@@ -228,7 +237,7 @@ test_expect_success \
git config --get svn-remote.svn.fetch \
"^bar:refs/remotes/bar$" &&
git config --get svn-remote.svn.fetch \
- "^:refs/${remotes_git_svn}$"
+ "^:refs/remotes/git-svn$"
'
test_expect_success 'dcommit $rev does not clobber current branch' '
@@ -250,34 +259,45 @@ test_expect_success 'dcommit $rev does not clobber current branch' '
git branch -D my-bar
'
-test_expect_success 'able to dcommit to a subdirectory' "
+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 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\`\" &&
+ 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 commit -m "add a new directory" &&
git svn dcommit -i bar &&
- test -z \"\`git diff refs/heads/my-bar refs/remotes/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 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 -z "$(git diff refs/heads/my-bar refs/remotes/bar)"
+'
-test_expect_success 'able to set-tree to a subdirectory' "
+test_expect_success 'dcommit should not fail with a touched file' '
+ test_commit "commit-new-file-foo2" foo2 &&
+ test-chmtime =-60 foo &&
+ git svn dcommit
+'
+
+test_expect_success 'rebase should not fail with a touched file' '
+ test-chmtime =-60 foo &&
+ git svn rebase
+'
+
+test_expect_success 'able to set-tree to a subdirectory' '
echo cba > d &&
git update-index d &&
- git commit -m 'update /bar/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 -z "$(git diff refs/heads/my-bar refs/remotes/bar)"
+'
test_expect_success 'git-svn works in a bare repository' '
mkdir bare-repo &&
@@ -287,5 +307,13 @@ test_expect_success 'git-svn works in a bare repository' '
git svn fetch ) &&
rm -rf bare-repo
'
+test_expect_success 'git-svn works in in a repository with a gitdir: link' '
+ mkdir worktree gitdir &&
+ ( cd worktree &&
+ git svn init "$svnrepo" &&
+ git init --separate-git-dir ../gitdir &&
+ git svn fetch ) &&
+ rm -rf worktree gitdir
+ '
test_done
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index 929499e..07bfb63 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -26,35 +26,36 @@ cd import
EOF
printf "Hello\r\nWorld\r\n" > crlf
- a_crlf=`git hash-object -w crlf`
+ a_crlf=$(git hash-object -w crlf)
printf "Hello\rWorld\r" > cr
- a_cr=`git hash-object -w cr`
+ a_cr=$(git hash-object -w cr)
printf "Hello\nWorld\n" > lf
- a_lf=`git hash-object -w lf`
+ a_lf=$(git hash-object -w lf)
printf "Hello\r\nWorld" > ne_crlf
- a_ne_crlf=`git hash-object -w 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`
+ a_ne_lf=$(git hash-object -w ne_lf)
printf "Hello\rWorld" > ne_cr
- a_ne_cr=`git hash-object -w ne_cr`
+ a_ne_cr=$(git hash-object -w ne_cr)
touch empty
- a_empty=`git hash-object -w empty`
+ a_empty=$(git hash-object -w empty)
printf "\n" > empty_lf
- a_empty_lf=`git hash-object -w empty_lf`
+ a_empty_lf=$(git hash-object -w empty_lf)
printf "\r" > empty_cr
- a_empty_cr=`git hash-object -w 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`
+ 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 &&
+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" &&
@@ -63,35 +64,38 @@ test_expect_success 'setup some commits to svn' \
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 ..'
+ svn_cmd commit -m "Propset Id"
+ )
+'
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} &&
+ '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}'
+ git svn set-tree remotes/git-svn..mybranch &&
+ git pull . remotes/git-svn'
expect='/* $Id$ */'
-got="`sed -ne 2p kw.c`"
+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 &&
+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 ..'
+ svn_cmd commit -m "propset CR on crlf files"
+ )
+'
test_expect_success 'fetch and pull latest from svn and checkout a new wc' \
'git svn fetch &&
- git pull . ${remotes_git_svn} &&
+ 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
@@ -103,8 +107,8 @@ 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`
+ 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 &&
@@ -113,10 +117,10 @@ cd test_wc
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}'
+ 'git svn fetch && git pull . remotes/git-svn'
-b_cr="`git hash-object cr`"
-b_ne_cr="`git hash-object ne_cr`"
+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'"
@@ -137,19 +141,20 @@ cat > show-ignore.expect <<\EOF
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 '
+ (
+ 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 .. &&
+ svn_cmd commit -m 'propset svn:ignore'
+ ) &&
git svn show-ignore > show-ignore.got &&
cmp show-ignore.expect show-ignore.got
- "
+"
cat >create-ignore.expect <<\EOF
/no-such-file*
@@ -163,7 +168,7 @@ cat >create-ignore-index.expect <<\EOF
EOF
test_expect_success 'test create-ignore' "
- git svn fetch && git pull . ${remotes_git_svn} &&
+ git svn fetch && git pull . remotes/git-svn &&
git svn create-ignore &&
cmp ./.gitignore create-ignore.expect &&
cmp ./deeply/.gitignore create-ignore.expect &&
diff --git a/t/t9102-git-svn-deep-rmdir.sh b/t/t9102-git-svn-deep-rmdir.sh
index 028fb19..66cd511 100755
--- a/t/t9102-git-svn-deep-rmdir.sh
+++ b/t/t9102-git-svn-deep-rmdir.sh
@@ -4,19 +4,20 @@ test_description='git svn rmdir'
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 ..
+ (
+ 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"
+ )
'
test_expect_success 'mirror via git svn' '
git svn init "$svnrepo" &&
git svn fetch &&
- git checkout -f -b test-rmdir ${remotes_git_svn}
+ git checkout -f -b test-rmdir remotes/git-svn
'
test_expect_success 'Try a commit on rmdir' '
diff --git a/t/t9103-git-svn-tracked-directory-removed.sh b/t/t9103-git-svn-tracked-directory-removed.sh
index 3413164..b282713 100755
--- a/t/t9103-git-svn-tracked-directory-removed.sh
+++ b/t/t9103-git-svn-tracked-directory-removed.sh
@@ -23,17 +23,19 @@ test_expect_success 'make history for tracking' '
test_expect_success 'clone repo with git' '
git svn clone -s "$svnrepo" x &&
- test -f x/FOLLOWME &&
- test ! -f x/README
+ test_path_is_file x/FOLLOWME &&
+ test_path_is_missing 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_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_path_is_file README &&
+ test_path_is_missing FOLLOWME &&
+ test -z "$(git svn find-rev r2)"
+ )
+'
test_done
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index bbfd7f4..cd480ed 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -8,33 +8,35 @@ test_description='git svn fetching'
test_expect_success 'initialize repo' '
mkdir import &&
- cd import &&
- mkdir -p trunk &&
- echo hello > trunk/readme &&
- svn_cmd import -m "initial" . "$svnrepo" &&
- cd .. &&
+ (
+ cd import &&
+ mkdir -p trunk &&
+ echo hello >trunk/readme &&
+ svn_cmd import -m "initial" . "$svnrepo"
+ ) &&
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 ..
+ (
+ 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"
+ )
'
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 "$(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' '
@@ -44,10 +46,10 @@ test_expect_success 'init and fetch from one svn-remote' '
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 "$(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' '
@@ -59,9 +61,9 @@ test_expect_success 'follow deleted parent' '
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 -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' '
@@ -70,29 +72,32 @@ test_expect_success 'follow larger parent' '
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 &&
+ "$svnrepo"/larger-parent/trunk/thunk/bump/thud &&
git svn fetch -i larger &&
+ git svn init --minimize-url -i larger-parent \
+ "$svnrepo"/another-larger/trunk/thunk/bump/thud &&
+ git svn fetch -i larger-parent &&
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
+ refs/remotes/larger-parent &&
+ test "$(git merge-base \
+ refs/remotes/larger-parent \
+ refs/remotes/larger)" = \
+ "$(git rev-parse refs/remotes/larger)"
'
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 &&
+ 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"
+ ) &&
+ 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
'
@@ -101,8 +106,8 @@ test_expect_success 'follow deleted directory' '
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
+ 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)
@@ -117,24 +122,29 @@ test_expect_success 'follow-parent avoids deleting relevant info' '
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 .. &&
+ (
+ cd import &&
+ svn import -m "r9270 test" . "$svnrepo"/r9270
+ ) &&
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 .. &&
+ (
+ 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"
+ ) &&
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 $(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" '
@@ -142,11 +152,11 @@ test_expect_success "track initial change if it was only made to parent" '
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 $(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" '
@@ -182,20 +192,20 @@ test_expect_success "follow-parent is atomic" '
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`"
+ 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 $(git cat-file commit refs/remotes/glob | \
+ grep "^parent " | wc -l) -eq 2
'
test_expect_success "multi-fetch continues to work" "
diff --git a/t/t9105-git-svn-commit-diff.sh b/t/t9105-git-svn-commit-diff.sh
index dd48e9c..6ed5f74 100755
--- a/t/t9105-git-svn-commit-diff.sh
+++ b/t/t9105-git-svn-commit-diff.sh
@@ -6,10 +6,11 @@ test_description='git svn commit-diff'
test_expect_success 'initialize repo' '
mkdir import &&
- cd import &&
- echo hello > readme &&
- svn_cmd import -m "initial" . "$svnrepo" &&
- cd .. &&
+ (
+ cd import &&
+ echo hello >readme &&
+ svn_cmd import -m "initial" . "$svnrepo"
+ ) &&
echo hello > readme &&
git update-index --add readme &&
git commit -a -m "initial" &&
@@ -17,8 +18,8 @@ test_expect_success 'initialize repo' '
git commit -a -m "another"
'
-head=`git rev-parse --verify HEAD^0`
-prev=`git rev-parse --verify HEAD^1`
+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
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index 12f21b7..dbe8dea 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -6,21 +6,23 @@ test_description='git svn commit-diff clobber'
test_expect_success 'initialize repo' '
mkdir import &&
- cd import &&
- echo initial > file &&
- svn_cmd import -m "initial" . "$svnrepo" &&
- cd .. &&
+ (
+ cd import &&
+ echo initial >file &&
+ svn_cmd import -m "initial" . "$svnrepo"
+ ) &&
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 .. &&
+ (
+ cd t.svn &&
+ echo second line from svn >>file &&
+ poke file &&
+ svn_cmd commit -m "second line from svn"
+ ) &&
rm -rf t.svn
'
@@ -42,13 +44,14 @@ test_expect_success 'commit complementing change from git' '
test_expect_success 'dcommit fails to commit because of conflict' '
git svn init "$svnrepo" &&
git svn fetch &&
- git reset --hard refs/${remotes_git_svn} &&
+ 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 .. &&
+ (
+ cd t.svn &&
+ echo fourth line from svn >>file &&
+ poke file &&
+ svn_cmd commit -m "fourth line from svn"
+ ) &&
rm -rf t.svn &&
echo "fourth line from git" >> file &&
git commit -a -m "fourth line from git" &&
@@ -56,7 +59,7 @@ test_expect_success 'dcommit fails to commit because of conflict' '
'
test_expect_success 'dcommit does the svn equivalent of an index merge' "
- git reset --hard refs/${remotes_git_svn} &&
+ git reset --hard refs/remotes/git-svn &&
echo 'index merge' > file2 &&
git update-index --add file2 &&
git commit -a -m 'index merge' &&
@@ -68,16 +71,17 @@ test_expect_success 'dcommit does the svn equivalent of an index merge' "
test_expect_success 'commit another change from svn side' '
svn_cmd co "$svnrepo" t.svn &&
- cd t.svn &&
- echo third line from svn >> file &&
+ (
+ cd t.svn &&
+ echo third line from svn >>file &&
poke file &&
- svn_cmd commit -m "third line from svn" &&
- cd .. &&
+ svn_cmd commit -m "third line from svn"
+ ) &&
rm -rf t.svn
'
test_expect_success 'multiple dcommit from git svn will not clobber svn' "
- git reset --hard refs/${remotes_git_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' &&
diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh
index 901b8e0..9f3ef8f 100755
--- a/t/t9107-git-svn-migrate.sh
+++ b/t/t9107-git-svn-migrate.sh
@@ -6,115 +6,130 @@ test_description='git svn metadata migrations from previous versions'
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 && \
+ (
+ 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}
+ 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 'git-svn-HEAD is a real HEAD' '
+ git rev-parse --verify refs/heads/git-svn-HEAD^0
+'
+
+svnrepo_escaped=$(echo $svnrepo | sed 's/ /%20/')
test_expect_success 'initialize old-style (v0) git svn layout' '
mkdir -p "$GIT_DIR"/git-svn/info "$GIT_DIR"/svn/info &&
echo "$svnrepo" > "$GIT_DIR"/git-svn/info/url &&
echo "$svnrepo" > "$GIT_DIR"/svn/info/url &&
git svn migrate &&
- ! test -d "$GIT_DIR"/git svn &&
- git rev-parse --verify refs/${remotes_git_svn}^0 &&
+ ! 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 "$(git config --get svn-remote.svn.url)" = "$svnrepo_escaped" &&
+ 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/\*$"`" &&
+ grep "^trunk:refs/remotes/origin/trunk$" fetch.out &&
+ test -n "$(git config --get svn-remote.svn.branches \
+ "^branches/\*:refs/remotes/origin/\*$")" &&
+ test -n "$(git config --get svn-remote.svn.tags \
+ "^tags/\*:refs/remotes/origin/tags/\*$")" &&
git config --unset svn-remote.svn.branches \
- "^branches/\*:refs/remotes/\*$" &&
+ "^branches/\*:refs/remotes/origin/\*$" &&
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
+ "^tags/\*:refs/remotes/origin/tags/\*$" &&
+ git config --add svn-remote.svn.fetch "branches/a:refs/remotes/origin/a" &&
+ git config --add svn-remote.svn.fetch "branches/b:refs/remotes/origin/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 &&
+ $i:refs/remotes/origin/$i || return 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
+ grep "^trunk:refs/remotes/origin/trunk$" fetch.out &&
+ grep "^branches/a:refs/remotes/origin/a$" fetch.out &&
+ grep "^branches/b:refs/remotes/origin/b$" fetch.out &&
+ grep "^tags/0\.1:refs/remotes/origin/tags/0\.1$" fetch.out &&
+ grep "^tags/0\.2:refs/remotes/origin/tags/0\.2$" fetch.out &&
+ grep "^tags/0\.3:refs/remotes/origin/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' "
+test_expect_success 'multi-fetch works on partial urls + paths' '
+ refs="trunk a b tags/0.1 tags/0.2 tags/0.3" &&
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
- "
+ for i in $refs
+ do
+ git rev-parse --verify refs/remotes/origin/$i^0 || return 1;
+ done >refs.out &&
+ test -z "$(sort <refs.out | uniq -d)" &&
+ for i in $refs
+ do
+ for j in $refs
+ do
+ git diff --exit-code refs/remotes/origin/$i \
+ refs/remotes/origin/$j ||
+ return 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/.*\)$"`
+ 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;
+ mkdir -p "$GIT_DIR"/svn/$ref/info/ &&
+ echo "$svnrepo"$path >"$GIT_DIR"/svn/$ref/info/url ||
+ return 1
done &&
git svn migrate --minimize &&
- test -z "`git config -l | grep "^svn-remote\.git-svn\."`" &&
+ 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
+ grep "^trunk:refs/remotes/origin/trunk$" fetch.out &&
+ grep "^branches/a:refs/remotes/origin/a$" fetch.out &&
+ grep "^branches/b:refs/remotes/origin/b$" fetch.out &&
+ grep "^tags/0\.1:refs/remotes/origin/tags/0\.1$" fetch.out &&
+ grep "^tags/0\.2:refs/remotes/origin/tags/0\.2$" fetch.out &&
+ grep "^tags/0\.3:refs/remotes/origin/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 -z "$(ls "$GIT_DIR"/svn/refs/remotes/origin/trunk/.rev_db.* 2>/dev/null)" &&
+ expect="$(ls "$GIT_DIR"/svn/refs/remotes/origin/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 -z "$(ls "$GIT_DIR"/svn/refs/remotes/origin/trunk/.rev_db.* 2>/dev/null)" &&
+ test ! -e "$GIT_DIR"/svn/refs/remotes/origin/trunk/.rev_db &&
test -f "$expect"
'
diff --git a/t/t9108-git-svn-glob.sh b/t/t9108-git-svn-glob.sh
index d732d31..a94286c 100755
--- a/t/t9108-git-svn-glob.sh
+++ b/t/t9108-git-svn-glob.sh
@@ -50,10 +50,10 @@ test_expect_success 'test refspec globbing' '
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 "$(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
'
@@ -75,20 +75,23 @@ test_expect_success 'test left-hand-side only globbing' '
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` &&
+ 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 'prepare test disallow multi-globs' "
+cat >expect.three <<EOF
+Only one set of wildcards (e.g. '*' or '*/*/*') is supported: branches/*/t/*
+
+EOF
+ "
test_expect_success 'test disallow multi-globs' '
git config --add svn-remote.three.url "$svnrepo" &&
diff --git a/t/t9109-git-svn-multi-glob.sh b/t/t9109-git-svn-multi-glob.sh
index c318f9f..8d99e84 100755
--- a/t/t9109-git-svn-multi-glob.sh
+++ b/t/t9109-git-svn-multi-glob.sh
@@ -50,10 +50,10 @@ test_expect_success 'test refspec globbing' '
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 "$(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
'
@@ -75,12 +75,12 @@ test_expect_success 'test left-hand-side only globbing' '
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` &&
+ 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
@@ -124,20 +124,23 @@ test_expect_success 'test another branch' '
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` &&
+ 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 'prepare test disallow multiple globs' "
+cat >expect.three <<EOF
+Only one set of wildcards (e.g. '*' or '*/*/*') is supported: branches/*/t/*
+
+EOF
+ "
test_expect_success 'test disallow multiple globs' '
git config --add svn-remote.three.url "$svnrepo" &&
diff --git a/t/t9110-git-svn-use-svm-props.sh b/t/t9110-git-svn-use-svm-props.sh
index a06e4c5..dde0a3c 100755
--- a/t/t9110-git-svn-use-svm-props.sh
+++ b/t/t9110-git-svn-use-svm-props.sh
@@ -22,36 +22,36 @@ 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$' &&
+ grep '^git-svn-id: $bar_url@12 $uuid$' &&
git cat-file commit refs/remotes/bar~1 | \
- grep '^${git_svn_id}: $bar_url@11 $uuid$' &&
+ grep '^git-svn-id: $bar_url@11 $uuid$' &&
git cat-file commit refs/remotes/bar~2 | \
- grep '^${git_svn_id}: $bar_url@10 $uuid$' &&
+ grep '^git-svn-id: $bar_url@10 $uuid$' &&
git cat-file commit refs/remotes/bar~3 | \
- grep '^${git_svn_id}: $bar_url@9 $uuid$' &&
+ grep '^git-svn-id: $bar_url@9 $uuid$' &&
git cat-file commit refs/remotes/bar~4 | \
- grep '^${git_svn_id}: $bar_url@6 $uuid$' &&
+ grep '^git-svn-id: $bar_url@6 $uuid$' &&
git cat-file commit refs/remotes/bar~5 | \
- grep '^${git_svn_id}: $bar_url@1 $uuid$'
+ 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$'
+ 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$' &&
+ grep '^git-svn-id: $dir_url@2 $uuid$' &&
git cat-file commit refs/remotes/dir~1 | \
- grep '^${git_svn_id}: $dir_url@1 $uuid$'
+ 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`
+ grep $(git rev-parse HEAD)
"
test_expect_success 'empty rebase' "
diff --git a/t/t9111-git-svn-use-svnsync-props.sh b/t/t9111-git-svn-use-svnsync-props.sh
index bd081c2..22b6e5e 100755
--- a/t/t9111-git-svn-use-svnsync-props.sh
+++ b/t/t9111-git-svn-use-svnsync-props.sh
@@ -21,31 +21,31 @@ 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$' &&
+ grep '^git-svn-id: $bar_url@12 $uuid$' &&
git cat-file commit refs/remotes/bar~1 | \
- grep '^${git_svn_id}: $bar_url@11 $uuid$' &&
+ grep '^git-svn-id: $bar_url@11 $uuid$' &&
git cat-file commit refs/remotes/bar~2 | \
- grep '^${git_svn_id}: $bar_url@10 $uuid$' &&
+ grep '^git-svn-id: $bar_url@10 $uuid$' &&
git cat-file commit refs/remotes/bar~3 | \
- grep '^${git_svn_id}: $bar_url@9 $uuid$' &&
+ grep '^git-svn-id: $bar_url@9 $uuid$' &&
git cat-file commit refs/remotes/bar~4 | \
- grep '^${git_svn_id}: $bar_url@6 $uuid$' &&
+ grep '^git-svn-id: $bar_url@6 $uuid$' &&
git cat-file commit refs/remotes/bar~5 | \
- grep '^${git_svn_id}: $bar_url@1 $uuid$'
+ 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$'
+ 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$' &&
+ grep '^git-svn-id: $dir_url@2 $uuid$' &&
git cat-file commit refs/remotes/dir~1 | \
- grep '^${git_svn_id}: $dir_url@1 $uuid$'
+ grep '^git-svn-id: $dir_url@1 $uuid$'
"
test_done
diff --git a/t/t9112-git-svn-md5less-file.sh b/t/t9112-git-svn-md5less-file.sh
index a61d671..9861c71 100755
--- a/t/t9112-git-svn-md5less-file.sh
+++ b/t/t9112-git-svn-md5less-file.sh
@@ -7,7 +7,7 @@ test_description='test that git handles an svn repository with missing md5sums'
# Loading a node from a svn dumpfile without a Text-Content-Length
# field causes svn to neglect to store or report an md5sum. (it will
# calculate one if you had put Text-Content-Length: 0). This showed
-# up in a repository creted with cvs2svn.
+# up in a repository created with cvs2svn.
cat > dumpfile.svn <<EOF
SVN-fs-dump-format-version: 1
diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh
index 84f7c9b..a3d3882 100755
--- a/t/t9114-git-svn-dcommit-merge.sh
+++ b/t/t9114-git-svn-dcommit-merge.sh
@@ -37,17 +37,18 @@ EOF
test_expect_success 'setup svn repository' '
svn_cmd co "$svnrepo" mysvnwork &&
mkdir -p mysvnwork/trunk &&
- cd mysvnwork &&
- big_text_block >> trunk/README &&
+ (
+ cd mysvnwork &&
+ big_text_block >>trunk/README &&
svn_cmd add trunk &&
- svn_cmd ci -m "first commit" trunk &&
- cd ..
+ svn_cmd ci -m "first commit" trunk
+ )
'
test_expect_success 'setup git mirror and merge' '
git svn init "$svnrepo" -t tags -T trunk -b branches &&
git svn fetch &&
- git checkout --track -b svn remotes/trunk &&
+ git checkout -b svn remotes/origin/trunk &&
git checkout -b merge &&
echo new file > new_file &&
git add new_file &&
@@ -61,14 +62,14 @@ test_expect_success 'setup git mirror and merge' '
echo friend > README &&
cat tmp >> README &&
git commit -a -m "friend" &&
- git pull . merge
+ git merge merge
'
test_debug 'gitk --all & sleep 1'
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\` &&
+ 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$'
"
@@ -79,10 +80,10 @@ test_expect_success 'git svn dcommit merges' "
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\` &&
+ test x\$(git rev-parse --verify refs/heads/svn) = \
+ x\$(git rev-parse --verify refs/remotes/origin/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$'
"
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
index 767799e..a87d3d3 100755
--- a/t/t9115-git-svn-dcommit-funky-renames.sh
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -61,11 +61,12 @@ test_expect_success 'add a file with plus signs' '
test_expect_success 'clone the repository to test rebase' '
git svn clone "$svnrepo" test-rebase &&
- cd test-rebase &&
- echo test-rebase > test-rebase &&
+ (
+ cd test-rebase &&
+ echo test-rebase >test-rebase &&
git add test-rebase &&
- git commit -m test-rebase &&
- cd ..
+ git commit -m test-rebase
+ )
'
test_expect_success 'make a commit to test rebase' '
@@ -76,11 +77,47 @@ test_expect_success 'make a commit to test rebase' '
'
test_expect_success 'git svn rebase works inside a fresh-cloned repository' '
- cd test-rebase &&
+ (
+ cd test-rebase &&
git svn rebase &&
test -e test-rebase-main &&
test -e test-rebase
- '
+ )'
+
+# Without this, LC_ALL=C as set in test-lib.sh, and Cygwin converts
+# non-ASCII characters in filenames unexpectedly, and causes errors.
+# https://cygwin.com/cygwin-ug-net/using-specialnames.html#pathnames-specialchars
+# > Some characters are disallowed in filenames on Windows filesystems. ...
+# ...
+# > ... All of the above characters, except for the backslash, are converted
+# > to special UNICODE characters in the range 0xf000 to 0xf0ff (the
+# > "Private use area") when creating or accessing files.
+prepare_a_utf8_locale
+test_expect_success UTF8,!MINGW,!UTF8_NFD_TO_NFC 'svn.pathnameencoding=cp932 new file on dcommit' '
+ LC_ALL=$a_utf8_locale &&
+ export LC_ALL &&
+ neq=$(printf "\201\202") &&
+ git config svn.pathnameencoding cp932 &&
+ echo neq >"$neq" &&
+ git add "$neq" &&
+ git commit -m "neq" &&
+ git svn dcommit
+'
+
+# See the comment on the above test for setting of LC_ALL.
+test_expect_success !MINGW,!UTF8_NFD_TO_NFC 'svn.pathnameencoding=cp932 rename on dcommit' '
+ LC_ALL=$a_utf8_locale &&
+ export LC_ALL &&
+ inf=$(printf "\201\207") &&
+ git config svn.pathnameencoding cp932 &&
+ echo inf >"$inf" &&
+ git add "$inf" &&
+ git commit -m "inf" &&
+ git svn dcommit &&
+ git mv "$inf" inf &&
+ git commit -m "inf rename" &&
+ git svn dcommit
+'
stop_httpd
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index 0374a74..45773ee 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -8,30 +8,32 @@ test_description='git svn log tests'
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 && \
+ (
+ 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 &&
+ git reset --hard origin/trunk &&
echo bye >> README &&
git commit -a -m bye &&
git svn dcommit &&
- git reset --hard a &&
+ git reset --hard origin/a &&
echo why >> FEEDME &&
git update-index --add FEEDME &&
git commit -m feedme &&
git svn dcommit &&
- git reset --hard trunk &&
+ git reset --hard origin/trunk &&
echo aye >> README &&
git commit -a -m aye &&
git svn dcommit &&
- git reset --hard b &&
+ git reset --hard origin/b &&
echo spy >> README &&
git commit -a -m spy &&
echo try >> README &&
@@ -40,88 +42,103 @@ test_expect_success 'setup repository and import' '
'
test_expect_success 'run log' "
- git reset --hard a &&
- git svn log -r2 trunk | grep ^r2 &&
- git svn log -r4 trunk | grep ^r4 &&
+ git reset --hard origin/a &&
+ git svn log -r2 origin/trunk | grep ^r2 &&
+ git svn log -r4 origin/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
+ git reset --hard origin/trunk &&
+ git svn log -r3 origin/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 reset --hard origin/trunk &&
git svn log -r 1:4 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r1-r2-r4 -
"
+test_expect_success 'test ascending revision range with --show-commit' "
+ git reset --hard origin/trunk &&
+ git svn log --show-commit -r 1:4 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r1-r2-r4 -
+ "
+
+test_expect_success 'test ascending revision range with --show-commit (sha1)' "
+ git svn find-rev r1 >expected-range-r1-r2-r4-sha1 &&
+ git svn find-rev r2 >>expected-range-r1-r2-r4-sha1 &&
+ git svn find-rev r4 >>expected-range-r1-r2-r4-sha1 &&
+ git reset --hard origin/trunk &&
+ git svn log --show-commit -r 1:4 | grep '^r[0-9]' | cut -d'|' -f2 >out &&
+ git rev-parse \$(cat out) >actual &&
+ test_cmp expected-range-r1-r2-r4-sha1 actual
+ "
+
printf 'r4 \nr2 \nr1 \n' > expected-range-r4-r2-r1
test_expect_success 'test descending revision range' "
- git reset --hard trunk &&
+ git reset --hard origin/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 reset --hard origin/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 reset --hard origin/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 reset --hard origin/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 reset --hard origin/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 reset --hard origin/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 reset --hard origin/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 reset --hard origin/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 reset --hard origin/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 reset --hard origin/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 reset --hard origin/trunk &&
git svn log -r 5:3 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r4 -
"
diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh
index b7ef9e2..69a6750 100755
--- a/t/t9117-git-svn-init-clone.sh
+++ b/t/t9117-git-svn-init-clone.sh
@@ -52,4 +52,77 @@ test_expect_success 'clone to target directory with --stdlayout' '
rm -rf target
'
+test_expect_success 'init without -s/-T/-b/-t does not warn' '
+ test ! -d trunk &&
+ git svn init "$svnrepo"/project/trunk trunk 2>warning &&
+ test_must_fail grep -q prefix warning &&
+ rm -rf trunk &&
+ rm -f warning
+ '
+
+test_expect_success 'clone without -s/-T/-b/-t does not warn' '
+ test ! -d trunk &&
+ git svn clone "$svnrepo"/project/trunk 2>warning &&
+ test_must_fail grep -q prefix warning &&
+ rm -rf trunk &&
+ rm -f warning
+ '
+
+test_svn_configured_prefix () {
+ prefix=$1 &&
+ cat >expect <<EOF &&
+project/trunk:refs/remotes/${prefix}trunk
+project/branches/*:refs/remotes/${prefix}*
+project/tags/*:refs/remotes/${prefix}tags/*
+EOF
+ test ! -f actual &&
+ git --git-dir=project/.git config svn-remote.svn.fetch >>actual &&
+ git --git-dir=project/.git config svn-remote.svn.branches >>actual &&
+ git --git-dir=project/.git config svn-remote.svn.tags >>actual &&
+ test_cmp expect actual &&
+ rm -f expect actual
+}
+
+test_expect_success 'init with -s/-T/-b/-t assumes --prefix=origin/' '
+ test ! -d project &&
+ git svn init -s "$svnrepo"/project project 2>warning &&
+ test_must_fail grep -q prefix warning &&
+ test_svn_configured_prefix "origin/" &&
+ rm -rf project &&
+ rm -f warning
+ '
+
+test_expect_success 'clone with -s/-T/-b/-t assumes --prefix=origin/' '
+ test ! -d project &&
+ git svn clone -s "$svnrepo"/project 2>warning &&
+ test_must_fail grep -q prefix warning &&
+ test_svn_configured_prefix "origin/" &&
+ rm -rf project &&
+ rm -f warning
+ '
+
+test_expect_success 'init with -s/-T/-b/-t and --prefix "" still works' '
+ test ! -d project &&
+ git svn init -s "$svnrepo"/project project --prefix "" 2>warning &&
+ test_must_fail grep -q prefix warning &&
+ test_svn_configured_prefix "" &&
+ rm -rf project &&
+ rm -f warning
+ '
+
+test_expect_success 'clone with -s/-T/-b/-t and --prefix "" still works' '
+ test ! -d project &&
+ git svn clone -s "$svnrepo"/project --prefix "" 2>warning &&
+ test_must_fail grep -q prefix warning &&
+ test_svn_configured_prefix "" &&
+ rm -rf project &&
+ rm -f warning
+ '
+
+test_expect_success 'init with -T as a full url works' '
+ test ! -d project &&
+ git svn init -T "$svnrepo"/project/trunk project &&
+ rm -rf project
+ '
+
test_done
diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh
index 63fc982..ecb1fed 100755
--- a/t/t9118-git-svn-funky-branch-names.sh
+++ b/t/t9118-git-svn-funky-branch-names.sh
@@ -23,33 +23,44 @@ test_expect_success 'setup svnrepo' '
"$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." &&
+ if test_have_prereq !MINGW
+ then
+ svn_cmd cp -m "trailing dot" "$svnrepo/pr ject/trunk" \
+ "$svnrepo/pr ject/branches/trailing_dot."
+ fi &&
svn_cmd cp -m "trailing .lock" "$svnrepo/pr ject/trunk" \
"$svnrepo/pr ject/branches/trailing_dotlock.lock" &&
svn_cmd cp -m "reflog" "$svnrepo/pr ject/trunk" \
- "$svnrepo/pr ject/branches/not-a%40{0}reflog" &&
+ "$svnrepo/pr ject/branches/not-a@{0}reflog@" &&
start_httpd
'
+# SVN 1.7 will truncate "not-a%40{0]" to just "not-a".
+# Look at what SVN wound up naming the branch and use that.
+# Be sure to escape the @ if it shows up.
+non_reflog=$(svn_cmd ls "$svnrepo/pr ject/branches" | grep not-a | sed 's/\///' | sed 's/@/%40/')
+
test_expect_success 'test clone with funky branch names' '
git svn clone -s "$svnrepo/pr ject" project &&
(
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"
+ git rev-parse "refs/remotes/origin/fun%20plugin" &&
+ git rev-parse "refs/remotes/origin/more%20fun%20plugin!" &&
+ git rev-parse "refs/remotes/origin/$scary_ref" &&
+ git rev-parse "refs/remotes/origin/%2Eleading_dot" &&
+ if test_have_prereq !MINGW
+ then
+ git rev-parse "refs/remotes/origin/trailing_dot%2E"
+ fi &&
+ git rev-parse "refs/remotes/origin/trailing_dotlock%2Elock" &&
+ git rev-parse "refs/remotes/origin/$non_reflog"
)
'
test_expect_success 'test dcommit to funky branch' "
(
cd project &&
- git reset --hard 'refs/remotes/more%20fun%20plugin!' &&
+ git reset --hard 'refs/remotes/origin/more%20fun%20plugin!' &&
echo hello >> foo &&
git commit -m 'hello' -- foo &&
git svn dcommit
@@ -59,7 +70,7 @@ test_expect_success 'test dcommit to funky branch' "
test_expect_success 'test dcommit to scary branch' '
(
cd project &&
- git reset --hard "refs/remotes/$scary_ref" &&
+ git reset --hard "refs/remotes/origin/$scary_ref" &&
echo urls are scary >> foo &&
git commit -m "eep" -- foo &&
git svn dcommit
@@ -69,7 +80,7 @@ test_expect_success 'test dcommit to scary branch' '
test_expect_success 'test dcommit to trailing_dotlock branch' '
(
cd project &&
- git reset --hard "refs/remotes/trailing_dotlock%2Elock" &&
+ git reset --hard "refs/remotes/origin/trailing_dotlock%2Elock" &&
echo who names branches like this anyway? >> foo &&
git commit -m "bar" -- foo &&
git svn dcommit
diff --git a/t/t9119-git-svn-info.sh b/t/t9119-git-svn-info.sh
index 5fb94fb..88241ba 100755
--- a/t/t9119-git-svn-info.sh
+++ b/t/t9119-git-svn-info.sh
@@ -8,7 +8,7 @@ test_description='git svn info'
# 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'`
+v=$(svn_cmd --version | sed -n -e 's/^svn, version \(1\.[0-9]*\.[0-9]*\).*$/\1/p')
case $v in
1.[456].*)
;;
@@ -18,58 +18,50 @@ case $v in
;;
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"
+# On the "Text Last Updated" line, "git svn info" does not return the
+# same value as "svn info" (i.e. the commit timestamp that touched the
+# path most recently); do not expect that field to match.
+test_cmp_info () {
+ sed -e '/^Text Last Updated:/d' "$1" >tmp.expect
+ sed -e '/^Text Last Updated:/d' "$2" >tmp.actual
+ test_cmp tmp.expect tmp.actual &&
+ rm -f tmp.expect tmp.actual
}
quoted_svnrepo="$(echo $svnrepo | sed 's/ /%20/')"
test_expect_success 'setup repository and import' '
mkdir info &&
- cd info &&
- echo FIRST > A &&
- echo one > file &&
+ (
+ 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 import -m "initial" . "$svnrepo"
+ ) &&
svn_cmd co "$svnrepo" svnwc &&
- cd svnwc &&
- echo foo > foo &&
+ (
+ cd svnwc &&
+ echo foo >foo &&
svn_cmd add foo &&
svn_cmd commit -m "change outside directory" &&
- svn_cmd update &&
- cd .. &&
+ svn_cmd update
+ ) &&
mkdir gitwc &&
- cd 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
+ git svn fetch
+ )
'
test_expect_success 'info' "
(cd svnwc; svn info) > expected.info &&
(cd gitwc; git svn info) > actual.info &&
- test_cmp expected.info actual.info
+ test_cmp_info expected.info actual.info
"
test_expect_success 'info --url' '
@@ -79,9 +71,39 @@ test_expect_success 'info --url' '
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_cmp_info expected.info-dot actual.info-dot
"
+test_expect_success 'info $(pwd)' '
+ (cd svnwc; svn info "$(pwd)") >expected.info-pwd &&
+ (cd gitwc; git svn info "$(pwd)") >actual.info-pwd &&
+ grep -v ^Path: <expected.info-pwd >expected.info-np &&
+ grep -v ^Path: <actual.info-pwd >actual.info-np &&
+ test_cmp_info expected.info-np actual.info-np &&
+ test "$(sed -ne \"/^Path:/ s!/svnwc!!\" <expected.info-pwd)" = \
+ "$(sed -ne \"/^Path:/ s!/gitwc!!\" <actual.info-pwd)"
+ '
+
+test_expect_success 'info $(pwd)/../___wc' '
+ (cd svnwc; svn info "$(pwd)/../svnwc") >expected.info-pwd &&
+ (cd gitwc; git svn info "$(pwd)/../gitwc") >actual.info-pwd &&
+ grep -v ^Path: <expected.info-pwd >expected.info-np &&
+ grep -v ^Path: <actual.info-pwd >actual.info-np &&
+ test_cmp_info expected.info-np actual.info-np &&
+ test "$(sed -ne \"/^Path:/ s!/svnwc!!\" <expected.info-pwd)" = \
+ "$(sed -ne \"/^Path:/ s!/gitwc!!\" <actual.info-pwd)"
+ '
+
+test_expect_success 'info $(pwd)/../___wc//file' '
+ (cd svnwc; svn info "$(pwd)/../svnwc//file") >expected.info-pwd &&
+ (cd gitwc; git svn info "$(pwd)/../gitwc//file") >actual.info-pwd &&
+ grep -v ^Path: <expected.info-pwd >expected.info-np &&
+ grep -v ^Path: <actual.info-pwd >actual.info-np &&
+ test_cmp_info expected.info-np actual.info-np &&
+ test "$(sed -ne \"/^Path:/ s!/svnwc!!\" <expected.info-pwd)" = \
+ "$(sed -ne \"/^Path:/ s!/gitwc!!\" <actual.info-pwd)"
+ '
+
test_expect_success 'info --url .' '
test "$(cd gitwc; git svn info --url .)" = "$quoted_svnrepo"
'
@@ -89,7 +111,7 @@ test_expect_success 'info --url .' '
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_cmp_info expected.info-file actual.info-file
"
test_expect_success 'info --url file' '
@@ -99,13 +121,13 @@ test_expect_success 'info --url 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_cmp_info 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_cmp_info expected.info-inside-directory actual.info-inside-directory
"
test_expect_success 'info --url directory' '
@@ -115,7 +137,7 @@ test_expect_success 'info --url 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_cmp_info expected.info-symlink-file actual.info-symlink-file
"
test_expect_success 'info --url symlink-file' '
@@ -128,7 +150,7 @@ test_expect_success '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_cmp_info expected.info-symlink-directory actual.info-symlink-directory
"
test_expect_success 'info --url symlink-directory' '
@@ -138,17 +160,18 @@ test_expect_success 'info --url symlink-directory' '
test_expect_success 'info added-file' "
echo two > gitwc/added-file &&
- cd gitwc &&
- git add added-file &&
- cd .. &&
+ (
+ cd gitwc &&
+ git add added-file
+ ) &&
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_cmd add added-file > /dev/null
+ ) &&
(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_cmp_info expected.info-added-file actual.info-added-file
"
test_expect_success 'info --url added-file' '
@@ -158,19 +181,20 @@ test_expect_success 'info --url 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_cmd add added-directory > /dev/null
+ ) &&
+ (
+ cd gitwc &&
+ git add added-directory
+ ) &&
(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_cmp_info expected.info-added-directory actual.info-added-directory
"
test_expect_success 'info --url added-directory' '
@@ -179,21 +203,22 @@ test_expect_success 'info --url added-directory' '
'
test_expect_success 'info added-symlink-file' "
- cd gitwc &&
+ (
+ cd gitwc &&
ln -s added-file added-symlink-file &&
- git add added-symlink-file &&
- cd .. &&
- cd svnwc &&
+ git add added-symlink-file
+ ) &&
+ (
+ 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 &&
+ svn_cmd add added-symlink-file > /dev/null
+ ) &&
(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_cmp_info expected.info-added-symlink-file \
+ actual.info-added-symlink-file
"
test_expect_success 'info --url added-symlink-file' '
@@ -202,21 +227,22 @@ test_expect_success 'info --url added-symlink-file' '
'
test_expect_success 'info added-symlink-directory' "
- cd gitwc &&
+ (
+ cd gitwc &&
ln -s added-directory added-symlink-directory &&
- git add added-symlink-directory &&
- cd .. &&
- cd svnwc &&
+ git add added-symlink-directory
+ ) &&
+ (
+ 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 &&
+ svn_cmd add added-symlink-directory > /dev/null
+ ) &&
(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_cmp_info expected.info-added-symlink-directory \
+ actual.info-added-symlink-directory
"
test_expect_success 'info --url added-symlink-directory' '
@@ -224,25 +250,18 @@ test_expect_success '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
+ (
+ cd gitwc &&
+ git rm -f file > /dev/null
+ ) &&
+ (
+ cd svnwc &&
+ svn_cmd rm --force file > /dev/null
+ ) &&
+ (cd svnwc; svn info file) >expected.info-deleted-file &&
+ (cd gitwc; git svn info file) >actual.info-deleted-file &&
+ test_cmp_info expected.info-deleted-file actual.info-deleted-file
"
test_expect_success 'info --url file (deleted)' '
@@ -251,19 +270,17 @@ test_expect_success 'info --url file (deleted)' '
'
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
+ (
+ cd gitwc &&
+ git rm -r -f directory > /dev/null
+ ) &&
+ (
+ cd svnwc &&
+ svn_cmd rm --force directory > /dev/null
+ ) &&
+ (cd svnwc; svn info directory) >expected.info-deleted-directory &&
+ (cd gitwc; git svn info directory) >actual.info-deleted-directory &&
+ test_cmp_info expected.info-deleted-directory actual.info-deleted-directory
"
test_expect_success 'info --url directory (deleted)' '
@@ -272,20 +289,17 @@ test_expect_success 'info --url directory (deleted)' '
'
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
+ (
+ cd gitwc &&
+ git rm -f symlink-file > /dev/null
+ ) &&
+ (
+ cd svnwc &&
+ svn_cmd rm --force symlink-file > /dev/null
+ ) &&
+ (cd svnwc; svn info symlink-file) >expected.info-deleted-symlink-file &&
+ (cd gitwc; git svn info symlink-file) >actual.info-deleted-symlink-file &&
+ test_cmp_info expected.info-deleted-symlink-file actual.info-deleted-symlink-file
"
test_expect_success 'info --url symlink-file (deleted)' '
@@ -294,20 +308,17 @@ test_expect_success 'info --url symlink-file (deleted)' '
'
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
+ (
+ cd gitwc &&
+ git rm -f symlink-directory > /dev/null
+ ) &&
+ (
+ cd svnwc &&
+ svn_cmd rm --force symlink-directory > /dev/null
+ ) &&
+ (cd svnwc; svn info symlink-directory) >expected.info-deleted-symlink-directory &&
+ (cd gitwc; git svn info symlink-directory) >actual.info-deleted-symlink-directory &&
+ test_cmp_info expected.info-deleted-symlink-directory actual.info-deleted-symlink-directory
"
test_expect_success 'info --url symlink-directory (deleted)' '
@@ -346,9 +357,10 @@ test_expect_success 'info --url unknown-directory' '
'
test_expect_success 'info unknown-symlink-file' "
- cd gitwc &&
- ln -s unknown-file unknown-symlink-file &&
- cd .. &&
+ (
+ cd gitwc &&
+ ln -s unknown-file unknown-symlink-file
+ ) &&
(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
@@ -361,9 +373,10 @@ test_expect_success 'info --url unknown-symlink-file' '
'
test_expect_success 'info unknown-symlink-directory' "
- cd gitwc &&
- ln -s unknown-directory unknown-symlink-directory &&
- cd .. &&
+ (
+ cd gitwc &&
+ ln -s unknown-directory unknown-symlink-directory
+ ) &&
(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
diff --git a/t/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh
index 9d9ebd5..59465b1 100755
--- a/t/t9120-git-svn-clone-with-percent-escapes.sh
+++ b/t/t9120-git-svn-clone-with-percent-escapes.sh
@@ -20,9 +20,10 @@ test_expect_success 'setup svnrepo' '
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 ..
+ (
+ cd clone &&
+ git rev-parse refs/remotes/git-svn
+ )
'
# SVN works either way, so should we...
@@ -41,7 +42,7 @@ 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}
+ git rev-parse refs/remotes/git-svn
)
'
@@ -49,7 +50,7 @@ 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}
+ git rev-parse refs/remotes/git-svn
)
'
@@ -57,19 +58,19 @@ 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
+ git rev-parse refs/remotes/origin/trunk^0 &&
+ git rev-parse refs/remotes/origin/b^0 &&
+ git rev-parse refs/remotes/origin/tags/v1^0
)
'
test_expect_success 'test clone -s with unescaped space' '
- git svn clone -s "$svnrepo/pr ject" space &&
+ git svn clone -s "$svnrepo/pr ject" --prefix origin/ 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
+ git rev-parse refs/remotes/origin/trunk^0 &&
+ git rev-parse refs/remotes/origin/b^0 &&
+ git rev-parse refs/remotes/origin/tags/v1^0
)
'
diff --git a/t/t9123-git-svn-rebuild-with-rewriteroot.sh b/t/t9123-git-svn-rebuild-with-rewriteroot.sh
index 0455216..ead4045 100755
--- a/t/t9123-git-svn-rebuild-with-rewriteroot.sh
+++ b/t/t9123-git-svn-rebuild-with-rewriteroot.sh
@@ -8,16 +8,16 @@ test_description='git svn respects rewriteRoot during rebuild'
. ./lib-git-svn.sh
mkdir import
-cd 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}
+ git svn fetch &&
+ git checkout -b mybranch remotes/git-svn
'
test_expect_success 'remove rev_map' '
diff --git a/t/t9124-git-svn-dcommit-auto-props.sh b/t/t9124-git-svn-dcommit-auto-props.sh
index d6b076f..9f7231d 100755
--- a/t/t9124-git-svn-dcommit-auto-props.sh
+++ b/t/t9124-git-svn-dcommit-auto-props.sh
@@ -24,7 +24,7 @@ test_expect_success 'initialize git svn' '
svn_cmd import -m "import for git svn" . "$svnrepo"
) &&
rm -rf import &&
- git svn init "$svnrepo"
+ git svn init "$svnrepo" &&
git svn fetch
'
@@ -34,8 +34,7 @@ test_expect_success 'enable auto-props config' '
'
test_expect_success 'add files matching auto-props' '
- echo "#!$SHELL_PATH" >exec1.sh &&
- chmod +x exec1.sh &&
+ write_script exec1.sh </dev/null &&
echo "hello" >hello.txt &&
echo bar >bar &&
git add exec1.sh hello.txt bar &&
@@ -48,8 +47,7 @@ test_expect_success 'disable auto-props config' '
'
test_expect_success 'add files matching disabled auto-props' '
- echo "#$SHELL_PATH" >exec2.sh &&
- chmod +x exec2.sh &&
+ write_script exec2.sh </dev/null &&
echo "world" >world.txt &&
echo zot >zot &&
git add exec2.sh world.txt zot &&
@@ -65,7 +63,10 @@ test_expect_success 'check resulting svn repository' '
cd svnrepo &&
# Check properties from first commit.
- test "x$(svn_cmd propget svn:executable exec1.sh)" = "x*" &&
+ if test_have_prereq POSIXPERM
+ then
+ test "x$(svn_cmd propget svn:executable exec1.sh)" = "x*"
+ fi &&
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" &&
@@ -73,7 +74,10 @@ test_expect_success 'check resulting svn repository' '
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*" &&
+ if test_have_prereq POSIXPERM
+ then
+ test "x$(svn_cmd propget svn:executable exec2.sh)" = "x*"
+ fi &&
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" &&
diff --git a/t/t9125-git-svn-multi-glob-branch-names.sh b/t/t9125-git-svn-multi-glob-branch-names.sh
index c194186..0d53fc9 100755
--- a/t/t9125-git-svn-multi-glob-branch-names.sh
+++ b/t/t9125-git-svn-multi-glob-branch-names.sh
@@ -19,19 +19,19 @@ test_expect_success 'setup svnrepo' '
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 ..
+ (cd project &&
+ git rev-parse "refs/remotes/origin/v14.1/beta" &&
+ git rev-parse "refs/remotes/origin/v14.1/gold"
+ )
'
test_expect_success 'test dcommit to multi-globbed branch' "
- cd project &&
- git reset --hard 'refs/remotes/v14.1/gold' &&
+ (cd project &&
+ git reset --hard 'refs/remotes/origin/v14.1/gold' &&
echo hello >> foo &&
git commit -m 'hello' -- foo &&
- git svn dcommit &&
- cd ..
+ git svn dcommit
+ )
"
test_done
diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
index 4aab8ec..2e4789d 100755
--- a/t/t9127-git-svn-partial-rebuild.sh
+++ b/t/t9127-git-svn-partial-rebuild.sh
@@ -9,27 +9,27 @@ test_description='git svn partial-rebuild tests'
test_expect_success 'initialize svnrepo' '
mkdir import &&
(
- cd import &&
+ (cd import &&
mkdir trunk branches tags &&
- cd trunk &&
- echo foo > foo &&
- cd .. &&
+ (cd trunk &&
+ echo foo > foo
+ ) &&
svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null &&
svn_cmd copy "$svnrepo"/trunk "$svnrepo"/branches/a \
- -m "created branch a" &&
- cd .. &&
+ -m "created branch a"
+ ) &&
rm -rf import &&
svn_cmd co "$svnrepo"/trunk trunk &&
- cd trunk &&
+ (cd trunk &&
echo bar >> foo &&
- svn_cmd ci -m "updated trunk" &&
- cd .. &&
+ svn_cmd ci -m "updated trunk"
+ ) &&
svn_cmd co "$svnrepo"/branches/a a &&
- cd a &&
+ (cd a &&
echo baz >> a &&
svn_cmd add a &&
- svn_cmd ci -m "updated a" &&
- cd .. &&
+ svn_cmd ci -m "updated a"
+ ) &&
git svn init --stdlayout "$svnrepo"
)
'
@@ -41,11 +41,11 @@ test_expect_success 'import an early SVN revision into git' '
test_expect_success 'make full git mirror of SVN' '
mkdir mirror &&
(
- cd mirror &&
+ (cd mirror &&
git init &&
git svn init --stdlayout "$svnrepo" &&
- git svn fetch &&
- cd ..
+ git svn fetch
+ )
)
'
diff --git a/t/t9128-git-svn-cmd-branch.sh b/t/t9128-git-svn-cmd-branch.sh
index 807e494..4e95f79 100755
--- a/t/t9128-git-svn-cmd-branch.sh
+++ b/t/t9128-git-svn-cmd-branch.sh
@@ -9,19 +9,19 @@ test_description='git svn partial-rebuild tests'
test_expect_success 'initialize svnrepo' '
mkdir import &&
(
- cd 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 .. &&
+ (cd trunk &&
+ echo foo > foo
+ ) &&
+ svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null
+ ) &&
rm -rf import &&
svn_cmd co "$svnrepo"/trunk trunk &&
- cd trunk &&
+ (cd trunk &&
echo bar >> foo &&
- svn_cmd ci -m "updated trunk" &&
- cd .. &&
+ svn_cmd ci -m "updated trunk"
+ ) &&
rm -rf trunk
)
'
@@ -29,30 +29,30 @@ test_expect_success 'initialize svnrepo' '
test_expect_success 'import into git' '
git svn init --stdlayout "$svnrepo" &&
git svn fetch &&
- git checkout remotes/trunk
+ git checkout remotes/origin/trunk
'
test_expect_success 'git svn branch tests' '
git svn branch a &&
base=$(git rev-parse HEAD:) &&
- test $base = $(git rev-parse remotes/a:) &&
+ test $base = $(git rev-parse remotes/origin/a:) &&
git svn branch -m "created branch b blah" b &&
- test $base = $(git rev-parse remotes/b:) &&
+ test $base = $(git rev-parse remotes/origin/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 rev-parse remotes/origin/c &&
test_must_fail git svn branch a &&
git svn branch -t tag1 &&
- test $base = $(git rev-parse remotes/tags/tag1:) &&
+ test $base = $(git rev-parse remotes/origin/tags/tag1:) &&
git svn branch --tag tag2 &&
- test $base = $(git rev-parse remotes/tags/tag2:) &&
+ test $base = $(git rev-parse remotes/origin/tags/tag2:) &&
git svn tag tag3 &&
- test $base = $(git rev-parse remotes/tags/tag3:) &&
+ test $base = $(git rev-parse remotes/origin/tags/tag3:) &&
git svn tag -m "created tag4 foo" tag4 &&
- test $base = $(git rev-parse remotes/tags/tag4:) &&
+ test $base = $(git rev-parse remotes/origin/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 rev-parse remotes/origin/tags/tag5 &&
test_must_fail git svn tag tag1
'
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index 8cfdfe7..8dbd647 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -7,29 +7,19 @@ test_description='git svn honors i18n.commitEncoding in config'
. ./lib-git-svn.sh
compare_git_head_with () {
- nr=`wc -l < "$1"`
+ 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
+prepare_a_utf8_locale
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 '
+ LC_ALL="$a_utf8_locale" svn log $(git svn info --url) | perl -w -e '
use bytes;
$/ = ("-"x72) . "\n";
my @x = <STDIN>;
diff --git a/t/t9130-git-svn-authors-file.sh b/t/t9130-git-svn-authors-file.sh
index 134411e..4126481 100755
--- a/t/t9130-git-svn-authors-file.sh
+++ b/t/t9130-git-svn-authors-file.sh
@@ -20,13 +20,13 @@ test_expect_success 'setup svnrepo' '
'
test_expect_success 'start import with incomplete authors file' '
- ! git svn clone --authors-file=svn-authors "$svnrepo" x
+ test_must_fail 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 &&
+ 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 | \
@@ -43,7 +43,7 @@ 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 &&
+ 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 | \
@@ -63,18 +63,18 @@ test_expect_success 'authors-file against globs' '
'
test_expect_success 'fetch fails on ee' '
- ( cd aa-work && ! git svn fetch --authors-file=../svn-authors )
+ ( cd aa-work && test_must_fail git svn fetch --authors-file=../svn-authors )
'
tmp_config_get () {
- GIT_CONFIG=.git/svn/.metadata git config --get "$1"
+ git config --file=.git/svn/.metadata --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`"
+ test 6 -eq "$(tmp_config_get svn-remote.svn.branches-maxRev)" &&
+ test 6 -eq "$(tmp_config_get svn-remote.svn.tags-maxRev)"
)
'
@@ -86,21 +86,17 @@ 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 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' '
+test_expect_success !MINGW '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 &&
+ sane_unset GIT_DIR &&
git config --global \
svn.authorsfile "$HOME"/svn-authors &&
test x"$HOME"/svn-authors = x"$(git config svn.authorsfile)" &&
diff --git a/t/t9131-git-svn-empty-symlink.sh b/t/t9131-git-svn-empty-symlink.sh
index 9a24a65..f762038 100755
--- a/t/t9131-git-svn-empty-symlink.sh
+++ b/t/t9131-git-svn-empty-symlink.sh
@@ -88,7 +88,7 @@ test_expect_success 'enable broken symlink workaround' \
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 SYMLINKS '"bar" becomes a symlink' 'test -h x/bar'
test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" y'
diff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh
index 6c4c90b..aeceffa 100755
--- a/t/t9132-git-svn-broken-symlink.sh
+++ b/t/t9132-git-svn-broken-symlink.sh
@@ -87,7 +87,7 @@ 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`")
+ (cd x && test xasdf = x"$(git cat-file blob HEAD:bar)")
'
test_expect_success 'get "bar" => symlink fix from svn' '
@@ -96,7 +96,7 @@ test_expect_success 'get "bar" => symlink fix from svn' '
test_expect_success SYMLINKS '"bar" remains a proper symlink' '
test -L x/bar &&
- (cd x && test xdoink = x"`git cat-file blob HEAD:bar`")
+ (cd x && test xdoink = x"$(git cat-file blob HEAD:bar)")
'
test_done
diff --git a/t/t9135-git-svn-moved-branch-empty-file.sh b/t/t9135-git-svn-moved-branch-empty-file.sh
index 5280e5f..93db45d 100755
--- a/t/t9135-git-svn-moved-branch-empty-file.sh
+++ b/t/t9135-git-svn-moved-branch-empty-file.sh
@@ -12,7 +12,7 @@ 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 &&
+ git reset --hard origin/branch-c &&
test -f b1 &&
! test -s b1
)
diff --git a/t/t9137-git-svn-dcommit-clobber-series.sh b/t/t9137-git-svn-dcommit-clobber-series.sh
index 636ca0a..5fa07a3 100755
--- a/t/t9137-git-svn-dcommit-clobber-series.sh
+++ b/t/t9137-git-svn-dcommit-clobber-series.sh
@@ -6,27 +6,27 @@ test_description='git svn dcommit clobber series'
test_expect_success 'initialize repo' '
mkdir import &&
- cd import &&
+ (cd import &&
awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file
- svn_cmd import -m "initial" . "$svnrepo" &&
- cd .. &&
+ svn_cmd import -m "initial" . "$svnrepo"
+ ) &&
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 &&
+ test x"$(sed -n -e 58p < file)" = x58 &&
+ test x"$(sed -n -e 61p < file)" = x61 &&
svn_cmd co "$svnrepo" tmp &&
- cd 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 x"$(sed -n -e 58p < file)" = x5588 &&
+ test x"$(sed -n -e 61p < file)" = x6611 &&
+ svn_cmd commit -m "58 => 5588, 61 => 6611"
+ )
'
test_expect_success 'some unrelated changes to git' "
@@ -38,20 +38,20 @@ test_expect_success 'some unrelated changes to git' "
"
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 &&
+ 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 &&
+ 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 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' '
diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh
index 83cc5fc..7d7e9d4 100755
--- a/t/t9138-git-svn-authors-prog.sh
+++ b/t/t9138-git-svn-authors-prog.sh
@@ -7,40 +7,39 @@ 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";
-}
+write_script svn-authors-prog "$PERL_PATH" <<-\EOF
+ $_ = 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 'svn-authors setup' '
+ 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 "$(git rev-list refs/remotes/git-svn | wc -l)" -eq 6
)
- '
+'
test_expect_success 'authors-prog ran correctly' '
(
@@ -56,7 +55,7 @@ test_expect_success 'authors-prog ran correctly' '
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' '
(
@@ -64,7 +63,7 @@ test_expect_success 'authors-file overrode authors-prog' '
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
diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh
index f337959..22d80b0 100755
--- a/t/t9139-git-svn-non-utf8-commitencoding.sh
+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh
@@ -39,7 +39,7 @@ do
(
cd $H &&
git config --unset i18n.commitencoding &&
- ! git svn dcommit
+ test_must_fail git svn dcommit
)
'
done
diff --git a/t/t9140-git-svn-reset.sh b/t/t9140-git-svn-reset.sh
index 0735526..e855904 100755
--- a/t/t9140-git-svn-reset.sh
+++ b/t/t9140-git-svn-reset.sh
@@ -41,7 +41,7 @@ test_expect_success 'modify hidden file in SVN repo' '
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 &&
+ test_must_fail git svn fetch 2> ../errors &&
git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
fgrep "not found in commit" errors &&
test_cmp expect expect2
diff --git a/t/t9141-git-svn-multiple-branches.sh b/t/t9141-git-svn-multiple-branches.sh
index 3cd0671..8e7f7d6 100755
--- a/t/t9141-git-svn-multiple-branches.sh
+++ b/t/t9141-git-svn-multiple-branches.sh
@@ -66,18 +66,18 @@ test_expect_success 'clone multiple branch and tag paths' '
-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
+ git rev-parse refs/remotes/origin/first &&
+ git rev-parse refs/remotes/origin/second &&
+ git rev-parse refs/remotes/origin/1 &&
+ git rev-parse refs/remotes/origin/2 &&
+ git rev-parse refs/remotes/origin/tags/1.0 &&
+ git rev-parse refs/remotes/origin/tags/2.0 &&
+ git rev-parse refs/remotes/origin/tags/3.0 &&
+ git rev-parse refs/remotes/origin/tags/4.0 &&
+ git rev-parse refs/remotes/origin/tags/v5 &&
+ git rev-parse refs/remotes/origin/tags/v6 &&
+ git rev-parse refs/remotes/origin/tags/v7 &&
+ git rev-parse refs/remotes/origin/tags/v8
)
'
@@ -85,8 +85,8 @@ 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
+ test_must_fail git rev-parse refs/remotes/origin/Nope &&
+ test_must_fail git rev-parse refs/remotes/origin/tags/Tagless
) &&
( cd svn_project &&
svn_cmd up &&
diff --git a/t/t9142-git-svn-shallow-clone.sh b/t/t9142-git-svn-shallow-clone.sh
index 1236acc..e21ee5f 100755
--- a/t/t9142-git-svn-shallow-clone.sh
+++ b/t/t9142-git-svn-shallow-clone.sh
@@ -17,11 +17,10 @@ test_expect_success 'setup test repository' '
> foo &&
svn_cmd add foo &&
svn_cmd commit -m "add foo"
- )
+ ) &&
+ start_httpd
'
-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 )
diff --git a/t/t9143-git-svn-gc.sh b/t/t9143-git-svn-gc.sh
index 337ea59..4594e1a 100755
--- a/t/t9143-git-svn-gc.sh
+++ b/t/t9143-git-svn-gc.sh
@@ -37,13 +37,11 @@ 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
+if test -r .git/svn/refs/remotes/git-svn/unhandled.log.gz
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' '
diff --git a/t/t9145-git-svn-master-branch.sh b/t/t9145-git-svn-master-branch.sh
index 16852d2..3bbf341 100755
--- a/t/t9145-git-svn-master-branch.sh
+++ b/t/t9145-git-svn-master-branch.sh
@@ -17,8 +17,8 @@ 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 x$(git rev-parse --verify refs/remotes/origin/trunk^0) = \
+ x$(git rev-parse --verify refs/heads/master^0)
)
'
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
index 565365c..6d3130e 100755
--- a/t/t9146-git-svn-empty-dirs.sh
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -28,12 +28,29 @@ test_expect_success 'empty directories exist' '
)
'
+test_expect_success 'option automkdirs set to false' '
+ (
+ git svn init "$svnrepo" cloned-no-mkdirs &&
+ cd cloned-no-mkdirs &&
+ git config svn-remote.svn.automkdirs false &&
+ git svn fetch &&
+ for i in a b c d d/e d/e/f "weird file name"
+ do
+ if test -d "$i"
+ then
+ echo >&2 "$i exists"
+ 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 )
+ ( cd cloned && git svn rebase ) &&
test -d cloned/"! !"
'
diff --git a/t/t9147-git-svn-include-paths.sh b/t/t9147-git-svn-include-paths.sh
new file mode 100755
index 0000000..a90ff58
--- /dev/null
+++ b/t/t9147-git-svn-include-paths.sh
@@ -0,0 +1,149 @@
+#!/bin/sh
+#
+# Copyright (c) 2013 Paul Walmsley - based on t9134 by Vitaly Shukela
+#
+
+test_description='git svn property tests'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository' '
+ svn_cmd co "$svnrepo" s &&
+ (
+ cd s &&
+ mkdir qqq www xxx &&
+ echo test_qqq > qqq/test_qqq.txt &&
+ echo test_www > www/test_www.txt &&
+ echo test_xxx > xxx/test_xxx.txt &&
+ svn_cmd add qqq &&
+ svn_cmd add www &&
+ svn_cmd add xxx &&
+ svn_cmd commit -m "create some files" &&
+ svn_cmd up &&
+ echo hi >> www/test_www.txt &&
+ svn_cmd commit -m "modify www/test_www.txt" &&
+ svn_cmd up
+ )
+'
+
+test_expect_success 'clone an SVN repository with filter to include qqq directory' '
+ git svn clone --include-paths="qqq" "$svnrepo" g &&
+ echo test_qqq > expect &&
+ for i in g/*/*.txt; do cat $i >> expect2; done &&
+ test_cmp expect expect2
+'
+
+
+test_expect_success 'init+fetch an SVN repository with included qqq directory' '
+ git svn init "$svnrepo" c &&
+ ( cd c && git svn fetch --include-paths="qqq" ) &&
+ rm expect2 &&
+ echo test_qqq > expect &&
+ for i in c/*/*.txt; do cat $i >> expect2; done &&
+ test_cmp expect expect2
+'
+
+test_expect_success 'verify include-paths config saved by clone' '
+ (
+ cd g &&
+ git config --get svn-remote.svn.include-paths | fgrep "qqq"
+ )
+'
+
+test_expect_success 'SVN-side change outside of www' '
+ (
+ cd s &&
+ echo b >> qqq/test_qqq.txt &&
+ svn_cmd commit -m "SVN-side change outside of www" &&
+ svn_cmd up &&
+ svn_cmd log -v | fgrep "SVN-side change outside of www"
+ )
+'
+
+test_expect_success 'update git svn-cloned repo (config include)' '
+ (
+ cd g &&
+ git svn rebase &&
+ printf "test_qqq\nb\n" > expect &&
+ for i in */*.txt; do cat $i >> expect2; done &&
+ test_cmp expect2 expect &&
+ rm expect expect2
+ )
+'
+
+test_expect_success 'update git svn-cloned repo (option include)' '
+ (
+ cd c &&
+ git svn rebase --include-paths="qqq" &&
+ printf "test_qqq\nb\n" > expect &&
+ for i in */*.txt; do cat $i >> expect2; done &&
+ test_cmp expect2 expect &&
+ rm expect expect2
+ )
+'
+
+test_expect_success 'SVN-side change inside of ignored www' '
+ (
+ cd s &&
+ echo zaq >> www/test_www.txt
+ svn_cmd commit -m "SVN-side change inside of www/test_www.txt" &&
+ svn_cmd up &&
+ svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt"
+ )
+'
+
+test_expect_success 'update git svn-cloned repo (config include)' '
+ (
+ cd g &&
+ git svn rebase &&
+ printf "test_qqq\nb\n" > expect &&
+ for i in */*.txt; do cat $i >> expect2; done &&
+ test_cmp expect2 expect &&
+ rm expect expect2
+ )
+'
+
+test_expect_success 'update git svn-cloned repo (option include)' '
+ (
+ cd c &&
+ git svn rebase --include-paths="qqq" &&
+ printf "test_qqq\nb\n" > expect &&
+ for i in */*.txt; do cat $i >> expect2; done &&
+ test_cmp expect2 expect &&
+ rm expect expect2
+ )
+'
+
+test_expect_success 'SVN-side change in and out of included qqq' '
+ (
+ cd s &&
+ echo cvf >> www/test_www.txt
+ echo ygg >> qqq/test_qqq.txt
+ svn_cmd commit -m "SVN-side change in and out of ignored www" &&
+ svn_cmd up &&
+ svn_cmd log -v | fgrep "SVN-side change in and out of ignored www"
+ )
+'
+
+test_expect_success 'update git svn-cloned repo again (config include)' '
+ (
+ cd g &&
+ git svn rebase &&
+ printf "test_qqq\nb\nygg\n" > expect &&
+ for i in */*.txt; do cat $i >> expect2; done &&
+ test_cmp expect2 expect &&
+ rm expect expect2
+ )
+'
+
+test_expect_success 'update git svn-cloned repo again (option include)' '
+ (
+ cd c &&
+ git svn rebase --include-paths="qqq" &&
+ printf "test_qqq\nb\nygg\n" > expect &&
+ for i in */*.txt; do cat $i >> expect2; done &&
+ test_cmp expect2 expect &&
+ rm expect expect2
+ )
+'
+
+test_done
diff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh
new file mode 100755
index 0000000..1026390
--- /dev/null
+++ b/t/t9148-git-svn-propset.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 Alfred Perlstein
+#
+
+test_description='git svn propset tests'
+
+. ./lib-git-svn.sh
+
+foo_subdir2="subdir/subdir2/foo_subdir2"
+
+set -e
+mkdir import &&
+(set -e ; cd import
+ mkdir subdir
+ mkdir subdir/subdir2
+ touch foo # for 'add props top level'
+ touch subdir/foo_subdir # for 'add props relative'
+ touch "$foo_subdir2" # for 'add props subdir'
+ svn_cmd import -m 'import for git svn' . "$svnrepo" >/dev/null
+)
+rm -rf import
+
+test_expect_success 'initialize git svn' '
+ git svn init "$svnrepo"
+ '
+
+test_expect_success 'fetch revisions from svn' '
+ git svn fetch
+ '
+
+set_props () {
+ subdir="$1"
+ file="$2"
+ shift;shift;
+ (cd "$subdir" &&
+ while [ $# -gt 0 ] ; do
+ git svn propset "$1" "$2" "$file" || exit 1
+ shift;shift;
+ done &&
+ echo hello >> "$file" &&
+ git commit -m "testing propset" "$file")
+}
+
+confirm_props () {
+ subdir="$1"
+ file="$2"
+ shift;shift;
+ (set -e ; cd "svn_project/$subdir" &&
+ while [ $# -gt 0 ] ; do
+ test "$(svn_cmd propget "$1" "$file")" = "$2" || exit 1
+ shift;shift;
+ done)
+}
+
+
+#The current implementation has a restriction:
+#svn propset will be taken as a delta for svn dcommit only
+#if the file content is also modified
+test_expect_success 'add props top level' '
+ set_props "." "foo" "svn:keywords" "FreeBSD=%H" &&
+ git svn dcommit &&
+ svn_cmd co "$svnrepo" svn_project &&
+ confirm_props "." "foo" "svn:keywords" "FreeBSD=%H" &&
+ rm -rf svn_project
+ '
+
+test_expect_success 'add multiple props' '
+ set_props "." "foo" \
+ "svn:keywords" "FreeBSD=%H" fbsd:nokeywords yes &&
+ git svn dcommit &&
+ svn_cmd co "$svnrepo" svn_project &&
+ confirm_props "." "foo" \
+ "svn:keywords" "FreeBSD=%H" fbsd:nokeywords yes &&
+ rm -rf svn_project
+ '
+
+test_expect_success 'add props subdir' '
+ set_props "." "$foo_subdir2" svn:keywords "FreeBSD=%H" &&
+ git svn dcommit &&
+ svn_cmd co "$svnrepo" svn_project &&
+ confirm_props "." "$foo_subdir2" "svn:keywords" "FreeBSD=%H" &&
+ rm -rf svn_project
+ '
+
+test_expect_success 'add props relative' '
+ set_props "subdir/subdir2" "../foo_subdir" \
+ svn:keywords "FreeBSD=%H" &&
+ git svn dcommit &&
+ svn_cmd co "$svnrepo" svn_project &&
+ confirm_props "subdir/subdir2" "../foo_subdir" \
+ svn:keywords "FreeBSD=%H" &&
+ rm -rf svn_project
+ '
+test_done
diff --git a/t/t9150-svk-mergetickets.sh b/t/t9150-svk-mergetickets.sh
index 24c2421..1bb676b 100755
--- a/t/t9150-svk-mergetickets.sh
+++ b/t/t9150-svk-mergetickets.sh
@@ -19,7 +19,7 @@ test_expect_success 'load svk depot' "
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 ]
+ [ $(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
index 2242f14..2242f14 100644..100755
--- a/t/t9150/make-svk-dump
+++ b/t/t9150/make-svk-dump
diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
index 250c651..4f6c06e 100755
--- a/t/t9151-svn-mergeinfo.sh
+++ b/t/t9151-svn-mergeinfo.sh
@@ -18,39 +18,39 @@ test_expect_success 'load svn dump' "
test_expect_success 'all svn merges became git merge commits' '
unmarked=$(git rev-list --parents --all --grep=Merge |
- grep -v " .* " | cut -f1 -d" ")
+ 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" ")
+ 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" ")
+ 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)
+ 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)
+ 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)
+ unmerged=$(git rev-list --all --not master) &&
[ -z "$unmerged" ]
'
diff --git a/t/t9151/make-svnmerge-dump b/t/t9151/make-svnmerge-dump
index e1e138c..e1e138c 100644..100755
--- a/t/t9151/make-svnmerge-dump
+++ b/t/t9151/make-svnmerge-dump
diff --git a/t/t9153-git-svn-rewrite-uuid.sh b/t/t9153-git-svn-rewrite-uuid.sh
index 88a2cfa..372ef15 100755
--- a/t/t9153-git-svn-rewrite-uuid.sh
+++ b/t/t9153-git-svn-rewrite-uuid.sh
@@ -17,9 +17,9 @@ test_expect_success 'load svn repo' "
test_expect_success 'verify uuid' "
git cat-file commit refs/remotes/git-svn~0 | \
- grep '^${git_svn_id}: .*@2 $uuid$' &&
+ grep '^git-svn-id: .*@2 $uuid$' &&
git cat-file commit refs/remotes/git-svn~1 | \
- grep '^${git_svn_id}: .*@1 $uuid$'
+ grep '^git-svn-id: .*@1 $uuid$'
"
test_done
diff --git a/t/t9154-git-svn-fancy-glob.sh b/t/t9154-git-svn-fancy-glob.sh
index a6a56a6..a0150f0 100755
--- a/t/t9154-git-svn-fancy-glob.sh
+++ b/t/t9154-git-svn-fancy-glob.sh
@@ -21,8 +21,17 @@ test_expect_success 'add red branch' "
test_must_fail git rev-parse refs/remotes/blue
"
+test_expect_success 'add gre branch' "
+ git config --file=.git/svn/.metadata --unset svn-remote.svn.branches-maxRev &&
+ git config svn-remote.svn.branches 'branches/{red,gre}:refs/remotes/*' &&
+ git svn fetch &&
+ git rev-parse refs/remotes/red &&
+ test_must_fail git rev-parse refs/remotes/green &&
+ test_must_fail git rev-parse refs/remotes/blue
+ "
+
test_expect_success 'add green branch' "
- GIT_CONFIG=.git/svn/.metadata git config --unset svn-remote.svn.branches-maxRev &&
+ git config --file=.git/svn/.metadata --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 &&
@@ -31,7 +40,7 @@ test_expect_success 'add green branch' "
"
test_expect_success 'add all branches' "
- GIT_CONFIG=.git/svn/.metadata git config --unset svn-remote.svn.branches-maxRev &&
+ git config --file=.git/svn/.metadata --unset svn-remote.svn.branches-maxRev &&
git config svn-remote.svn.branches 'branches/*:refs/remotes/*' &&
git svn fetch &&
git rev-parse refs/remotes/red &&
diff --git a/t/t9155-git-svn-fetch-deleted-tag.sh b/t/t9155-git-svn-fetch-deleted-tag.sh
new file mode 100755
index 0000000..184336f
--- /dev/null
+++ b/t/t9155-git-svn-fetch-deleted-tag.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='git svn fetch deleted tag'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svn repo' '
+ mkdir -p import/trunk/subdir &&
+ mkdir -p import/branches &&
+ mkdir -p import/tags &&
+ echo "base" >import/trunk/subdir/file &&
+ svn_cmd import -m "import for git svn" import "$svnrepo" &&
+ rm -rf import &&
+
+ svn_cmd mkdir -m "create mybranch directory" "$svnrepo/branches/mybranch" &&
+ svn_cmd cp -m "create branch mybranch" "$svnrepo/trunk" "$svnrepo/branches/mybranch/trunk" &&
+
+ svn_cmd co "$svnrepo/trunk" svn_project &&
+ (cd svn_project &&
+ echo "trunk change" >>subdir/file &&
+ svn_cmd ci -m "trunk change" subdir/file &&
+
+ svn_cmd switch "$svnrepo/branches/mybranch/trunk" &&
+ echo "branch change" >>subdir/file &&
+ svn_cmd ci -m "branch change" subdir/file
+ ) &&
+
+ svn_cmd cp -m "create mytag attempt 1" -r5 "$svnrepo/trunk/subdir" "$svnrepo/tags/mytag" &&
+ svn_cmd rm -m "delete mytag attempt 1" "$svnrepo/tags/mytag" &&
+ svn_cmd cp -m "create mytag attempt 2" -r5 "$svnrepo/branches/mybranch/trunk/subdir" "$svnrepo/tags/mytag"
+'
+
+test_expect_success 'fetch deleted tags from same revision with checksum error' '
+ git svn init --stdlayout "$svnrepo" git_project &&
+ cd git_project &&
+ git svn fetch &&
+
+ git diff --exit-code origin/mybranch:trunk/subdir/file origin/tags/mytag:file &&
+ git diff --exit-code master:subdir/file origin/tags/mytag^:file
+'
+
+test_done
diff --git a/t/t9156-git-svn-fetch-deleted-tag-2.sh b/t/t9156-git-svn-fetch-deleted-tag-2.sh
new file mode 100755
index 0000000..7a6e33b
--- /dev/null
+++ b/t/t9156-git-svn-fetch-deleted-tag-2.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+test_description='git svn fetch deleted tag 2'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svn repo' '
+ mkdir -p import/branches &&
+ mkdir -p import/tags &&
+ mkdir -p import/trunk/subdir1 &&
+ mkdir -p import/trunk/subdir2 &&
+ mkdir -p import/trunk/subdir3 &&
+ echo "file1" >import/trunk/subdir1/file &&
+ echo "file2" >import/trunk/subdir2/file &&
+ echo "file3" >import/trunk/subdir3/file &&
+ svn_cmd import -m "import for git svn" import "$svnrepo" &&
+ rm -rf import &&
+
+ svn_cmd co "$svnrepo/trunk" svn_project &&
+ (cd svn_project &&
+ echo "change1" >>subdir1/file &&
+ echo "change2" >>subdir2/file &&
+ echo "change3" >>subdir3/file &&
+ svn_cmd ci -m "change" .
+ ) &&
+
+ svn_cmd cp -m "create mytag 1" -r2 "$svnrepo/trunk/subdir1" "$svnrepo/tags/mytag" &&
+ svn_cmd rm -m "delete mytag 1" "$svnrepo/tags/mytag" &&
+ svn_cmd cp -m "create mytag 2" -r2 "$svnrepo/trunk/subdir2" "$svnrepo/tags/mytag" &&
+ svn_cmd rm -m "delete mytag 2" "$svnrepo/tags/mytag" &&
+ svn_cmd cp -m "create mytag 3" -r2 "$svnrepo/trunk/subdir3" "$svnrepo/tags/mytag"
+'
+
+test_expect_success 'fetch deleted tags from same revision with no checksum error' '
+ git svn init --stdlayout "$svnrepo" git_project &&
+ cd git_project &&
+ git svn fetch &&
+
+ git diff --exit-code master:subdir3/file origin/tags/mytag:file &&
+ git diff --exit-code master:subdir2/file origin/tags/mytag^:file &&
+ git diff --exit-code master:subdir1/file origin/tags/mytag^^:file
+'
+
+test_done
diff --git a/t/t9157-git-svn-fetch-merge.sh b/t/t9157-git-svn-fetch-merge.sh
new file mode 100755
index 0000000..991d2aa
--- /dev/null
+++ b/t/t9157-git-svn-fetch-merge.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Steven Walter
+#
+
+test_description='git svn merge detection'
+. ./lib-git-svn.sh
+
+svn_ver="$(svn --version --quiet)"
+case $svn_ver in
+0.* | 1.[0-4].*)
+ skip_all="skipping git-svn test - SVN too old ($svn_ver)"
+ test_done
+ ;;
+esac
+
+test_expect_success 'initialize source svn repo' '
+ svn_cmd mkdir -m x "$svnrepo"/trunk &&
+ svn_cmd mkdir -m x "$svnrepo"/branches &&
+ svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
+ (
+ cd "$SVN_TREE" &&
+ touch foo &&
+ svn add foo &&
+ svn commit -m "initial commit" &&
+ svn cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch1 &&
+ touch bar &&
+ svn add bar &&
+ svn commit -m x &&
+ svn cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch2 &&
+ svn switch "$svnrepo"/branches/branch1 &&
+ touch baz &&
+ svn add baz &&
+ svn commit -m x &&
+ svn switch "$svnrepo"/trunk &&
+ svn merge "$svnrepo"/branches/branch1 &&
+ svn commit -m "merge" &&
+ svn switch "$svnrepo"/branches/branch1 &&
+ svn commit -m x &&
+ svn switch "$svnrepo"/branches/branch2 &&
+ svn merge "$svnrepo"/branches/branch1 &&
+ svn commit -m "merge branch1" &&
+ svn switch "$svnrepo"/trunk &&
+ svn merge "$svnrepo"/branches/branch2 &&
+ svn resolved baz &&
+ svn commit -m "merge branch2"
+ ) &&
+ rm -rf "$SVN_TREE"
+'
+
+test_expect_success 'clone svn repo' '
+ git svn init -s "$svnrepo" &&
+ git svn fetch
+'
+
+test_expect_success 'verify merge commit' 'git rev-parse HEAD^2'
+
+test_done
diff --git a/t/t9158-git-svn-mergeinfo.sh b/t/t9158-git-svn-mergeinfo.sh
new file mode 100755
index 0000000..13f78f2
--- /dev/null
+++ b/t/t9158-git-svn-mergeinfo.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Steven Walter
+#
+
+test_description='git svn mergeinfo propagation'
+
+. ./lib-git-svn.sh
+
+say 'define NO_SVN_TESTS to skip git svn tests'
+
+test_expect_success 'initialize source svn repo' '
+ svn_cmd mkdir -m x "$svnrepo"/trunk &&
+ svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
+ (
+ cd "$SVN_TREE" &&
+ touch foo &&
+ svn_cmd add foo &&
+ svn_cmd commit -m "initial commit"
+ ) &&
+ rm -rf "$SVN_TREE"
+'
+
+test_expect_success 'clone svn repo' '
+ git svn init "$svnrepo"/trunk &&
+ git svn fetch
+'
+
+test_expect_success 'change svn:mergeinfo' '
+ touch bar &&
+ git add bar &&
+ git commit -m "bar" &&
+ git svn dcommit --mergeinfo="/branches/foo:1-10"
+'
+
+test_expect_success 'verify svn:mergeinfo' '
+ mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/trunk) &&
+ test "$mergeinfo" = "/branches/foo:1-10"
+'
+
+test_expect_success 'change svn:mergeinfo multiline' '
+ touch baz &&
+ git add baz &&
+ git commit -m "baz" &&
+ git svn dcommit --mergeinfo="/branches/bar:1-10 /branches/other:3-5,8,10-11"
+'
+
+test_expect_success 'verify svn:mergeinfo multiline' '
+ mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/trunk) &&
+ test "$mergeinfo" = "/branches/bar:1-10
+/branches/other:3-5,8,10-11"
+'
+
+test_done
diff --git a/t/t9159-git-svn-no-parent-mergeinfo.sh b/t/t9159-git-svn-no-parent-mergeinfo.sh
new file mode 100755
index 0000000..69e4815
--- /dev/null
+++ b/t/t9159-git-svn-no-parent-mergeinfo.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+test_description='git svn handling of root commits in merge ranges'
+. ./lib-git-svn.sh
+
+svn_ver="$(svn --version --quiet)"
+case $svn_ver in
+0.* | 1.[0-4].*)
+ skip_all="skipping git-svn test - SVN too old ($svn_ver)"
+ test_done
+ ;;
+esac
+
+test_expect_success 'test handling of root commits in merge ranges' '
+ mkdir -p init/trunk init/branches init/tags &&
+ echo "r1" > init/trunk/file.txt &&
+ svn_cmd import -m "initial import" init "$svnrepo" &&
+ svn_cmd co "$svnrepo" tmp &&
+ (
+ cd tmp &&
+ echo "r2" > trunk/file.txt &&
+ svn_cmd commit -m "Modify file.txt on trunk" &&
+ svn_cmd cp trunk@1 branches/a &&
+ svn_cmd commit -m "Create branch a from trunk r1" &&
+ svn_cmd propset svn:mergeinfo /trunk:1-2 branches/a &&
+ svn_cmd commit -m "Fake merge of trunk r2 into branch a" &&
+ mkdir branches/b &&
+ echo "r5" > branches/b/file2.txt &&
+ svn_cmd add branches/b &&
+ svn_cmd commit -m "Create branch b from thin air" &&
+ echo "r6" > branches/b/file2.txt &&
+ svn_cmd commit -m "Modify file2.txt on branch b" &&
+ svn_cmd cp branches/b@5 branches/c &&
+ svn_cmd commit -m "Create branch c from branch b r5" &&
+ svn_cmd propset svn:mergeinfo /branches/b:5-6 branches/c &&
+ svn_cmd commit -m "Fake merge of branch b r6 into branch c"
+ ) &&
+ git svn init -s "$svnrepo" &&
+ git svn fetch
+ '
+
+test_done
diff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh
new file mode 100755
index 0000000..b4a4434
--- /dev/null
+++ b/t/t9160-git-svn-preserve-empty-dirs.sh
@@ -0,0 +1,153 @@
+#!/bin/sh
+#
+# Copyright (c) 2011 Ray Chen
+#
+
+test_description='git svn test (option --preserve-empty-dirs)
+
+This test uses git to clone a Subversion repository that contains empty
+directories, and checks that corresponding directories are created in the
+local Git repository with placeholder files.'
+
+. ./lib-git-svn.sh
+
+say 'define NO_SVN_TESTS to skip git svn tests'
+GIT_REPO=git-svn-repo
+
+test_expect_success 'initialize source svn repo containing empty dirs' '
+ svn_cmd mkdir -m x "$svnrepo"/trunk &&
+ svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
+ (
+ cd "$SVN_TREE" &&
+ mkdir -p 1 2 3/a 3/b 4 5 6 &&
+ echo "First non-empty file" > 2/file1.txt &&
+ echo "Second non-empty file" > 2/file2.txt &&
+ echo "Third non-empty file" > 3/a/file1.txt &&
+ echo "Fourth non-empty file" > 3/b/file1.txt &&
+ svn_cmd add 1 2 3 4 5 6 &&
+ svn_cmd commit -m "initial commit" &&
+
+ mkdir 4/a &&
+ svn_cmd add 4/a &&
+ svn_cmd commit -m "nested empty directory" &&
+ mkdir 4/a/b &&
+ svn_cmd add 4/a/b &&
+ svn_cmd commit -m "deeply nested empty directory" &&
+ mkdir 4/a/b/c &&
+ svn_cmd add 4/a/b/c &&
+ svn_cmd commit -m "really deeply nested empty directory" &&
+ echo "Kill the placeholder file" > 4/a/b/c/foo &&
+ svn_cmd add 4/a/b/c/foo &&
+ svn_cmd commit -m "Regular file to remove placeholder" &&
+
+ svn_cmd del 2/file2.txt &&
+ svn_cmd del 3/b &&
+ svn_cmd commit -m "delete non-last entry in directory" &&
+
+ svn_cmd del 2/file1.txt &&
+ svn_cmd del 3/a &&
+ svn_cmd commit -m "delete last entry in directory" &&
+
+ echo "Conflict file" > 5/.placeholder &&
+ mkdir 6/.placeholder &&
+ svn_cmd add 5/.placeholder 6/.placeholder &&
+ svn_cmd commit -m "Placeholder Namespace conflict"
+ ) &&
+ rm -rf "$SVN_TREE"
+'
+
+test_expect_success 'clone svn repo with --preserve-empty-dirs' '
+ git svn clone "$svnrepo"/trunk --preserve-empty-dirs "$GIT_REPO"
+'
+
+# "$GIT_REPO"/1 should only contain the placeholder file.
+test_expect_success 'directory empty from inception' '
+ test -f "$GIT_REPO"/1/.gitignore &&
+ test $(find "$GIT_REPO"/1 -type f | wc -l) = "1"
+'
+
+# "$GIT_REPO"/2 and "$GIT_REPO"/3 should only contain the placeholder file.
+test_expect_success 'directory empty from subsequent svn commit' '
+ test -f "$GIT_REPO"/2/.gitignore &&
+ test $(find "$GIT_REPO"/2 -type f | wc -l) = "1" &&
+ test -f "$GIT_REPO"/3/.gitignore &&
+ test $(find "$GIT_REPO"/3 -type f | wc -l) = "1"
+'
+
+# No placeholder files should exist in "$GIT_REPO"/4, even though one was
+# generated for every sub-directory at some point in the repo's history.
+test_expect_success 'add entry to previously empty directory' '
+ test $(find "$GIT_REPO"/4 -type f | wc -l) = "1" &&
+ test -f "$GIT_REPO"/4/a/b/c/foo
+'
+
+# The HEAD~2 commit should not have introduced .gitignore placeholder files.
+test_expect_success 'remove non-last entry from directory' '
+ (
+ cd "$GIT_REPO" &&
+ git checkout HEAD~2
+ ) &&
+ test_must_fail test -f "$GIT_REPO"/2/.gitignore &&
+ test_must_fail test -f "$GIT_REPO"/3/.gitignore
+'
+
+# After re-cloning the repository with --placeholder-file specified, there
+# should be 5 files named ".placeholder" in the local Git repo.
+test_expect_success 'clone svn repo with --placeholder-file specified' '
+ rm -rf "$GIT_REPO" &&
+ git svn clone "$svnrepo"/trunk --preserve-empty-dirs \
+ --placeholder-file=.placeholder "$GIT_REPO" &&
+ find "$GIT_REPO" -type f -name ".placeholder" &&
+ test $(find "$GIT_REPO" -type f -name ".placeholder" | wc -l) = "5"
+'
+
+# "$GIT_REPO"/5/.placeholder should be a file, and non-empty.
+test_expect_success 'placeholder namespace conflict with file' '
+ test -s "$GIT_REPO"/5/.placeholder
+'
+
+# "$GIT_REPO"/6/.placeholder should be a directory, and the "$GIT_REPO"/6 tree
+# should only contain one file: the placeholder.
+test_expect_success 'placeholder namespace conflict with directory' '
+ test -d "$GIT_REPO"/6/.placeholder &&
+ test -f "$GIT_REPO"/6/.placeholder/.placeholder &&
+ test $(find "$GIT_REPO"/6 -type f | wc -l) = "1"
+'
+
+# Prepare a second set of svn commits to test persistence during rebase.
+test_expect_success 'second set of svn commits and rebase' '
+ svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
+ (
+ cd "$SVN_TREE" &&
+ mkdir -p 7 &&
+ echo "This should remove placeholder" > 1/file1.txt &&
+ echo "This should not remove placeholder" > 5/file1.txt &&
+ svn_cmd add 7 1/file1.txt 5/file1.txt &&
+ svn_cmd commit -m "subsequent svn commit for persistence tests"
+ ) &&
+ rm -rf "$SVN_TREE" &&
+ (
+ cd "$GIT_REPO" &&
+ git svn rebase
+ )
+'
+
+# Check that --preserve-empty-dirs and --placeholder-file flag state
+# stays persistent over multiple invocations.
+test_expect_success 'flag persistence during subsqeuent rebase' '
+ test -f "$GIT_REPO"/7/.placeholder &&
+ test $(find "$GIT_REPO"/7 -type f | wc -l) = "1"
+'
+
+# Check that placeholder files are properly removed when unnecessary,
+# even across multiple invocations.
+test_expect_success 'placeholder list persistence during subsqeuent rebase' '
+ test -f "$GIT_REPO"/1/file1.txt &&
+ test $(find "$GIT_REPO"/1 -type f | wc -l) = "1" &&
+
+ test -f "$GIT_REPO"/5/file1.txt &&
+ test -f "$GIT_REPO"/5/.placeholder &&
+ test $(find "$GIT_REPO"/5 -type f | wc -l) = "2"
+'
+
+test_done
diff --git a/t/t9161-git-svn-mergeinfo-push.sh b/t/t9161-git-svn-mergeinfo-push.sh
new file mode 100755
index 0000000..f113aca
--- /dev/null
+++ b/t/t9161-git-svn-mergeinfo-push.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+#
+# Portions copyright (c) 2007, 2009 Sam Vilain
+# Portions copyright (c) 2011 Bryan Jacobs
+#
+
+test_description='git-svn svn mergeinfo propagation'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'load svn dump' "
+ svnadmin load -q '$rawsvnrepo' \
+ < '$TEST_DIRECTORY/t9161/branches.dump' &&
+ git svn init --minimize-url -R svnmerge \
+ -T trunk -b branches '$svnrepo' &&
+ git svn fetch --all
+ "
+
+test_expect_success 'propagate merge information' '
+ git config svn.pushmergeinfo yes &&
+ git checkout origin/svnb1 &&
+ git merge --no-ff origin/svnb2 &&
+ git svn dcommit
+ '
+
+test_expect_success 'check svn:mergeinfo' '
+ mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1) &&
+ test "$mergeinfo" = "/branches/svnb2:3,8"
+ '
+
+test_expect_success 'merge another branch' '
+ git merge --no-ff origin/svnb3 &&
+ git svn dcommit
+ '
+
+test_expect_success 'check primary parent mergeinfo respected' '
+ mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1) &&
+ test "$mergeinfo" = "/branches/svnb2:3,8
+/branches/svnb3:4,9"
+ '
+
+test_expect_success 'merge existing merge' '
+ git merge --no-ff origin/svnb4 &&
+ git svn dcommit
+ '
+
+test_expect_success "check both parents' mergeinfo respected" '
+ mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1) &&
+ test "$mergeinfo" = "/branches/svnb2:3,8
+/branches/svnb3:4,9
+/branches/svnb4:5-6,10-12
+/branches/svnb5:6,11"
+ '
+
+test_expect_success 'make further commits to branch' '
+ git checkout origin/svnb2 &&
+ touch newb2file &&
+ git add newb2file &&
+ git commit -m "later b2 commit" &&
+ touch newb2file-2 &&
+ git add newb2file-2 &&
+ git commit -m "later b2 commit 2" &&
+ git svn dcommit
+ '
+
+test_expect_success 'second forward merge' '
+ git checkout origin/svnb1 &&
+ git merge --no-ff origin/svnb2 &&
+ git svn dcommit
+ '
+
+test_expect_success 'check new mergeinfo added' '
+ mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1) &&
+ test "$mergeinfo" = "/branches/svnb2:3,8,16-17
+/branches/svnb3:4,9
+/branches/svnb4:5-6,10-12
+/branches/svnb5:6,11"
+ '
+
+test_expect_success 'reintegration merge' '
+ git checkout origin/svnb4 &&
+ git merge --no-ff origin/svnb1 &&
+ git svn dcommit
+ '
+
+test_expect_success 'check reintegration mergeinfo' '
+ mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb4) &&
+ test "$mergeinfo" = "/branches/svnb1:2-4,7-9,13-18
+/branches/svnb2:3,8,16-17
+/branches/svnb3:4,9
+/branches/svnb5:6,11"
+ '
+
+test_expect_success 'dcommit a merge at the top of a stack' '
+ git checkout origin/svnb1 &&
+ touch anotherfile &&
+ git add anotherfile &&
+ git commit -m "a commit" &&
+ git merge origin/svnb4 &&
+ git svn dcommit
+ '
+
+test_done
diff --git a/t/t9161/branches.dump b/t/t9161/branches.dump
new file mode 100644
index 0000000..e61c3e7
--- /dev/null
+++ b/t/t9161/branches.dump
@@ -0,0 +1,374 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 1ef08553-f2d1-45df-b38c-19af6b7c926d
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2011-09-02T16:08:02.941384Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 114
+Content-length: 114
+
+K 7
+svn:log
+V 12
+Base commit
+
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:08:27.205062Z
+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 19
+Create branch svnb1
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:09:43.628137Z
+PROPS-END
+
+Node-path: branches/svnb1
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Revision-number: 3
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 19
+Create branch svnb2
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:09:46.339930Z
+PROPS-END
+
+Node-path: branches/svnb2
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Revision-number: 4
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 19
+Create branch svnb3
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:09:49.394515Z
+PROPS-END
+
+Node-path: branches/svnb3
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Revision-number: 5
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 19
+Create branch svnb4
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:09:54.114607Z
+PROPS-END
+
+Node-path: branches/svnb4
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Revision-number: 6
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 19
+Create branch svnb5
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:09:58.602623Z
+PROPS-END
+
+Node-path: branches/svnb5
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Revision-number: 7
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 9
+b1 commit
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:10:20.292369Z
+PROPS-END
+
+Node-path: branches/svnb1/b1file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 8
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 9
+b2 commit
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:10:38.429199Z
+PROPS-END
+
+Node-path: branches/svnb2/b2file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 9
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 9
+b3 commit
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:10:52.843023Z
+PROPS-END
+
+Node-path: branches/svnb3/b3file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 10
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 9
+b4 commit
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:11:17.489870Z
+PROPS-END
+
+Node-path: branches/svnb4/b4file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 11
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 9
+b5 commit
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:11:32.277404Z
+PROPS-END
+
+Node-path: branches/svnb5/b5file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 12
+Prop-content-length: 192
+Content-length: 192
+
+K 7
+svn:log
+V 90
+Merge remote-tracking branch 'svnb5' into HEAD
+
+* svnb5:
+ b5 commit
+ Create branch svnb5
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:11:54.274722Z
+PROPS-END
+
+Node-path: branches/svnb4
+Node-kind: dir
+Node-action: change
+Prop-content-length: 56
+Content-length: 56
+
+K 13
+svn:mergeinfo
+V 21
+/branches/svnb5:6,11
+
+PROPS-END
+
+
+Node-path: branches/svnb4/b5file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 10
+
+PROPS-END
+
+
diff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh
new file mode 100755
index 0000000..e38d9fa
--- /dev/null
+++ b/t/t9162-git-svn-dcommit-interactive.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+#
+# Copyright (c) 2011 Frédéric Heitzmann
+
+test_description='git svn dcommit --interactive series'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' '
+ svn_cmd mkdir -m"mkdir test-interactive" "$svnrepo/test-interactive" &&
+ git svn clone "$svnrepo/test-interactive" test-interactive &&
+ cd test-interactive &&
+ touch foo && git add foo && git commit -m"foo: first commit" &&
+ git svn dcommit
+ '
+
+test_expect_success 'answers: y [\n] yes' '
+ (
+ echo "change #1" >> foo && git commit -a -m"change #1" &&
+ echo "change #2" >> foo && git commit -a -m"change #2" &&
+ echo "change #3" >> foo && git commit -a -m"change #3" &&
+ ( echo "y
+
+y" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) &&
+ test $(git rev-parse HEAD) = $(git rev-parse remotes/git-svn)
+ )
+ '
+
+test_expect_success 'answers: yes yes no' '
+ (
+ echo "change #1" >> foo && git commit -a -m"change #1" &&
+ echo "change #2" >> foo && git commit -a -m"change #2" &&
+ echo "change #3" >> foo && git commit -a -m"change #3" &&
+ ( echo "yes
+yes
+no" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) &&
+ test $(git rev-parse HEAD^^^) = $(git rev-parse remotes/git-svn) &&
+ git reset --hard remotes/git-svn
+ )
+ '
+
+test_expect_success 'answers: yes quit' '
+ (
+ echo "change #1" >> foo && git commit -a -m"change #1" &&
+ echo "change #2" >> foo && git commit -a -m"change #2" &&
+ echo "change #3" >> foo && git commit -a -m"change #3" &&
+ ( echo "yes
+quit" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) &&
+ test $(git rev-parse HEAD^^^) = $(git rev-parse remotes/git-svn) &&
+ git reset --hard remotes/git-svn
+ )
+ '
+
+test_expect_success 'answers: all' '
+ (
+ echo "change #1" >> foo && git commit -a -m"change #1" &&
+ echo "change #2" >> foo && git commit -a -m"change #2" &&
+ echo "change #3" >> foo && git commit -a -m"change #3" &&
+ ( echo "all" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) &&
+ test $(git rev-parse HEAD) = $(git rev-parse remotes/git-svn) &&
+ git reset --hard remotes/git-svn
+ )
+ '
+
+test_done
diff --git a/t/t9163-git-svn-reset-clears-caches.sh b/t/t9163-git-svn-reset-clears-caches.sh
new file mode 100755
index 0000000..d6245ce
--- /dev/null
+++ b/t/t9163-git-svn-reset-clears-caches.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Peter Baumann
+#
+
+test_description='git svn reset clears memoized caches'
+. ./lib-git-svn.sh
+
+svn_ver="$(svn --version --quiet)"
+case $svn_ver in
+0.* | 1.[0-4].*)
+ skip_all="skipping git-svn test - SVN too old ($svn_ver)"
+ test_done
+ ;;
+esac
+
+# ... a - b - m <- trunk
+# \ /
+# ... c <- branch1
+#
+# SVN Commits not interesting for this test are abbreviated with "..."
+#
+test_expect_success 'initialize source svn repo' '
+ svn_cmd mkdir -m "create trunk" "$svnrepo"/trunk &&
+ svn_cmd mkdir -m "create branches" "$svnrepo/branches" &&
+ svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
+ (
+ cd "$SVN_TREE" &&
+ touch foo &&
+ svn_cmd add foo &&
+ svn_cmd commit -m "a" &&
+ svn_cmd cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch1 &&
+ svn_cmd switch "$svnrepo"/branches/branch1 &&
+ touch bar &&
+ svn_cmd add bar &&
+ svn_cmd commit -m b &&
+ svn_cmd switch "$svnrepo"/trunk &&
+ touch baz &&
+ svn_cmd add baz &&
+ svn_cmd commit -m c &&
+ svn_cmd up &&
+ svn_cmd merge "$svnrepo"/branches/branch1 &&
+ svn_cmd commit -m "m"
+ ) &&
+ rm -rf "$SVN_TREE"
+'
+
+test_expect_success 'fetch to merge-base (a)' '
+ git svn init -s "$svnrepo" &&
+ git svn fetch --revision BASE:3
+'
+
+# git svn rebase looses the merge commit
+#
+# ... a - b - m <- trunk
+# \
+# ... c
+#
+test_expect_success 'rebase looses SVN merge (m)' '
+ git svn rebase &&
+ git svn fetch &&
+ test 1 = $(git cat-file -p master|grep parent|wc -l)
+'
+
+# git svn fetch creates correct history with merge commit
+#
+# ... a - b - m <- trunk
+# \ /
+# ... c <- branch1
+#
+test_expect_success 'reset and fetch gets the SVN merge (m) correctly' '
+ git svn reset -r 3 &&
+ git reset --hard origin/trunk &&
+ git svn fetch &&
+ test 2 = $(git cat-file -p origin/trunk|grep parent|wc -l)
+'
+
+test_done
diff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh
new file mode 100755
index 0000000..d8464d4
--- /dev/null
+++ b/t/t9164-git-svn-dcommit-concurrent.sh
@@ -0,0 +1,216 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Robert Luberda
+#
+
+test_description='concurrent git svn dcommit'
+. ./lib-git-svn.sh
+
+
+
+test_expect_success 'setup svn repository' '
+ svn_cmd checkout "$svnrepo" work.svn &&
+ (
+ cd work.svn &&
+ echo >file && echo > auto_updated_file
+ svn_cmd add file auto_updated_file &&
+ svn_cmd commit -m "initial commit"
+ ) &&
+ svn_cmd checkout "$svnrepo" work-auto-commits.svn
+'
+N=0
+next_N()
+{
+ N=$(( $N + 1 ))
+}
+
+# Setup SVN repository hooks to emulate SVN failures or concurrent commits
+# The function adds
+# either pre-commit hook, which causes SVN commit given in second argument
+# to fail
+# or post-commit hook, which creates a new commit (a new line added to
+# auto_updated_file) after given SVN commit
+# The first argument contains a type of the hook
+# The second argument contains a number (not SVN revision) of commit
+# the hook should be applied for (each time the hook is run, the given
+# number is decreased by one until it gets 0, in which case the hook
+# will execute its real action)
+setup_hook()
+{
+ hook_type="$1" # "pre-commit" or "post-commit"
+ skip_revs="$2"
+ [ "$hook_type" = "pre-commit" ] ||
+ [ "$hook_type" = "post-commit" ] ||
+ { echo "ERROR: invalid argument ($hook_type)" \
+ "passed to setup_hook" >&2 ; return 1; }
+ echo "cnt=$skip_revs" > "$hook_type-counter"
+ rm -f "$rawsvnrepo/hooks/"*-commit # drop previous hooks
+ hook="$rawsvnrepo/hooks/$hook_type"
+ cat > "$hook" <<- 'EOF1'
+ #!/bin/sh
+ set -e
+ cd "$1/.." # "$1" is repository location
+ exec >> svn-hook.log 2>&1
+ hook="$(basename "$0")"
+ echo "*** Executing $hook $@"
+ set -x
+ . ./$hook-counter
+ cnt="$(($cnt - 1))"
+ echo "cnt=$cnt" > ./$hook-counter
+ [ "$cnt" = "0" ] || exit 0
+EOF1
+ if [ "$hook_type" = "pre-commit" ]; then
+ echo "echo 'commit disallowed' >&2; exit 1" >>"$hook"
+ else
+ echo "PATH=\"$PATH\"; export PATH" >>"$hook"
+ echo "svnconf=\"$svnconf\"" >>"$hook"
+ cat >>"$hook" <<- 'EOF2'
+ cd work-auto-commits.svn
+ svn up --config-dir "$svnconf"
+ echo "$$" >> auto_updated_file
+ svn commit --config-dir "$svnconf" \
+ -m "auto-committing concurrent change"
+ exit 0
+EOF2
+ fi
+ chmod 755 "$hook"
+}
+
+check_contents()
+{
+ gitdir="$1"
+ (cd ../work.svn && svn_cmd up) &&
+ test_cmp file ../work.svn/file &&
+ test_cmp auto_updated_file ../work.svn/auto_updated_file
+}
+
+test_expect_success 'check if post-commit hook creates a concurrent commit' '
+ setup_hook post-commit 1 &&
+ (
+ cd work.svn &&
+ cp auto_updated_file au_file_saved &&
+ echo 1 >> file &&
+ svn_cmd commit -m "changing file" &&
+ svn_cmd up &&
+ test_must_fail test_cmp auto_updated_file au_file_saved
+ )
+'
+
+test_expect_success 'check if pre-commit hook fails' '
+ setup_hook pre-commit 2 &&
+ (
+ cd work.svn &&
+ echo 2 >> file &&
+ svn_cmd commit -m "changing file once again" &&
+ echo 3 >> file &&
+ test_must_fail svn_cmd commit -m "this commit should fail" &&
+ svn_cmd revert file
+ )
+'
+
+test_expect_success 'dcommit error handling' '
+ setup_hook pre-commit 2 &&
+ next_N && git svn clone "$svnrepo" work$N.git &&
+ (
+ cd work$N.git &&
+ echo 1 >> file && git commit -am "commit change $N.1" &&
+ echo 2 >> file && git commit -am "commit change $N.2" &&
+ echo 3 >> file && git commit -am "commit change $N.3" &&
+ # should fail to dcommit 2nd and 3rd change
+ # but still should leave the repository in reasonable state
+ test_must_fail git svn dcommit &&
+ git update-index --refresh &&
+ git show HEAD~2 | grep -q git-svn-id &&
+ ! git show HEAD~1 | grep -q git-svn-id &&
+ ! git show HEAD | grep -q git-svn-id
+ )
+'
+
+test_expect_success 'dcommit concurrent change in non-changed file' '
+ setup_hook post-commit 2 &&
+ next_N && git svn clone "$svnrepo" work$N.git &&
+ (
+ cd work$N.git &&
+ echo 1 >> file && git commit -am "commit change $N.1" &&
+ echo 2 >> file && git commit -am "commit change $N.2" &&
+ echo 3 >> file && git commit -am "commit change $N.3" &&
+ # should rebase and leave the repository in reasonable state
+ git svn dcommit &&
+ git update-index --refresh &&
+ check_contents &&
+ git show HEAD~3 | grep -q git-svn-id &&
+ git show HEAD~2 | grep -q git-svn-id &&
+ git show HEAD~1 | grep -q auto-committing &&
+ git show HEAD | grep -q git-svn-id
+ )
+'
+
+# An utility function used in the following test
+delete_first_line()
+{
+ file="$1" &&
+ sed 1d < "$file" > "${file}.tmp" &&
+ rm "$file" &&
+ mv "${file}.tmp" "$file"
+}
+
+test_expect_success 'dcommit concurrent non-conflicting change' '
+ setup_hook post-commit 2 &&
+ next_N && git svn clone "$svnrepo" work$N.git &&
+ (
+ cd work$N.git &&
+ cat file >> auto_updated_file &&
+ git commit -am "commit change $N.1" &&
+ delete_first_line auto_updated_file &&
+ git commit -am "commit change $N.2" &&
+ delete_first_line auto_updated_file &&
+ git commit -am "commit change $N.3" &&
+ # should rebase and leave the repository in reasonable state
+ git svn dcommit &&
+ git update-index --refresh &&
+ check_contents &&
+ git show HEAD~3 | grep -q git-svn-id &&
+ git show HEAD~2 | grep -q git-svn-id &&
+ git show HEAD~1 | grep -q auto-committing &&
+ git show HEAD | grep -q git-svn-id
+ )
+'
+
+test_expect_success 'dcommit --no-rebase concurrent non-conflicting change' '
+ setup_hook post-commit 2 &&
+ next_N && git svn clone "$svnrepo" work$N.git &&
+ (
+ cd work$N.git &&
+ cat file >> auto_updated_file &&
+ git commit -am "commit change $N.1" &&
+ delete_first_line auto_updated_file &&
+ git commit -am "commit change $N.2" &&
+ delete_first_line auto_updated_file &&
+ git commit -am "commit change $N.3" &&
+ # should fail as rebase is needed
+ test_must_fail git svn dcommit --no-rebase &&
+ # but should leave HEAD unchanged
+ git update-index --refresh &&
+ ! git show HEAD~2 | grep -q git-svn-id &&
+ ! git show HEAD~1 | grep -q git-svn-id &&
+ ! git show HEAD | grep -q git-svn-id
+ )
+'
+
+test_expect_success 'dcommit fails on concurrent conflicting change' '
+ setup_hook post-commit 1 &&
+ next_N && git svn clone "$svnrepo" work$N.git &&
+ (
+ cd work$N.git &&
+ echo a >> file &&
+ git commit -am "commit change $N.1" &&
+ echo b >> auto_updated_file &&
+ git commit -am "commit change $N.2" &&
+ echo c >> auto_updated_file &&
+ git commit -am "commit change $N.3" &&
+ test_must_fail git svn dcommit && # rebase should fail
+ test_must_fail git update-index --refresh
+ )
+'
+
+test_done
diff --git a/t/t9165-git-svn-fetch-merge-branch-of-branch.sh b/t/t9165-git-svn-fetch-merge-branch-of-branch.sh
new file mode 100755
index 0000000..fa3ef3b
--- /dev/null
+++ b/t/t9165-git-svn-fetch-merge-branch-of-branch.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Steven Walter
+#
+
+test_description='git svn merge detection'
+. ./lib-git-svn.sh
+
+svn_ver="$(svn --version --quiet)"
+case $svn_ver in
+0.* | 1.[0-4].*)
+ skip_all="skipping git-svn test - SVN too old ($svn_ver)"
+ test_done
+ ;;
+esac
+
+test_expect_success 'initialize source svn repo' '
+ svn_cmd mkdir -m x "$svnrepo"/trunk &&
+ svn_cmd mkdir -m x "$svnrepo"/branches &&
+ svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
+ (
+ cd "$SVN_TREE" &&
+ touch foo &&
+ svn_cmd add foo &&
+ svn_cmd commit -m "initial commit" &&
+ svn_cmd cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch1 &&
+ svn_cmd switch "$svnrepo"/branches/branch1 &&
+ touch bar &&
+ svn_cmd add bar &&
+ svn_cmd commit -m branch1 &&
+ svn_cmd cp -m branch "$svnrepo"/branches/branch1 "$svnrepo"/branches/branch2 &&
+ svn_cmd switch "$svnrepo"/branches/branch2 &&
+ touch baz &&
+ svn_cmd add baz &&
+ svn_cmd commit -m branch2 &&
+ svn_cmd switch "$svnrepo"/trunk &&
+ touch bar2 &&
+ svn_cmd add bar2 &&
+ svn_cmd commit -m trunk &&
+ svn_cmd switch "$svnrepo"/branches/branch2 &&
+ svn_cmd merge "$svnrepo"/trunk &&
+ svn_cmd commit -m "merge trunk"
+ svn_cmd switch "$svnrepo"/trunk &&
+ svn_cmd merge --reintegrate "$svnrepo"/branches/branch2 &&
+ svn_cmd commit -m "merge branch2"
+ ) &&
+ rm -rf "$SVN_TREE"
+'
+
+test_expect_success 'clone svn repo' '
+ git svn init -s "$svnrepo" &&
+ git svn fetch
+'
+
+test_expect_success 'verify merge commit' 'x=$(git rev-parse HEAD^2) &&
+ y=$(git rev-parse origin/branch2) &&
+ test "x$x" = "x$y"
+'
+
+test_done
diff --git a/t/t9166-git-svn-fetch-merge-branch-of-branch2.sh b/t/t9166-git-svn-fetch-merge-branch-of-branch2.sh
new file mode 100755
index 0000000..52f2e46
--- /dev/null
+++ b/t/t9166-git-svn-fetch-merge-branch-of-branch2.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Steven Walter
+#
+
+test_description='git svn merge detection'
+. ./lib-git-svn.sh
+
+svn_ver="$(svn --version --quiet)"
+case $svn_ver in
+0.* | 1.[0-4].*)
+ skip_all="skipping git-svn test - SVN too old ($svn_ver)"
+ test_done
+ ;;
+esac
+
+test_expect_success 'initialize source svn repo' '
+ svn_cmd mkdir -m x "$svnrepo"/trunk &&
+ svn_cmd mkdir -m x "$svnrepo"/branches &&
+ svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
+ (
+ cd "$SVN_TREE" &&
+ touch foo &&
+ svn_cmd add foo &&
+ svn_cmd commit -m "initial commit" &&
+ svn_cmd cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch1 &&
+ svn_cmd switch "$svnrepo"/branches/branch1 &&
+ touch bar &&
+ svn_cmd add bar &&
+ svn_cmd commit -m branch1 &&
+ svn_cmd cp -m branch "$svnrepo"/branches/branch1 "$svnrepo"/branches/branch2 &&
+ svn_cmd switch "$svnrepo"/branches/branch2 &&
+ touch baz &&
+ svn_cmd add baz &&
+ svn_cmd commit -m branch2 &&
+ svn_cmd switch "$svnrepo"/trunk &&
+ svn_cmd merge --reintegrate "$svnrepo"/branches/branch2 &&
+ svn_cmd commit -m "merge branch2"
+ ) &&
+ rm -rf "$SVN_TREE"
+'
+
+test_expect_success 'clone svn repo' '
+ git svn init -s "$svnrepo" &&
+ git svn fetch
+'
+
+test_expect_success 'verify merge commit' 'x=$(git rev-parse HEAD^2) &&
+ y=$(git rev-parse origin/branch2) &&
+ test "x$x" = "x$y"
+'
+
+test_done
diff --git a/t/t9167-git-svn-cmd-branch-subproject.sh b/t/t9167-git-svn-cmd-branch-subproject.sh
new file mode 100755
index 0000000..ba35fc0
--- /dev/null
+++ b/t/t9167-git-svn-cmd-branch-subproject.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# Copyright (c) 2013 Tobias Schulte
+#
+
+test_description='git svn branch for subproject clones'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize svnrepo' '
+ mkdir import &&
+ (
+ cd import &&
+ mkdir -p trunk/project branches tags &&
+ (
+ cd trunk/project &&
+ echo foo > foo
+ ) &&
+ svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null
+ ) &&
+ rm -rf import &&
+ svn_cmd co "$svnrepo"/trunk/project trunk/project &&
+ (
+ cd trunk/project &&
+ echo bar >> foo &&
+ svn_cmd ci -m "updated trunk"
+ ) &&
+ rm -rf trunk
+'
+
+test_expect_success 'import into git' '
+ git svn init --trunk=trunk/project --branches=branches/*/project \
+ --tags=tags/*/project "$svnrepo" &&
+ git svn fetch &&
+ git checkout remotes/origin/trunk
+'
+
+test_expect_success 'git svn branch tests' '
+ test_must_fail git svn branch a &&
+ git svn branch --parents a &&
+ test_must_fail git svn branch -t tag1 &&
+ git svn branch --parents -t tag1 &&
+ test_must_fail git svn branch --tag tag2 &&
+ git svn branch --parents --tag tag2 &&
+ test_must_fail git svn tag tag3 &&
+ git svn tag --parents tag3
+'
+
+test_done
diff --git a/t/t9168-git-svn-partially-globbed-names.sh b/t/t9168-git-svn-partially-globbed-names.sh
new file mode 100755
index 0000000..8b22f22
--- /dev/null
+++ b/t/t9168-git-svn-partially-globbed-names.sh
@@ -0,0 +1,223 @@
+#!/bin/sh
+test_description='git svn globbing refspecs with prefixed globs'
+. ./lib-git-svn.sh
+
+test_expect_success 'prepare test refspec prefixed globbing' '
+ cat >expect.end <<EOF
+the end
+hi
+start a new branch
+initial
+EOF
+ '
+
+test_expect_success 'test refspec prefixed 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/b_start &&
+ svn_cmd commit -m "start a new branch" &&
+ svn_cmd up &&
+ echo "hi" >>branches/b_start/src/b/readme &&
+ poke branches/b_start/src/b/readme &&
+ echo "hey" >>branches/b_start/src/a/readme &&
+ poke branches/b_start/src/a/readme &&
+ svn_cmd commit -m "hi" &&
+ svn_cmd up &&
+ svn_cmd cp branches/b_start tags/t_end &&
+ echo "bye" >>tags/t_end/src/b/readme &&
+ poke tags/t_end/src/b/readme &&
+ echo "aye" >>tags/t_end/src/a/readme &&
+ poke tags/t_end/src/a/readme &&
+ svn_cmd commit -m "the end" &&
+ echo "byebye" >>tags/t_end/src/b/readme &&
+ poke tags/t_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/b_*/src/a:refs/remotes/branches/b_*" &&
+ git config --add svn-remote.svn.tags\
+ "tags/t_*/src/a:refs/remotes/tags/t_*" &&
+ git svn multi-fetch &&
+ git log --pretty=oneline refs/remotes/tags/t_end | \
+ sed -e "s/^.\{41\}//" >output.end &&
+ test_cmp expect.end output.end &&
+ test "$(git rev-parse refs/remotes/tags/t_end~1)" = \
+ "$(git rev-parse refs/remotes/branches/b_start)" &&
+ test "$(git rev-parse refs/remotes/branches/b_start~2)" = \
+ "$(git rev-parse refs/remotes/trunk)" &&
+ test_must_fail git rev-parse refs/remotes/tags/t_end@3
+ '
+
+test_expect_success 'prepare test left-hand-side only prefixed globbing' '
+ 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 prefixed 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/b_*:refs/remotes/two/branches/*" &&
+ git config --add svn-remote.two.tags \
+ "tags/t_*:refs/remotes/two/tags/*" &&
+ (
+ cd tmp &&
+ echo "try try" >>tags/t_end/src/b/readme &&
+ poke tags/t_end/src/b/readme &&
+ svn_cmd commit -m "try to try"
+ ) &&
+ git svn fetch two &&
+ test $(git rev-list refs/remotes/two/tags/t_end | wc -l) -eq 6 &&
+ test $(git rev-list refs/remotes/two/branches/b_start | wc -l) -eq 3 &&
+ test $(git rev-parse refs/remotes/two/branches/b_start~2) = \
+ $(git rev-parse refs/remotes/two/trunk) &&
+ test $(git rev-parse refs/remotes/two/tags/t_end~3) = \
+ $(git rev-parse refs/remotes/two/branches/b_start) &&
+ git log --pretty=oneline refs/remotes/two/tags/t_end | \
+ sed -e "s/^.\{41\}//" >output.two &&
+ test_cmp expect.two output.two
+ '
+
+test_expect_success 'prepare test prefixed globs match just prefix' '
+ cat >expect.three <<EOF
+Tag commit to t_
+Branch commit to b_
+initial
+EOF
+ '
+
+test_expect_success 'test prefixed globs match just prefix' '
+ 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/b_*:refs/remotes/three/branches/*" &&
+ git config --add svn-remote.three.tags \
+ "tags/t_*:refs/remotes/three/tags/*" &&
+ (
+ cd tmp &&
+ svn_cmd cp trunk branches/b_ &&
+ echo "Branch commit to b_" >>branches/b_/src/a/readme &&
+ poke branches/b_/src/a/readme &&
+ svn_cmd commit -m "Branch commit to b_" &&
+ svn_cmd up && svn_cmd cp branches/b_ tags/t_ &&
+ echo "Tag commit to t_" >>tags/t_/src/a/readme &&
+ poke tags/t_/src/a/readme &&
+ svn_cmd commit -m "Tag commit to t_" &&
+ svn_cmd up
+ ) &&
+ git svn fetch three &&
+ test $(git rev-list refs/remotes/three/branches/b_ | wc -l) -eq 2 &&
+ test $(git rev-list refs/remotes/three/tags/t_ | wc -l) -eq 3 &&
+ test $(git rev-parse refs/remotes/three/branches/b_~1) = \
+ $(git rev-parse refs/remotes/three/trunk) &&
+ test $(git rev-parse refs/remotes/three/tags/t_~1) = \
+ $(git rev-parse refs/remotes/three/branches/b_) &&
+ git log --pretty=oneline refs/remotes/three/tags/t_ | \
+ sed -e "s/^.\{41\}//" >output.three &&
+ test_cmp expect.three output.three
+ '
+
+test_expect_success 'prepare test disallow prefixed multi-globs' "
+cat >expect.four <<EOF
+Only one set of wildcards (e.g. '*' or '*/*/*') is supported: branches/b_*/t/*
+
+EOF
+ "
+
+test_expect_success 'test disallow prefixed multi-globs' '
+ 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/b_*/t/*:refs/remotes/four/branches/*" &&
+ git config --add svn-remote.four.tags \
+ "tags/t_*/*:refs/remotes/four/tags/*" &&
+ (
+ cd tmp &&
+ echo "try try" >>tags/t_end/src/b/readme &&
+ poke tags/t_end/src/b/readme &&
+ svn_cmd commit -m "try to try"
+ ) &&
+ test_must_fail git svn fetch four 2>stderr.four &&
+ test_cmp expect.four stderr.four &&
+ git config --unset svn-remote.four.branches &&
+ git config --unset svn-remote.four.tags
+ '
+
+test_expect_success 'prepare test globbing in the middle of the word' '
+ cat >expect.five <<EOF
+Tag commit to fghij
+Branch commit to abcde
+initial
+EOF
+ '
+
+test_expect_success 'test globbing in the middle of the word' '
+ git config --add svn-remote.five.url "$svnrepo" &&
+ git config --add svn-remote.five.fetch \
+ trunk:refs/remotes/five/trunk &&
+ git config --add svn-remote.five.branches \
+ "branches/a*e:refs/remotes/five/branches/*" &&
+ git config --add svn-remote.five.tags \
+ "tags/f*j:refs/remotes/five/tags/*" &&
+ (
+ cd tmp &&
+ svn_cmd cp trunk branches/abcde &&
+ echo "Branch commit to abcde" >>branches/abcde/src/a/readme &&
+ poke branches/b_/src/a/readme &&
+ svn_cmd commit -m "Branch commit to abcde" &&
+ svn_cmd up &&
+ svn_cmd cp branches/abcde tags/fghij &&
+ echo "Tag commit to fghij" >>tags/fghij/src/a/readme &&
+ poke tags/fghij/src/a/readme &&
+ svn_cmd commit -m "Tag commit to fghij" &&
+ svn_cmd up
+ ) &&
+ git svn fetch five &&
+ test $(git rev-list refs/remotes/five/branches/abcde | wc -l) -eq 2 &&
+ test $(git rev-list refs/remotes/five/tags/fghij | wc -l) -eq 3 &&
+ test $(git rev-parse refs/remotes/five/branches/abcde~1) = \
+ $(git rev-parse refs/remotes/five/trunk) &&
+ test $(git rev-parse refs/remotes/five/tags/fghij~1) = \
+ $(git rev-parse refs/remotes/five/branches/abcde) &&
+ git log --pretty=oneline refs/remotes/five/tags/fghij | \
+ sed -e "s/^.\{41\}//" >output.five &&
+ test_cmp expect.five output.five
+ '
+
+test_expect_success 'prepare test disallow multiple asterisks in one word' "
+ echo \"Only one '*' is allowed in a pattern: 'a*c*e'\" >expect.six &&
+ echo \"\" >>expect.six
+ "
+
+test_expect_success 'test disallow multiple asterisks in one word' '
+ git config --add svn-remote.six.url "$svnrepo" &&
+ git config --add svn-remote.six.fetch \
+ trunk:refs/remotes/six/trunk &&
+ git config --add svn-remote.six.branches \
+ "branches/a*c*e:refs/remotes/six/branches/*" &&
+ git config --add svn-remote.six.tags \
+ "tags/f*h*j:refs/remotes/six/tags/*" &&
+ (
+ cd tmp &&
+ echo "try try" >>tags/fghij/src/b/readme &&
+ poke tags/fghij/src/b/readme &&
+ svn_cmd commit -m "try to try"
+ ) &&
+ test_must_fail git svn fetch six 2>stderr.six &&
+ test_cmp expect.six stderr.six
+ '
+
+test_done
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index ee39b36..bb879a5 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -18,14 +18,15 @@ then
test_done
fi
-CVSROOT=$(pwd)/cvsroot
-CVSWORK=$(pwd)/cvswork
-GIT_DIR=$(pwd)/.git
+CVSROOT=$PWD/tmpcvsroot
+CVSWORK=$PWD/cvswork
+GIT_DIR=$PWD/.git
export CVSROOT CVSWORK GIT_DIR
rm -rf "$CVSROOT" "$CVSWORK"
-mkdir "$CVSROOT" &&
+
cvs init &&
+test -d "$CVSROOT" &&
cvs -Q co -d "$CVSWORK" . &&
echo >empty &&
git add empty &&
@@ -34,7 +35,7 @@ exit 1
check_entries () {
# $1 == directory, $2 == expected
- grep '^/' "$1/CVS/Entries" | sort | cut -d/ -f2,3,5 >actual
+ sed -ne '/^\//p' "$1/CVS/Entries" | sort | cut -d/ -f2,3,5 >actual
if test -z "$2"
then
>expected
@@ -49,8 +50,8 @@ test_expect_success \
'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 &&
+ cp "$TEST_DIRECTORY"/test-binary-1.png C/newfile3.png &&
+ cp "$TEST_DIRECTORY"/test-binary-1.png D/newfile4.png &&
git add A/newfile1.txt &&
git add B/newfile2.txt &&
git add C/newfile3.png &&
@@ -75,8 +76,8 @@ test_expect_success \
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 &&
+ cp "$TEST_DIRECTORY"/test-binary-2.png D/newfile4.png &&
+ cp "$TEST_DIRECTORY"/test-binary-1.png F/newfile6.png &&
git add E/newfile5.txt &&
git add F/newfile6.png &&
git commit -a -m "Test: Remove, add and update" &&
@@ -164,7 +165,7 @@ test_expect_success \
'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" && \
+ cp "$TEST_DIRECTORY"/test-binary-1.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) &&
@@ -176,7 +177,7 @@ test_expect_success \
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" && \
+ cat "$TEST_DIRECTORY"/test-binary-1.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) &&
@@ -196,12 +197,12 @@ 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/å/ä/ö" &&
then
# This test contains UTF-8 characters
-test_expect_success \
+test_expect_success !MINGW \
'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 &&
+ cp "$TEST_DIRECTORY"/test-binary-1.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) &&
@@ -229,11 +230,6 @@ test_expect_success \
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 &&
@@ -325,7 +321,7 @@ test_expect_success 'use the same checkout for Git and CVS' '
(mkdir shared &&
cd shared &&
- unset GIT_DIR &&
+ sane_unset GIT_DIR &&
cvs co . &&
git init &&
git add " space" &&
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index 131f032..4bca35c 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -7,6 +7,30 @@ test_description='test git fast-import utility'
. ./test-lib.sh
. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+# Print $1 bytes from stdin to stdout.
+#
+# This could be written as "head -c $1", but IRIX "head" does not
+# support the -c option.
+head_c () {
+ perl -e '
+ my $len = $ARGV[1];
+ while ($len > 0) {
+ my $s;
+ my $nread = sysread(STDIN, $s, $len);
+ die "cannot read: $!" unless defined($nread);
+ print $s;
+ $len -= $nread;
+ }
+ ' - "$1"
+}
+
+verify_packs () {
+ for p in .git/objects/pack/*.pack
+ do
+ git verify-pack "$@" "$p" || return
+ done
+}
+
file2_data='file2
second line of EOF'
@@ -27,1559 +51,3088 @@ 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 'empty stream succeeds' '
+ git fast-import </dev/null
+'
+
+test_expect_success 'truncated stream complains' '
+ echo "tag foo" | test_must_fail git fast-import
+'
+
+test_expect_success 'A: create pack from stdin' '
+ 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
+
+ tag series-A-blob
+ from :3
+ data <<EOF
+ An annotated tag that annotates a blob.
+ EOF
+
+ INPUT_END
+ git fast-import --export-marks=marks.out <input &&
+ git whatchanged master
+'
+
+test_expect_success 'A: verify pack' '
+ verify_packs
+'
+
+test_expect_success 'A: verify commit' '
+ 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
+ git cat-file commit master | sed 1d >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'A: verify tree' '
+ cat >expect <<-EOF &&
+ 100644 blob file2
+ 100644 blob file3
+ 100755 blob file4
+ EOF
+ git cat-file -p master^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'A: verify file2' '
+ echo "$file2_data" >expect &&
+ git cat-file blob master:file2 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'A: verify file3' '
+ echo "$file3_data" >expect &&
+ git cat-file blob master:file3 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'A: verify file4' '
+ printf "$file4_data" >expect &&
+ git cat-file blob master:file4 >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'A: verify tag/series-A' '
+ cat >expect <<-EOF &&
+ object $(git rev-parse refs/heads/master)
+ type commit
+ tag series-A
+
+ An annotated tag without a tagger
+ EOF
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 tag/series-A-blob' '
+ cat >expect <<-EOF &&
+ object $(git rev-parse refs/heads/master:file3)
+ type blob
+ tag series-A-blob
+
+ An annotated tag that annotates a blob.
+ EOF
+ git cat-file tag tags/series-A-blob >actual &&
+ test_cmp expect actual
+'
-test_expect_success \
- 'A: verify marks import' \
- 'git fast-import \
+test_expect_success 'A: verify marks output' '
+ 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_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`'
+ test_cmp expect marks.new
+'
+
+test_expect_success 'A: tag blob by sha1' '
+ test_tick &&
+ new_blob=$(echo testing | git hash-object --stdin) &&
+ cat >input <<-INPUT_END &&
+ tag series-A-blob-2
+ from $(git rev-parse refs/heads/master:file3)
+ data <<EOF
+ Tag blob by sha1.
+ EOF
+
+ blob
+ mark :6
+ data <<EOF
+ testing
+ EOF
+
+ commit refs/heads/new_blob
+ committer <> 0 +0000
+ data 0
+ M 644 :6 new_blob
+ #pretend we got sha1 from fast-import
+ ls "new_blob"
+
+ tag series-A-blob-3
+ from $new_blob
+ data <<EOF
+ Tag new_blob.
+ EOF
+ INPUT_END
+
+ cat >expect <<-EOF &&
+ object $(git rev-parse refs/heads/master:file3)
+ type blob
+ tag series-A-blob-2
+
+ Tag blob by sha1.
+ object $new_blob
+ type blob
+ tag series-A-blob-3
+
+ Tag new_blob.
+ EOF
+
+ git fast-import <input &&
+ git cat-file tag tags/series-A-blob-2 >actual &&
+ git cat-file tag tags/series-A-blob-3 >>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'A: verify marks import does not crash' '
+ 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
+
+ git fast-import --import-marks=marks.out <input &&
+ git whatchanged verify--import-marks
+'
+
+test_expect_success 'A: verify pack' '
+ verify_packs
+'
+
+test_expect_success 'A: verify diff' '
+ cat >expect <<-EOF &&
+ :000000 100755 0000000000000000000000000000000000000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 A copy-of-file2
+ EOF
+ git diff-tree -M -r master verify--import-marks >actual &&
+ compare_diff_raw expect actual &&
+ test $(git rev-parse --verify master:file2) \
+ = $(git rev-parse --verify verify--import-marks:copy-of-file2)
+'
+
+test_expect_success 'A: export marks with large values' '
+ test_tick &&
+ mt=$(git hash-object --stdin < /dev/null) &&
+ >input.blob &&
+ >marks.exp &&
+ >tree.exp &&
+
+ cat >input.commit <<-EOF &&
+ commit refs/heads/verify--dump-marks
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ test the sparse array dumping routines with exponentially growing marks
+ COMMIT
+ EOF
+
+ i=0 l=4 m=6 n=7 &&
+ while test "$i" -lt 27
+ do
+ cat >>input.blob <<-EOF &&
+ blob
+ mark :$l
+ data 0
+ blob
+ mark :$m
+ data 0
+ blob
+ mark :$n
+ data 0
+ EOF
+ echo "M 100644 :$l l$i" >>input.commit &&
+ echo "M 100644 :$m m$i" >>input.commit &&
+ echo "M 100644 :$n n$i" >>input.commit &&
+
+ echo ":$l $mt" >>marks.exp &&
+ echo ":$m $mt" >>marks.exp &&
+ echo ":$n $mt" >>marks.exp &&
+
+ printf "100644 blob $mt\tl$i\n" >>tree.exp &&
+ printf "100644 blob $mt\tm$i\n" >>tree.exp &&
+ printf "100644 blob $mt\tn$i\n" >>tree.exp &&
+
+ l=$(($l + $l)) &&
+ m=$(($m + $m)) &&
+ n=$(($l + $n)) &&
+
+ i=$((1 + $i)) || return 1
+ done &&
+
+ sort tree.exp > tree.exp_s &&
+
+ cat input.blob input.commit | git fast-import --export-marks=marks.large &&
+ git ls-tree refs/heads/verify--dump-marks >tree.out &&
+ test_cmp tree.exp_s tree.out &&
+ test_cmp marks.exp marks.large
+'
###
### series B
###
-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
+test_expect_success 'B: fail on invalid blob sha1' '
+ 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
+ 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
+ INPUT_END
+
+ test_when_finished "rm -f .git/objects/pack_* .git/objects/index_*" &&
+ test_must_fail git fast-import <input
+'
+
+test_expect_success 'B: accept branch name "TEMP_TAG"' '
+ 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_when_finished "rm -f .git/TEMP_TAG
+ git gc
+ git prune" &&
+ git fast-import <input &&
+ test -f .git/TEMP_TAG &&
+ test $(git rev-parse master) = $(git rev-parse TEMP_TAG^)
'
-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
+test_expect_success 'B: accept empty committer' '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/empty-committer-1
+ committer <> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ empty commit
+ COMMIT
+ INPUT_END
+
+ test_when_finished "git update-ref -d refs/heads/empty-committer-1
+ git gc
+ git prune" &&
+ git fast-import <input &&
+ out=$(git fsck) &&
+ echo "$out" &&
+ test -z "$out"
+'
+
+test_expect_success 'B: accept and fixup committer with no name' '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/empty-committer-2
+ committer <a@b.com> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ empty commit
+ COMMIT
+ INPUT_END
+
+ test_when_finished "git update-ref -d refs/heads/empty-committer-2
+ git gc
+ git prune" &&
+ git fast-import <input &&
+ out=$(git fsck) &&
+ echo "$out" &&
+ test -z "$out"
+'
-from refs/heads/master
+test_expect_success 'B: fail on invalid committer (1)' '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/invalid-committer
+ committer Name email> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ empty commit
+ COMMIT
+ INPUT_END
-INPUT_END
-test_expect_success 'B: fail on invalid branch name ".badbranchname"' '
- test_must_fail git fast-import <input
+ test_when_finished "git update-ref -d refs/heads/invalid-committer" &&
+ 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
+test_expect_success 'B: fail on invalid committer (2)' '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/invalid-committer
+ committer Name <e<mail> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ empty commit
+ COMMIT
+ INPUT_END
+
+ test_when_finished "git update-ref -d refs/heads/invalid-committer" &&
+ test_must_fail git fast-import <input
+'
-from refs/heads/master
+test_expect_success 'B: fail on invalid committer (3)' '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/invalid-committer
+ committer Name <email>> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ empty commit
+ COMMIT
+ INPUT_END
-INPUT_END
-test_expect_success 'B: fail on invalid branch name "bad[branch]name"' '
- test_must_fail git fast-import <input
+ test_when_finished "git update-ref -d refs/heads/invalid-committer" &&
+ 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
+test_expect_success 'B: fail on invalid committer (4)' '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/invalid-committer
+ committer Name <email $GIT_COMMITTER_DATE
+ data <<COMMIT
+ empty commit
+ COMMIT
+ INPUT_END
-from refs/heads/master
+ test_when_finished "git update-ref -d refs/heads/invalid-committer" &&
+ test_must_fail git fast-import <input
+'
-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
+test_expect_success 'B: fail on invalid committer (5)' '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/invalid-committer
+ committer Name<email> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ empty commit
+ COMMIT
+ INPUT_END
+
+ test_when_finished "git update-ref -d refs/heads/invalid-committer" &&
+ test_must_fail git fast-import <input
+'
###
### 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'
+test_expect_success 'C: incremental import create pack from stdin' '
+ 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
+
+ git fast-import <input &&
+ git whatchanged branch
+'
+
+test_expect_success 'C: verify pack' '
+ verify_packs
+'
+
+test_expect_success 'C: validate reuse existing blob' '
+ test $newf = $(git rev-parse --verify branch:file2/newf) &&
+ test $oldf = $(git rev-parse --verify branch:file2/oldf)
+'
+
+test_expect_success 'C: verify commit' '
+ 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
+
+ git cat-file commit branch | sed 1d >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'C: validate rename result' '
+ 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 &&
+ 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'
+test_expect_success 'D: inline data in commit' '
+ 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
+
+ git fast-import <input &&
+ git whatchanged branch
+'
+
+test_expect_success 'D: verify pack' '
+ verify_packs
+'
+
+test_expect_success 'D: validate new files added' '
+ cat >expect <<-EOF &&
+ :000000 100755 0000000000000000000000000000000000000000 e74b7d465e52746be2b4bae983670711e6e66657 A newdir/exec.sh
+ :000000 100644 0000000000000000000000000000000000000000 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 A newdir/interesting
+ EOF
+ git diff-tree -M -r branch^ branch >actual &&
+ compare_diff_raw expect actual
+'
+
+test_expect_success 'D: verify file5' '
+ echo "$file5_data" >expect &&
+ git cat-file blob branch:newdir/interesting >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'D: verify file6' '
+ echo "$file6_data" >expect &&
+ 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
+test_expect_success 'E: rfc2822 date, --date-format=raw' '
+ 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
-from refs/heads/branch^0
+ INPUT_END
-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'
+ 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' '
+ verify_packs
+'
+
+test_expect_success 'E: verify commit' '
+ 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
+ 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'
+test_expect_success 'F: non-fast-forward update skips' '
+ 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_must_fail git fast-import <input &&
+ # branch must remain unaffected
+ test $old_branch = $(git rev-parse --verify branch^0)
+'
+
+test_expect_success 'F: verify pack' '
+ verify_packs
+'
+
+test_expect_success 'F: verify other commit' '
+ 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
+ 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}`'
+test_expect_success 'G: non-fast-forward update forced' '
+ 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
+ git fast-import --force <input
+'
+
+test_expect_success 'G: verify pack' '
+ verify_packs
+'
+
+test_expect_success 'G: branch changed, but logged' '
+ test $old_branch != $(git rev-parse --verify branch^0) &&
+ 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'
+test_expect_success 'H: deletall, add 1' '
+ 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
+ git fast-import <input &&
+ git whatchanged H
+'
+
+test_expect_success 'H: verify pack' '
+ verify_packs
+'
+
+test_expect_success 'H: validate old files removed, new files added' '
+ 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 &&
+ compare_diff_raw expect actual
+'
+
+test_expect_success 'H: verify file' '
+ echo "$file5_data" >expect &&
+ 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
+test_expect_success 'I: export-pack-edges' '
+ 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
+ from refs/heads/branch
-INPUT_END
-test_expect_success \
- 'I: export-pack-edges' \
- 'git fast-import --export-pack-edges=edges.list <input'
+ INPUT_END
+ 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'
+test_expect_success 'I: verify edge list' '
+ cat >expect <<-EOF &&
+ .git/objects/pack/pack-.pack: $(git rev-parse --verify export-boundary)
+ EOF
+ 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
+test_expect_success 'J: reset existing branch creates empty commit' '
+ 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
-from refs/heads/branch
+ reset refs/heads/J
-reset refs/heads/J
+ commit refs/heads/J
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ initialize J
+ COMMIT
-commit refs/heads/J
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-initialize J
-COMMIT
+ INPUT_END
+ 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)
+'
+
+test_expect_success 'J: tag must fail on empty branch' '
+ cat >input <<-INPUT_END &&
+ reset refs/heads/J2
-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`'
+ tag wrong_tag
+ from refs/heads/J2
+ data <<EOF
+ Tag branch that was reset.
+ EOF
+ INPUT_END
+ test_must_fail git fast-import <input
+'
###
### 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
+test_expect_success 'K: reinit branch with from' '
+ 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
+ from refs/heads/branch
-commit refs/heads/K
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-redo K
-COMMIT
+ commit refs/heads/K
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ redo K
+ COMMIT
-from refs/heads/branch^1
+ 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`'
+ INPUT_END
+ 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'
+test_expect_success 'L: verify internal tree sorting' '
+ 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
+
+ git fast-import <input &&
+ git diff-tree --abbrev --raw L^ L >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'L: nested tree copy does not corrupt deltas' '
+ cat >input <<-INPUT_END &&
+ blob
+ mark :1
+ data <<EOF
+ the data
+ EOF
+
+ commit refs/heads/L2
+ committer C O Mitter <committer@example.com> 1112912473 -0700
+ data <<COMMIT
+ init L2
+ COMMIT
+ M 644 :1 a/b/c
+ M 644 :1 a/b/d
+ M 644 :1 a/e/f
+
+ commit refs/heads/L2
+ committer C O Mitter <committer@example.com> 1112912473 -0700
+ data <<COMMIT
+ update L2
+ COMMIT
+ C a g
+ C a/e g/b
+ M 644 :1 g/b/h
+ INPUT_END
+
+ cat >expect <<-\EOF &&
+ g/b/f
+ g/b/h
+ EOF
+
+ test_when_finished "git update-ref -d refs/heads/L2" &&
+ git fast-import <input &&
+ git ls-tree L2 g/b/ >tmp &&
+ cat tmp | cut -f 2 >actual &&
+ test_cmp expect actual &&
+ git fsck $(git rev-parse L2)
+'
###
### 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'
+test_expect_success 'M: rename file in same subdirectory' '
+ 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
+ git fast-import <input &&
+ git diff-tree -M -r M1^ M1 >actual &&
+ compare_diff_raw expect actual
+'
+
+test_expect_success 'M: rename file to new subdirectory' '
+ 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
+ git fast-import <input &&
+ git diff-tree -M -r M2^ M2 >actual &&
+ compare_diff_raw expect actual
+'
+
+test_expect_success 'M: rename subdirectory to new subdirectory' '
+ 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
+ git fast-import <input &&
+ git diff-tree -M -r M3^ M3 >actual &&
+ compare_diff_raw expect actual
+'
+
+test_expect_success 'M: rename root to subdirectory' '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/M4
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ rename root
+ COMMIT
+
+ from refs/heads/M2^0
+ R "" sub
+
+ INPUT_END
+
+ cat >expect <<-EOF &&
+ :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 R100 file2/oldf sub/file2/oldf
+ :100755 100755 85df50785d62d3b05ab03d9cbf7e4a0b49449730 85df50785d62d3b05ab03d9cbf7e4a0b49449730 R100 file4 sub/file4
+ :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100 i/am/new/to/you sub/i/am/new/to/you
+ :100755 100755 e74b7d465e52746be2b4bae983670711e6e66657 e74b7d465e52746be2b4bae983670711e6e66657 R100 newdir/exec.sh sub/newdir/exec.sh
+ :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 R100 newdir/interesting sub/newdir/interesting
+ EOF
+ git fast-import <input &&
+ git diff-tree -M -r M4^ M4 >actual &&
+ cat 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}`'
+test_expect_success 'N: copy file in same subdirectory' '
+ 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
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N1^ N1 >actual &&
+ compare_diff_raw expect actual
+'
+
+test_expect_success 'N: copy then modify subdirectory' '
+ 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
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N2^^ N2 >actual &&
+ compare_diff_raw expect actual
+'
+
+test_expect_success 'N: copy dirty subdirectory' '
+ 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
+
+ git fast-import <input &&
+ test $(git rev-parse N2^{tree}) = $(git rev-parse N3^{tree})
+'
+
+test_expect_success 'N: copy directory by id' '
+ cat >expect <<-\EOF &&
+ :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
+ :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
+ EOF
+ subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N4
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy by tree hash
+ COMMIT
+
+ from refs/heads/branch^0
+ M 040000 $subdir file3
+ INPUT_END
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
+ compare_diff_raw expect actual
+'
+
+test_expect_success PIPE 'N: read and copy directory' '
+ cat >expect <<-\EOF &&
+ :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
+ :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
+ EOF
+ git update-ref -d refs/heads/N4 &&
+ rm -f backflow &&
+ mkfifo backflow &&
+ (
+ exec <backflow &&
+ cat <<-EOF &&
+ commit refs/heads/N4
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy by tree hash, part 2
+ COMMIT
+
+ from refs/heads/branch^0
+ ls "file2"
+ EOF
+ read mode type tree filename &&
+ echo "M 040000 $tree file3"
+ ) |
+ git fast-import --cat-blob-fd=3 3>backflow &&
+ git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
+ compare_diff_raw expect actual
+'
+
+test_expect_success PIPE 'N: empty directory reads as missing' '
+ cat <<-\EOF >expect &&
+ OBJNAME
+ :000000 100644 OBJNAME OBJNAME A unrelated
+ EOF
+ echo "missing src" >expect.response &&
+ git update-ref -d refs/heads/read-empty &&
+ rm -f backflow &&
+ mkfifo backflow &&
+ (
+ exec <backflow &&
+ cat <<-EOF &&
+ commit refs/heads/read-empty
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ read "empty" (missing) directory
+ COMMIT
+
+ M 100644 inline src/greeting
+ data <<BLOB
+ hello
+ BLOB
+ C src/greeting dst1/non-greeting
+ C src/greeting unrelated
+ # leave behind "empty" src directory
+ D src/greeting
+ ls "src"
+ EOF
+ read -r line &&
+ printf "%s\n" "$line" >response &&
+ cat <<-\EOF
+ D dst1
+ D dst2
+ EOF
+ ) |
+ git fast-import --cat-blob-fd=3 3>backflow &&
+ test_cmp expect.response response &&
+ git rev-list read-empty |
+ git diff-tree -r --root --stdin |
+ sed "s/$_x40/OBJNAME/g" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'N: copy root directory by tree hash' '
+ cat >expect <<-\EOF &&
+ :100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D file3/newf
+ :100644 000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 0000000000000000000000000000000000000000 D file3/oldf
+ EOF
+ root=$(git rev-parse refs/heads/branch^0^{tree}) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N6
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy root directory by tree hash
+ COMMIT
+
+ from refs/heads/branch^0
+ M 040000 $root ""
+ INPUT_END
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N4 N6 >actual &&
+ compare_diff_raw expect actual
+'
+
+test_expect_success 'N: copy root by path' '
+ cat >expect <<-\EOF &&
+ :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf oldroot/file2/newf
+ :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf oldroot/file2/oldf
+ :100755 100755 85df50785d62d3b05ab03d9cbf7e4a0b49449730 85df50785d62d3b05ab03d9cbf7e4a0b49449730 C100 file4 oldroot/file4
+ :100755 100755 e74b7d465e52746be2b4bae983670711e6e66657 e74b7d465e52746be2b4bae983670711e6e66657 C100 newdir/exec.sh oldroot/newdir/exec.sh
+ :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100 newdir/interesting oldroot/newdir/interesting
+ EOF
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N-copy-root-path
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy root directory by (empty) path
+ COMMIT
+
+ from refs/heads/branch^0
+ C "" oldroot
+ INPUT_END
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r branch N-copy-root-path >actual &&
+ compare_diff_raw expect actual
+'
+
+test_expect_success 'N: delete directory by copying' '
+ cat >expect <<-\EOF &&
+ OBJID
+ :100644 000000 OBJID OBJID D foo/bar/qux
+ OBJID
+ :000000 100644 OBJID OBJID A foo/bar/baz
+ :000000 100644 OBJID OBJID A foo/bar/qux
+ EOF
+ empty_tree=$(git mktree </dev/null) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N-delete
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ collect data to be deleted
+ COMMIT
+
+ deleteall
+ M 100644 inline foo/bar/baz
+ data <<DATA_END
+ hello
+ DATA_END
+ C "foo/bar/baz" "foo/bar/qux"
+ C "foo/bar/baz" "foo/bar/quux/1"
+ C "foo/bar/baz" "foo/bar/quuux"
+ M 040000 $empty_tree foo/bar/quux
+ M 040000 $empty_tree foo/bar/quuux
+
+ commit refs/heads/N-delete
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ delete subdirectory
+ COMMIT
+
+ M 040000 $empty_tree foo/bar/qux
+ INPUT_END
+ git fast-import <input &&
+ git rev-list N-delete |
+ git diff-tree -r --stdin --root --always |
+ sed -e "s/$_x40/OBJID/g" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'N: modify copied tree' '
+ cat >expect <<-\EOF &&
+ :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100 newdir/interesting file3/file5
+ :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
+ :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
+ EOF
+ subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N5
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy by tree hash
+ COMMIT
+
+ from refs/heads/branch^0
+ M 040000 $subdir file3
+
+ commit refs/heads/N5
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ modify directory copy
+ COMMIT
+
+ M 644 inline file3/file5
+ data <<EOF
+ $file5_data
+ EOF
+ INPUT_END
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N5^^ N5 >actual &&
+ compare_diff_raw expect actual
+'
+
+test_expect_success 'N: reject foo/ syntax' '
+ subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+ test_must_fail git fast-import <<-INPUT_END
+ commit refs/heads/N5B
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy with invalid syntax
+ COMMIT
+
+ from refs/heads/branch^0
+ M 040000 $subdir file3/
+ INPUT_END
+'
+
+test_expect_success 'N: reject foo/ syntax in copy source' '
+ test_must_fail git fast-import <<-INPUT_END
+ commit refs/heads/N5C
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy with invalid syntax
+ COMMIT
+
+ from refs/heads/branch^0
+ C file2/ file3
+ INPUT_END
+'
+
+test_expect_success 'N: reject foo/ syntax in rename source' '
+ test_must_fail git fast-import <<-INPUT_END
+ commit refs/heads/N5D
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ rename with invalid syntax
+ COMMIT
+
+ from refs/heads/branch^0
+ R file2/ file3
+ INPUT_END
+'
+
+test_expect_success 'N: reject foo/ syntax in ls argument' '
+ test_must_fail git fast-import <<-INPUT_END
+ commit refs/heads/N5E
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy with invalid syntax
+ COMMIT
+
+ from refs/heads/branch^0
+ ls "file2/"
+ INPUT_END
+'
+
+test_expect_success 'N: copy to root by id and modify' '
+ echo "hello, world" >expect.foo &&
+ echo hello >expect.bar &&
+ git fast-import <<-SETUP_END &&
+ commit refs/heads/N7
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ hello, tree
+ COMMIT
+
+ deleteall
+ M 644 inline foo/bar
+ data <<EOF
+ hello
+ EOF
+ SETUP_END
+
+ tree=$(git rev-parse --verify N7:) &&
+ git fast-import <<-INPUT_END &&
+ commit refs/heads/N8
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy to root by id and modify
+ COMMIT
+
+ M 040000 $tree ""
+ M 644 inline foo/foo
+ data <<EOF
+ hello, world
+ EOF
+ INPUT_END
+ git show N8:foo/foo >actual.foo &&
+ git show N8:foo/bar >actual.bar &&
+ test_cmp expect.foo actual.foo &&
+ test_cmp expect.bar actual.bar
+'
+
+test_expect_success 'N: extract subtree' '
+ branch=$(git rev-parse --verify refs/heads/branch^{tree}) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N9
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ extract subtree branch:newdir
+ COMMIT
+
+ M 040000 $branch ""
+ C "newdir" ""
+ INPUT_END
+ git fast-import <input &&
+ git diff --exit-code branch:newdir N9
+'
+
+test_expect_success 'N: modify subtree, extract it, and modify again' '
+ echo hello >expect.baz &&
+ echo hello, world >expect.qux &&
+ git fast-import <<-SETUP_END &&
+ commit refs/heads/N10
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ hello, tree
+ COMMIT
+
+ deleteall
+ M 644 inline foo/bar/baz
+ data <<EOF
+ hello
+ EOF
+ SETUP_END
+
+ tree=$(git rev-parse --verify N10:) &&
+ git fast-import <<-INPUT_END &&
+ commit refs/heads/N11
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy to root by id and modify
+ COMMIT
+
+ M 040000 $tree ""
+ M 100644 inline foo/bar/qux
+ data <<EOF
+ hello, world
+ EOF
+ R "foo" ""
+ C "bar/qux" "bar/quux"
+ INPUT_END
+ git show N11:bar/baz >actual.baz &&
+ git show N11:bar/qux >actual.qux &&
+ git show N11:bar/quux >actual.quux &&
+ test_cmp expect.baz actual.baz &&
+ test_cmp expect.qux actual.qux &&
+ test_cmp expect.qux actual.quux'
###
### series O
###
-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'
+test_expect_success 'O: comments are all skipped' '
+ 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
+
+ # do not 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
+
+ # do not forget to copy file2 to file3
+ C file2 file3
+ #
+ # or to delete file5 from file2.
+ D file2/file5
+ # are we done yet?
+
+ INPUT_END
+
+ git fast-import <input &&
+ test $(git rev-parse N3) = $(git rev-parse O1)
+'
+
+test_expect_success 'O: blank lines not necessary after data commands' '
+ 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
+
+ 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
+'
+
+test_expect_success 'O: blank lines not necessary after other commands' '
+ 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
+
+ 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
+'
+
+test_expect_success 'O: progress outputs as requested by input' '
+ 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 done!
+ INPUT_END
+ 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
+test_expect_success 'P: superproject & submodule mix' '
+ 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
+
+ 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
+ ) &&
+ git submodule init &&
+ git submodule update
+'
-commit refs/heads/subuse3
-mark :2
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-corrupt
-COMMIT
+test_expect_success 'P: verbatim SHA gitlinks' '
+ 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
+
+ git branch -D sub &&
+ git gc &&
+ git prune &&
+ git fast-import <input &&
+ test $(git rev-parse --verify subuse2) = $(git rev-parse --verify subuse1)
+'
-from refs/heads/subuse2
-M 160000 :1 sub
+test_expect_success 'P: fail on inline gitlink' '
+ 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
-INPUT_END
+ test_must_fail git fast-import <input
+'
test_expect_success 'P: fail on blob mark in 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_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'
+test_expect_success 'Q: commit 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
+
+ git fast-import <input &&
+ git whatchanged notes-test
+'
+
+test_expect_success 'Q: verify pack' '
+ verify_packs
+'
+
+test_expect_success 'Q: verify first commit' '
+ 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
+ git cat-file commit notes-test~2 | sed 1d >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify second commit' '
+ 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
+ git cat-file commit notes-test^ | sed 1d >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify third commit' '
+ 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
+ git cat-file commit notes-test | sed 1d >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify first notes commit' '
+ 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
+ git cat-file commit refs/notes/foobar~2 | sed 1d >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify first notes tree' '
+ cat >expect.unsorted <<-EOF &&
+ 100644 blob $commit1
+ 100644 blob $commit2
+ 100644 blob $commit3
+ EOF
+ cat expect.unsorted | sort >expect &&
+ git cat-file -p refs/notes/foobar~2^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify first note for first commit' '
+ echo "$note1_data" >expect &&
+ git cat-file blob refs/notes/foobar~2:$commit1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify first note for second commit' '
+ echo "$note2_data" >expect &&
+ git cat-file blob refs/notes/foobar~2:$commit2 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify first note for third commit' '
+ echo "$note3_data" >expect &&
+ git cat-file blob refs/notes/foobar~2:$commit3 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify second notes commit' '
+ 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
+ git cat-file commit refs/notes/foobar^ | sed 1d >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify second notes tree' '
+ cat >expect.unsorted <<-EOF &&
+ 100644 blob $commit1
+ 100644 blob $commit2
+ 100644 blob $commit3
+ EOF
+ cat expect.unsorted | sort >expect &&
+ git cat-file -p refs/notes/foobar^^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify second note for first commit' '
+ echo "$note1b_data" >expect &&
+ git cat-file blob refs/notes/foobar^:$commit1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify first note for second commit' '
+ echo "$note2_data" >expect &&
+ git cat-file blob refs/notes/foobar^:$commit2 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify first note for third commit' '
+ echo "$note3_data" >expect &&
+ git cat-file blob refs/notes/foobar^:$commit3 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify third notes commit' '
+ 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
+ git cat-file commit refs/notes/foobar2 | sed 1d >actual &&
+ test_cmp expect actual
+'
+test_expect_success 'Q: verify third notes tree' '
+ cat >expect.unsorted <<-EOF &&
+ 100644 blob $commit1
+ EOF
+ cat expect.unsorted | sort >expect &&
+ git cat-file -p refs/notes/foobar2^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify third note for first commit' '
+ echo "$note1c_data" >expect &&
+ git cat-file blob refs/notes/foobar2:$commit1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify fourth notes commit' '
+ 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
+ git cat-file commit refs/notes/foobar | sed 1d >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify fourth notes tree' '
+ cat >expect.unsorted <<-EOF &&
+ 100644 blob $commit2
+ EOF
+ cat expect.unsorted | sort >expect &&
+ git cat-file -p refs/notes/foobar^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: verify second note for second commit' '
+ echo "$note2b_data" >expect &&
+ git cat-file blob refs/notes/foobar:$commit2 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Q: deny note on empty branch' '
+ cat >input <<-EOF &&
+ reset refs/heads/Q0
+
+ commit refs/heads/note-Q0
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ Note for an empty branch.
+ COMMIT
+
+ N inline refs/heads/Q0
+ data <<NOTE
+ some note
+ NOTE
+ EOF
+ test_must_fail git fast-import <input
+'
###
### series R (feature and option)
###
-cat >input <<EOF
-feature no-such-feature-exists
-EOF
-
test_expect_success 'R: abort on unsupported feature' '
+ cat >input <<-EOF &&
+ feature no-such-feature-exists
+ EOF
+
test_must_fail git fast-import <input
'
-cat >input <<EOF
-feature date-format=now
-EOF
-
test_expect_success 'R: supported feature is accepted' '
+ cat >input <<-EOF &&
+ feature date-format=now
+ EOF
+
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' '
+ cat >input <<-EOF &&
+ blob
+ data 3
+ hi
+ feature date-format=now
+ EOF
+
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' '
+ cat >input <<-EOF &&
+ feature import-marks=git.marks
+ feature import-marks=git2.marks
+ EOF
+
test_must_fail git fast-import <input
'
-cat >input << EOF
-feature export-marks=git.marks
-blob
-mark :1
-data 3
-hi
+test_expect_success 'R: export-marks feature results in a marks file being created' '
+ cat >input <<-EOF &&
+ feature export-marks=git.marks
+ blob
+ mark :1
+ data 3
+ hi
+
+ EOF
+
+ cat input | git fast-import &&
+ grep :1 git.marks
+'
+
+test_expect_success 'R: export-marks options can be overridden by commandline options' '
+ cat input | git fast-import --export-marks=other.marks &&
+ grep :1 other.marks
+'
+
+test_expect_success 'R: catch typo in marks file name' '
+ test_must_fail git fast-import --import-marks=nonexistent.marks </dev/null &&
+ echo "feature import-marks=nonexistent.marks" |
+ test_must_fail git fast-import
+'
-EOF
+test_expect_success 'R: import and output marks can be the same file' '
+ rm -f io.marks &&
+ blob=$(echo hi | git hash-object --stdin) &&
+ cat >expect <<-EOF &&
+ :1 $blob
+ :2 $blob
+ EOF
+ git fast-import --export-marks=io.marks <<-\EOF &&
+ blob
+ mark :1
+ data 3
+ hi
+
+ EOF
+ git fast-import --import-marks=io.marks --export-marks=io.marks <<-\EOF &&
+ blob
+ mark :2
+ data 3
+ hi
+
+ EOF
+ test_cmp expect io.marks
+'
-test_expect_success \
- 'R: export-marks feature results in a marks file being created' \
- 'cat input | git fast-import &&
- grep :1 git.marks'
+test_expect_success 'R: --import-marks=foo --output-marks=foo to create foo fails' '
+ rm -f io.marks &&
+ test_must_fail git fast-import --import-marks=io.marks --export-marks=io.marks <<-\EOF
+ blob
+ mark :1
+ data 3
+ hi
-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'
+ EOF
+'
-cat >input << EOF
-feature import-marks=marks.out
-feature export-marks=marks.new
-EOF
+test_expect_success 'R: --import-marks-if-exists' '
+ rm -f io.marks &&
+ blob=$(echo hi | git hash-object --stdin) &&
+ echo ":1 $blob" >expect &&
+ git fast-import --import-marks-if-exists=io.marks --export-marks=io.marks <<-\EOF &&
+ blob
+ mark :1
+ data 3
+ hi
+
+ EOF
+ test_cmp expect io.marks
+'
-test_expect_success \
- 'R: import to output marks works without any content' \
- 'cat input | git fast-import &&
- test_cmp marks.out marks.new'
+test_expect_success 'R: feature import-marks-if-exists' '
+ rm -f io.marks &&
+ >expect &&
-cat >input <<EOF
-feature import-marks=nonexistant.marks
-feature export-marks=marks.new
-EOF
+ git fast-import --export-marks=io.marks <<-\EOF &&
+ feature import-marks-if-exists=not_io.marks
+ EOF
+ test_cmp expect io.marks &&
-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'
+ blob=$(echo hi | git hash-object --stdin) &&
+ echo ":1 $blob" >io.marks &&
+ echo ":1 $blob" >expect &&
+ echo ":2 $blob" >>expect &&
-cat >input <<EOF
-feature import-marks=nonexistant.marks
-feature export-marks=combined.marks
-EOF
+ git fast-import --export-marks=io.marks <<-\EOF &&
+ feature import-marks-if-exists=io.marks
+ blob
+ mark :2
+ data 3
+ hi
-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
+ EOF
+ test_cmp expect io.marks &&
+
+ echo ":3 $blob" >>expect &&
+
+ git fast-import --import-marks=io.marks \
+ --export-marks=io.marks <<-\EOF &&
+ feature import-marks-if-exists=not_io.marks
+ blob
+ mark :3
+ data 3
+ hi
+
+ EOF
+ test_cmp expect io.marks &&
+
+ >expect &&
+
+ git fast-import --import-marks-if-exists=not_io.marks \
+ --export-marks=io.marks <<-\EOF &&
+ feature import-marks-if-exists=io.marks
+ EOF
+ test_cmp expect io.marks
'
-cat >input <<EOF
-feature relative-marks
-feature import-marks=relative.in
-feature export-marks=relative.out
-EOF
+test_expect_success 'R: import to output marks works without any content' '
+ cat >input <<-EOF &&
+ feature import-marks=marks.out
+ feature export-marks=marks.new
+ 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 | git fast-import &&
+ test_cmp marks.out marks.new
+'
+
+test_expect_success 'R: import marks prefers commandline marks file over the stream' '
+ cat >input <<-EOF &&
+ feature import-marks=nonexistent.marks
+ feature export-marks=marks.new
+ EOF
+
+ cat input | git fast-import --import-marks=marks.out &&
+ test_cmp marks.out marks.new
+'
+
+
+test_expect_success 'R: multiple --import-marks= should be honoured' '
+ cat >input <<-EOF &&
+ feature import-marks=nonexistent.marks
+ feature export-marks=combined.marks
+ EOF
+
+ 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 no-relative-marks
-feature export-marks=non-relative.out
-EOF
+test_expect_success 'R: feature relative-marks should be honoured' '
+ cat >input <<-EOF &&
+ feature relative-marks
+ feature import-marks=relative.in
+ feature export-marks=relative.out
+ EOF
+
+ 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
+'
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 &&
+ feature relative-marks
+ feature import-marks=relative.in
+ feature no-relative-marks
+ feature export-marks=non-relative.out
+ EOF
+
+ git fast-import <input &&
+ test_cmp marks.new non-relative.out
'
-cat >input << EOF
-option git quiet
-blob
-data 3
-hi
+test_expect_success 'R: feature ls supported' '
+ echo "feature ls" |
+ git fast-import
+'
+
+test_expect_success 'R: feature cat-blob supported' '
+ echo "feature cat-blob" |
+ git fast-import
+'
-EOF
+test_expect_success 'R: cat-blob-fd must be a nonnegative integer' '
+ test_must_fail git fast-import --cat-blob-fd=-1 </dev/null
+'
-touch empty
+test_expect_success !MINGW 'R: print old blob' '
+ blob=$(echo "yes it can" | git hash-object -w --stdin) &&
+ cat >expect <<-EOF &&
+ ${blob} blob 11
+ yes it can
+
+ EOF
+ echo "cat-blob $blob" |
+ git fast-import --cat-blob-fd=6 6>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success !MINGW 'R: in-stream cat-blob-fd not respected' '
+ echo hello >greeting &&
+ blob=$(git hash-object -w greeting) &&
+ cat >expect <<-EOF &&
+ ${blob} blob 6
+ hello
+
+ EOF
+ git fast-import --cat-blob-fd=3 3>actual.3 >actual.1 <<-EOF &&
+ cat-blob $blob
+ EOF
+ test_cmp expect actual.3 &&
+ test_must_be_empty actual.1 &&
+ git fast-import 3>actual.3 >actual.1 <<-EOF &&
+ option cat-blob-fd=3
+ cat-blob $blob
+ EOF
+ test_must_be_empty actual.3 &&
+ test_cmp expect actual.1
+'
+
+test_expect_success !MINGW 'R: print mark for new blob' '
+ echo "effluentish" | git hash-object --stdin >expect &&
+ git fast-import --cat-blob-fd=6 6>actual <<-\EOF &&
+ blob
+ mark :1
+ data <<BLOB_END
+ effluentish
+ BLOB_END
+ get-mark :1
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success !MINGW 'R: print new blob' '
+ blob=$(echo "yep yep yep" | git hash-object --stdin) &&
+ cat >expect <<-EOF &&
+ ${blob} blob 12
+ yep yep yep
+
+ EOF
+ git fast-import --cat-blob-fd=6 6>actual <<-\EOF &&
+ blob
+ mark :1
+ data <<BLOB_END
+ yep yep yep
+ BLOB_END
+ cat-blob :1
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success !MINGW 'R: print new blob by sha1' '
+ blob=$(echo "a new blob named by sha1" | git hash-object --stdin) &&
+ cat >expect <<-EOF &&
+ ${blob} blob 25
+ a new blob named by sha1
+
+ EOF
+ git fast-import --cat-blob-fd=6 6>actual <<-EOF &&
+ blob
+ data <<BLOB_END
+ a new blob named by sha1
+ BLOB_END
+ cat-blob $blob
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'setup: big file' '
+ (
+ echo "the quick brown fox jumps over the lazy dog" >big &&
+ for i in 1 2 3
+ do
+ cat big big big big >bigger &&
+ cat bigger bigger bigger bigger >big ||
+ exit
+ done
+ )
+'
+
+test_expect_success 'R: print two blobs to stdout' '
+ blob1=$(git hash-object big) &&
+ blob1_len=$(wc -c <big) &&
+ blob2=$(echo hello | git hash-object --stdin) &&
+ {
+ echo ${blob1} blob $blob1_len &&
+ cat big &&
+ cat <<-EOF
+
+ ${blob2} blob 6
+ hello
+
+ EOF
+ } >expect &&
+ {
+ cat <<-\END_PART1 &&
+ blob
+ mark :1
+ data <<data_end
+ END_PART1
+ cat big &&
+ cat <<-\EOF
+ data_end
+ blob
+ mark :2
+ data <<data_end
+ hello
+ data_end
+ cat-blob :1
+ cat-blob :2
+ EOF
+ } |
+ git fast-import >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success PIPE 'R: copy using cat-file' '
+ expect_id=$(git hash-object big) &&
+ expect_len=$(wc -c <big) &&
+ echo $expect_id blob $expect_len >expect.response &&
+
+ rm -f blobs &&
+ cat >frontend <<-\FRONTEND_END &&
+ #!/bin/sh
+ FRONTEND_END
+
+ mkfifo blobs &&
+ (
+ export GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL GIT_COMMITTER_DATE &&
+ cat <<-\EOF &&
+ feature cat-blob
+ blob
+ mark :1
+ data <<BLOB
+ EOF
+ cat big &&
+ cat <<-\EOF &&
+ BLOB
+ cat-blob :1
+ EOF
+
+ read blob_id type size <&3 &&
+ echo "$blob_id $type $size" >response &&
+ head_c $size >blob <&3 &&
+ read newline <&3 &&
+
+ cat <<-EOF &&
+ commit refs/heads/copied
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy big file as file3
+ COMMIT
+ M 644 inline file3
+ data <<BLOB
+ EOF
+ cat blob &&
+ echo BLOB
+ ) 3<blobs |
+ git fast-import --cat-blob-fd=3 3>blobs &&
+ git show copied:file3 >actual &&
+ test_cmp expect.response response &&
+ test_cmp big actual
+'
+
+test_expect_success PIPE 'R: print blob mid-commit' '
+ rm -f blobs &&
+ echo "A blob from _before_ the commit." >expect &&
+ mkfifo blobs &&
+ (
+ exec 3<blobs &&
+ cat <<-EOF &&
+ feature cat-blob
+ blob
+ mark :1
+ data <<BLOB
+ A blob from _before_ the commit.
+ BLOB
+ commit refs/heads/temporary
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ Empty commit
+ COMMIT
+ cat-blob :1
+ EOF
+
+ read blob_id type size <&3 &&
+ head_c $size >actual <&3 &&
+ read newline <&3 &&
+
+ echo
+ ) |
+ git fast-import --cat-blob-fd=3 3>blobs &&
+ test_cmp expect actual
+'
+
+test_expect_success PIPE 'R: print staged blob within commit' '
+ rm -f blobs &&
+ echo "A blob from _within_ the commit." >expect &&
+ mkfifo blobs &&
+ (
+ exec 3<blobs &&
+ cat <<-EOF &&
+ feature cat-blob
+ commit refs/heads/within
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ Empty commit
+ COMMIT
+ M 644 inline within
+ data <<BLOB
+ A blob from _within_ the commit.
+ BLOB
+ EOF
+
+ to_get=$(
+ echo "A blob from _within_ the commit." |
+ git hash-object --stdin
+ ) &&
+ echo "cat-blob $to_get" &&
+
+ read blob_id type size <&3 &&
+ head_c $size >actual <&3 &&
+ read newline <&3 &&
+
+ echo deleteall
+ ) |
+ git fast-import --cat-blob-fd=3 3>blobs &&
+ test_cmp expect actual
+'
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 quiet
+ blob
+ data 3
+ hi
+
+ EOF
+
+ cat input | git fast-import 2> output &&
+ test_must_be_empty output
+'
+
+test_expect_success 'R: feature done means terminating "done" is mandatory' '
+ echo feature done | test_must_fail git fast-import &&
+ test_must_fail git fast-import --done </dev/null
+'
+
+test_expect_success 'R: terminating "done" with trailing gibberish is ok' '
+ git fast-import <<-\EOF &&
+ feature done
+ done
+ trailing gibberish
+ EOF
+ git fast-import <<-\EOF
+ done
+ more trailing gibberish
+ EOF
'
-cat >input <<EOF
-option git non-existing-option
-EOF
+test_expect_success 'R: terminating "done" within commit' '
+ cat >expect <<-\EOF &&
+ OBJID
+ :000000 100644 OBJID OBJID A hello.c
+ :000000 100644 OBJID OBJID A hello2.c
+ EOF
+ git fast-import <<-EOF &&
+ commit refs/heads/done-ends
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<EOT
+ Commit terminated by "done" command
+ EOT
+ M 100644 inline hello.c
+ data <<EOT
+ Hello, world.
+ EOT
+ C hello.c hello2.c
+ done
+ EOF
+ git rev-list done-ends |
+ git diff-tree -r --stdin --root --always |
+ sed -e "s/$_x40/OBJID/g" >actual &&
+ test_cmp expect actual
+'
test_expect_success 'R: die on unknown option' '
- test_must_fail git fast-import <input
+ cat >input <<-EOF &&
+ option git non-existing-option
+ EOF
+
+ 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
+ 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: die on invalid option argument' '
+ echo "option git active-branches=-5" |
+ test_must_fail git fast-import &&
+ echo "option git depth=" |
+ test_must_fail git fast-import &&
+ test_must_fail git fast-import --depth="5 elephants" </dev/null
+'
test_expect_success 'R: ignore non-git options' '
- git fast-import <input
+ cat >input <<-EOF &&
+ option non-existing-vcs non-existing-option
+ EOF
+
+ git fast-import <input
+'
+
+test_expect_success 'R: corrupt lines do not mess marks file' '
+ rm -f io.marks &&
+ blob=$(echo hi | git hash-object --stdin) &&
+ cat >expect <<-EOF &&
+ :3 0000000000000000000000000000000000000000
+ :1 $blob
+ :2 $blob
+ EOF
+ cp expect io.marks &&
+ test_must_fail git fast-import --import-marks=io.marks --export-marks=io.marks <<-\EOF &&
+
+ EOF
+ test_cmp expect io.marks
'
##
## 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_expect_success 'R: blob bigger than threshold' '
+ 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_create_repo R &&
+ git --git-dir=R/.git fast-import --big-file-threshold=1 <input
+'
+
+test_expect_success 'R: verify created pack' '
+ (
+ cd R &&
+ verify_packs -v > ../verify
+ )
+'
+
+test_expect_success 'R: verify written objects' '
+ git --git-dir=R/.git cat-file blob big-file:big1 >actual &&
+ test_cmp_bin 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
+'
+
+###
+### series S
+###
+#
+# Make sure missing spaces and EOLs after mark references
+# cause errors.
+#
+# Setup:
+#
+# 1--2--4
+# \ /
+# -3-
+#
+# commit marks: 301, 302, 303, 304
+# blob marks: 403, 404, resp.
+# note mark: 202
+#
+# The error message when a space is missing not at the
+# end of the line is:
+#
+# Missing space after ..
+#
+# or when extra characters come after the mark at the end
+# of the line:
+#
+# Garbage after ..
+#
+# or when the dataref is neither "inline " or a known SHA1,
+#
+# Invalid dataref ..
+#
+test_expect_success 'S: initialize for S tests' '
+ test_tick &&
+
+ cat >input <<-INPUT_END &&
+ commit refs/heads/S
+ mark :301
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit 1
+ COMMIT
+ M 100644 inline hello.c
+ data <<BLOB
+ blob 1
+ BLOB
+
+ commit refs/heads/S
+ mark :302
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit 2
+ COMMIT
+ from :301
+ M 100644 inline hello.c
+ data <<BLOB
+ blob 2
+ BLOB
+
+ blob
+ mark :403
+ data <<BLOB
+ blob 3
+ BLOB
+
+ blob
+ mark :202
+ data <<BLOB
+ note 2
+ BLOB
+ INPUT_END
+
+ git fast-import --export-marks=marks <input
+'
+
+#
+# filemodify, three datarefs
+#
+test_expect_success 'S: filemodify with garbage after mark must fail' '
+ test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+ commit refs/heads/S
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit N
+ COMMIT
+ M 100644 :403x hello.c
+ EOF
+ cat err &&
+ test_i18ngrep "space after mark" err
+'
+
+# inline is misspelled; fast-import thinks it is some unknown dataref
+test_expect_success 'S: filemodify with garbage after inline must fail' '
+ test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+ commit refs/heads/S
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit N
+ COMMIT
+ M 100644 inlineX hello.c
+ data <<BLOB
+ inline
+ BLOB
+ EOF
+ cat err &&
+ test_i18ngrep "nvalid dataref" err
+'
+
+test_expect_success 'S: filemodify with garbage after sha1 must fail' '
+ sha1=$(grep :403 marks | cut -d\ -f2) &&
+ test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+ commit refs/heads/S
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit N
+ COMMIT
+ M 100644 ${sha1}x hello.c
+ EOF
+ cat err &&
+ test_i18ngrep "space after SHA1" err
+'
+
+#
+# notemodify, three ways to say dataref
+#
+test_expect_success 'S: notemodify with garabge after mark dataref must fail' '
+ test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+ commit refs/heads/S
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit S note dataref markref
+ COMMIT
+ N :202x :302
+ EOF
+ cat err &&
+ test_i18ngrep "space after mark" err
+'
+
+test_expect_success 'S: notemodify with garbage after inline dataref must fail' '
+ test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+ commit refs/heads/S
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit S note dataref inline
+ COMMIT
+ N inlineX :302
+ data <<BLOB
+ note blob
+ BLOB
+ EOF
+ cat err &&
+ test_i18ngrep "nvalid dataref" err
+'
+
+test_expect_success 'S: notemodify with garbage after sha1 dataref must fail' '
+ sha1=$(grep :202 marks | cut -d\ -f2) &&
+ test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+ commit refs/heads/S
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit S note dataref sha1
+ COMMIT
+ N ${sha1}x :302
+ EOF
+ cat err &&
+ test_i18ngrep "space after SHA1" err
+'
+
+#
+# notemodify, mark in commit-ish
+#
+test_expect_success 'S: notemodify with garbage after mark commit-ish must fail' '
+ test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+ commit refs/heads/Snotes
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit S note commit-ish
+ COMMIT
+ N :202 :302x
+ EOF
+ cat err &&
+ test_i18ngrep "after mark" err
+'
+
+#
+# from
+#
+test_expect_success 'S: from with garbage after mark must fail' '
+ test_must_fail \
+ git fast-import --import-marks=marks --export-marks=marks <<-EOF 2>err &&
+ commit refs/heads/S2
+ mark :303
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit 3
+ COMMIT
+ from :301x
+ M 100644 :403 hello.c
+ EOF
+
+
+ # go create the commit, need it for merge test
+ git fast-import --import-marks=marks --export-marks=marks <<-EOF &&
+ commit refs/heads/S2
+ mark :303
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit 3
+ COMMIT
+ from :301
+ M 100644 :403 hello.c
+ EOF
+
+ # now evaluate the error
+ cat err &&
+ test_i18ngrep "after mark" err
+'
+
+
+#
+# merge
+#
+test_expect_success 'S: merge with garbage after mark must fail' '
+ test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+ commit refs/heads/S
+ mark :304
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ merge 4
+ COMMIT
+ from :302
+ merge :303x
+ M 100644 :403 hello.c
+ EOF
+ cat err &&
+ test_i18ngrep "after mark" err
+'
+
+#
+# tag, from markref
+#
+test_expect_success 'S: tag with garbage after mark must fail' '
+ test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+ tag refs/tags/Stag
+ from :302x
+ tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<TAG
+ tag S
+ TAG
+ EOF
+ cat err &&
+ test_i18ngrep "after mark" err
+'
+
+#
+# cat-blob markref
+#
+test_expect_success 'S: cat-blob with garbage after mark must fail' '
+ test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+ cat-blob :403x
+ EOF
+ cat err &&
+ test_i18ngrep "after mark" err
+'
+
+#
+# ls markref
+#
+test_expect_success 'S: ls with garbage after mark must fail' '
+ test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+ ls :302x hello.c
+ EOF
+ cat err &&
+ test_i18ngrep "space after mark" err
+'
+
+test_expect_success 'S: ls with garbage after sha1 must fail' '
+ sha1=$(grep :302 marks | cut -d\ -f2) &&
+ test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+ ls ${sha1}x hello.c
+ EOF
+ cat err &&
+ test_i18ngrep "space after tree-ish" err
+'
+
+###
+### series T (ls)
+###
+# Setup is carried over from series S.
+
+test_expect_success 'T: ls root tree' '
+ sed -e "s/Z\$//" >expect <<-EOF &&
+ 040000 tree $(git rev-parse S^{tree}) Z
+ EOF
+ sha1=$(git rev-parse --verify S) &&
+ git fast-import --import-marks=marks <<-EOF >actual &&
+ ls $sha1 ""
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'T: delete branch' '
+ git branch to-delete &&
+ git fast-import <<-EOF &&
+ reset refs/heads/to-delete
+ from 0000000000000000000000000000000000000000
+ EOF
+ test_must_fail git rev-parse --verify refs/heads/to-delete
+'
+
+test_expect_success 'T: empty reset doesnt delete branch' '
+ git branch not-to-delete &&
+ git fast-import <<-EOF &&
+ reset refs/heads/not-to-delete
+ EOF
+ git show-ref &&
+ git rev-parse --verify refs/heads/not-to-delete
+'
+
+###
+### series U (filedelete)
+###
+
+test_expect_success 'U: initialize for U tests' '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/U
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ test setup
+ COMMIT
+ M 100644 inline hello.c
+ data <<BLOB
+ blob 1
+ BLOB
+ M 100644 inline good/night.txt
+ data <<BLOB
+ sleep well
+ BLOB
+ M 100644 inline good/bye.txt
+ data <<BLOB
+ au revoir
+ BLOB
+
+ INPUT_END
+
+ git fast-import <input
+'
+
+test_expect_success 'U: filedelete file succeeds' '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/U
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ delete good/night.txt
+ COMMIT
+ from refs/heads/U^0
+ D good/night.txt
+
+ INPUT_END
+
+ git fast-import <input
+'
+
+test_expect_success 'U: validate file delete result' '
+ cat >expect <<-EOF &&
+ :100644 000000 2907ebb4bf85d91bf0716bb3bd8a68ef48d6da76 0000000000000000000000000000000000000000 D good/night.txt
+ EOF
+
+ git diff-tree -M -r U^1 U >actual &&
+
+ compare_diff_raw expect actual
+'
+
+test_expect_success 'U: filedelete directory succeeds' '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/U
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ delete good dir
+ COMMIT
+ from refs/heads/U^0
+ D good
+
+ INPUT_END
+
+ git fast-import <input
+'
+
+test_expect_success 'U: validate directory delete result' '
+ cat >expect <<-EOF &&
+ :100644 000000 69cb75792f55123d8389c156b0b41c2ff00ed507 0000000000000000000000000000000000000000 D good/bye.txt
+ EOF
+
+ git diff-tree -M -r U^1 U >actual &&
+
+ compare_diff_raw expect actual
+'
+
+test_expect_success 'U: filedelete root succeeds' '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/U
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ must succeed
+ COMMIT
+ from refs/heads/U^0
+ D ""
+
+ INPUT_END
+
+ git fast-import <input
+'
+
+test_expect_success 'U: validate root delete result' '
+ cat >expect <<-EOF &&
+ :100644 000000 c18147dc648481eeb65dc5e66628429a64843327 0000000000000000000000000000000000000000 D hello.c
+ EOF
+
+ git diff-tree -M -r U^1 U >actual &&
+
+ compare_diff_raw expect actual
+'
test_done
diff --git a/t/t9301-fast-import-notes.sh b/t/t9301-fast-import-notes.sh
index a5c99d8..83acf68 100755
--- a/t/t9301-fast-import-notes.sh
+++ b/t/t9301-fast-import-notes.sh
@@ -120,6 +120,7 @@ test_expect_success 'add notes with simple M command' '
test_tick
cat >input <<INPUT_END
+feature notes
commit refs/notes/test
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data <<COMMIT
@@ -255,13 +256,18 @@ EOF
INPUT_END
+whitespace=" "
+
cat >expect <<EXPECT_END
fourth commit
pre-prefix of note for fourth commit
+$whitespace
prefix of note for fourth commit
+$whitespace
third note for fourth commit
third commit
prefix of note for third commit
+$whitespace
third note for third commit
second commit
third note for second commit
@@ -499,9 +505,63 @@ test_expect_success 'verify that non-notes are untouched by a fanout change' '
test_cmp expect_non-note3 actual
'
+
+# Change the notes for the three top commits
+test_tick
+cat >input <<INPUT_END
+commit refs/notes/many_notes
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+changing notes for the top three commits
+COMMIT
+from refs/notes/many_notes^0
+INPUT_END
+
+rm expect
+i=$num_commits
+j=0
+while test $j -lt 3
+do
+ cat >>input <<INPUT_END
+N inline refs/heads/many_commits~$j
+data <<EOF
+changed note for commit #$i
+EOF
+INPUT_END
+ cat >>expect <<EXPECT_END
+ commit #$i
+ changed note for commit #$i
+EXPECT_END
+ i=$(($i - 1))
+ j=$(($j + 1))
+done
+
+test_expect_success 'change a few existing notes' '
+
+ git fast-import <input &&
+ GIT_NOTES_REF=refs/notes/many_notes git log -n3 refs/heads/many_commits |
+ grep "^ " > actual &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'verify that changing notes respect existing fanout' '
+
+ # 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
+
+'
+
remaining_notes=10
test_tick
-cat >>input <<INPUT_END
+cat >input <<INPUT_END
commit refs/notes/many_notes
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data <<COMMIT
@@ -510,12 +570,11 @@ COMMIT
from refs/notes/many_notes^0
INPUT_END
-i=$remaining_notes
-while test $i -lt $num_commits
+i=$(($num_commits - $remaining_notes))
+for sha1 in $(git rev-list -n $i refs/heads/many_commits)
do
- i=$(($i + 1))
cat >>input <<INPUT_END
-N 0000000000000000000000000000000000000000 :$i
+N 0000000000000000000000000000000000000000 $sha1
INPUT_END
done
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
index d43f37c..b5149fd 100755
--- a/t/t9350-fast-export.sh
+++ b/t/t9350-fast-export.sh
@@ -26,7 +26,7 @@ test_expect_success 'setup' '
test_tick &&
git tag rein &&
git checkout -b wer HEAD^ &&
- echo lange > file2
+ echo lange > file2 &&
test_tick &&
git commit -m sitzt file2 &&
test_tick &&
@@ -86,7 +86,7 @@ test_expect_success 'import/export-marks' '
git checkout -b marks master &&
git fast-export --export-marks=tmp-marks HEAD &&
test -s tmp-marks &&
- test $(wc -l < tmp-marks) -eq 3 &&
+ test_line_count = 3 tmp-marks &&
test $(
git fast-export --import-marks=tmp-marks\
--export-marks=tmp-marks HEAD |
@@ -101,7 +101,7 @@ test_expect_success 'import/export-marks' '
grep ^commit\ |
wc -l) \
-eq 1 &&
- test $(wc -l < tmp-marks) -eq 4
+ test_line_count = 4 tmp-marks
'
@@ -146,6 +146,12 @@ test_expect_success 'signed-tags=strip' '
'
+test_expect_success 'signed-tags=warn-strip' '
+ git fast-export --signed-tags=warn-strip sign-your-name >output 2>err &&
+ ! grep PGP output &&
+ test -s err
+'
+
test_expect_success 'setup submodule' '
git checkout -f master &&
@@ -157,7 +163,7 @@ test_expect_success 'setup submodule' '
git add file &&
git commit -m sub_initial
) &&
- git submodule add "`pwd`/sub" sub &&
+ git submodule add "$(pwd)/sub" sub &&
git commit -m initial &&
test_tick &&
(
@@ -228,7 +234,7 @@ test_expect_success 'fast-export -C -C | fast-import' '
mkdir new &&
git --git-dir=new/.git init &&
git fast-export -C -C --signed-tags=strip --all > output &&
- grep "^C \"file6\" \"file7\"\$" output &&
+ grep "^C file6 file7\$" output &&
cat output |
(cd new &&
git fast-import &&
@@ -303,7 +309,7 @@ test_expect_success 'dropping tag of filtered out object' '
(
cd limit-by-paths &&
git fast-export --tag-of-filtered-object=drop mytag -- there > output &&
- test_cmp output expected
+ test_cmp expected output
)
'
@@ -320,7 +326,7 @@ test_expect_success 'rewriting tag of filtered out object' '
(
cd limit-by-paths &&
git fast-export --tag-of-filtered-object=rewrite mytag -- there > output &&
- test_cmp output expected
+ test_cmp expected output
)
'
@@ -351,13 +357,27 @@ test_expect_failure 'no exact-ref revisions included' '
(
cd limit-by-paths &&
git fast-export master~2..master~1 > output &&
- test_cmp output expected
+ test_cmp expected output
)
'
+test_expect_success 'path limiting with import-marks does not lose unmodified files' '
+ git checkout -b simple marks~2 &&
+ git fast-export --export-marks=marks simple -- file > /dev/null &&
+ echo more content >> file &&
+ test_tick &&
+ git commit -mnext file &&
+ git fast-export --import-marks=marks simple -- file file0 | grep file0
+'
+
+test_expect_success 'full-tree re-shows unmodified files' '
+ git checkout -f simple &&
+ test $(git fast-export --full-tree simple | grep -c file0) -eq 3
+'
+
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 //"` &&
+ 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 &&
@@ -376,4 +396,130 @@ test_expect_success 'tree_tag-obj' 'git fast-export tree_tag-obj'
test_expect_success 'tag-obj_tag' 'git fast-export tag-obj_tag'
test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj'
+test_expect_success 'directory becomes symlink' '
+ git init dirtosymlink &&
+ git init result &&
+ (
+ cd dirtosymlink &&
+ mkdir foo &&
+ mkdir bar &&
+ echo hello > foo/world &&
+ echo hello > bar/world &&
+ git add foo/world bar/world &&
+ git commit -q -mone &&
+ git rm -r foo &&
+ test_ln_s_add bar foo &&
+ git commit -q -mtwo
+ ) &&
+ (
+ cd dirtosymlink &&
+ git fast-export master -- foo |
+ (cd ../result && git fast-import --quiet)
+ ) &&
+ (cd result && git show master:foo)
+'
+
+test_expect_success 'fast-export quotes pathnames' '
+ git init crazy-paths &&
+ (cd crazy-paths &&
+ blob=$(echo foo | git hash-object -w --stdin) &&
+ git update-index --add \
+ --cacheinfo 100644 $blob "$(printf "path with\\nnewline")" \
+ --cacheinfo 100644 $blob "path with \"quote\"" \
+ --cacheinfo 100644 $blob "path with \\backslash" \
+ --cacheinfo 100644 $blob "path with space" &&
+ git commit -m addition &&
+ git ls-files -z -s | perl -0pe "s{\\t}{$&subdir/}" >index &&
+ git read-tree --empty &&
+ git update-index -z --index-info <index &&
+ git commit -m rename &&
+ git read-tree --empty &&
+ git commit -m deletion &&
+ git fast-export -M HEAD >export.out &&
+ git rev-list HEAD >expect &&
+ git init result &&
+ cd result &&
+ git fast-import <../export.out &&
+ git rev-list HEAD >actual &&
+ test_cmp ../expect actual
+ )
+'
+
+test_expect_success 'test bidirectionality' '
+ >marks-cur &&
+ >marks-new &&
+ git init marks-test &&
+ git fast-export --export-marks=marks-cur --import-marks=marks-cur --branches | \
+ git --git-dir=marks-test/.git fast-import --export-marks=marks-new --import-marks=marks-new &&
+ (cd marks-test &&
+ git reset --hard &&
+ echo Wohlauf > file &&
+ git commit -a -m "back in time") &&
+ git --git-dir=marks-test/.git fast-export --export-marks=marks-new --import-marks=marks-new --branches | \
+ git fast-import --export-marks=marks-cur --import-marks=marks-cur
+'
+
+cat > expected << EOF
+blob
+mark :13
+data 5
+bump
+
+commit refs/heads/master
+mark :14
+author A U Thor <author@example.com> 1112912773 -0700
+committer C O Mitter <committer@example.com> 1112912773 -0700
+data 5
+bump
+from :12
+M 100644 :13 file
+
+EOF
+
+test_expect_success 'avoid uninteresting refs' '
+ > tmp-marks &&
+ git fast-export --import-marks=tmp-marks \
+ --export-marks=tmp-marks master > /dev/null &&
+ git tag v1.0 &&
+ git branch uninteresting &&
+ echo bump > file &&
+ git commit -a -m bump &&
+ git fast-export --import-marks=tmp-marks \
+ --export-marks=tmp-marks ^uninteresting ^v1.0 master > actual &&
+ test_cmp expected actual
+'
+
+cat > expected << EOF
+reset refs/heads/master
+from :14
+
+EOF
+
+test_expect_success 'refs are updated even if no commits need to be exported' '
+ > tmp-marks &&
+ git fast-export --import-marks=tmp-marks \
+ --export-marks=tmp-marks master > /dev/null &&
+ git fast-export --import-marks=tmp-marks \
+ --export-marks=tmp-marks master > actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'use refspec' '
+ git fast-export --refspec refs/heads/master:refs/heads/foobar master | \
+ grep "^commit " | sort | uniq > actual &&
+ echo "commit refs/heads/foobar" > expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'delete refspec' '
+ git branch to-delete &&
+ git fast-export --refspec :refs/heads/to-delete to-delete ^to-delete > actual &&
+ cat > expected <<-EOF &&
+ reset refs/heads/to-delete
+ from 0000000000000000000000000000000000000000
+
+ EOF
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t9351-fast-export-anonymize.sh b/t/t9351-fast-export-anonymize.sh
new file mode 100755
index 0000000..897dc50
--- /dev/null
+++ b/t/t9351-fast-export-anonymize.sh
@@ -0,0 +1,112 @@
+#!/bin/sh
+
+test_description='basic tests for fast-export --anonymize'
+. ./test-lib.sh
+
+test_expect_success 'setup simple repo' '
+ test_commit base &&
+ test_commit foo &&
+ git checkout -b other HEAD^ &&
+ mkdir subdir &&
+ test_commit subdir/bar &&
+ test_commit subdir/xyzzy &&
+ git tag -m "annotated tag" mytag
+'
+
+test_expect_success 'export anonymized stream' '
+ git fast-export --anonymize --all >stream
+'
+
+# this also covers commit messages
+test_expect_success 'stream omits path names' '
+ ! grep base stream &&
+ ! grep foo stream &&
+ ! grep subdir stream &&
+ ! grep bar stream &&
+ ! grep xyzzy stream
+'
+
+test_expect_success 'stream allows master as refname' '
+ grep master stream
+'
+
+test_expect_success 'stream omits other refnames' '
+ ! grep other stream &&
+ ! grep mytag stream
+'
+
+test_expect_success 'stream omits identities' '
+ ! grep "$GIT_COMMITTER_NAME" stream &&
+ ! grep "$GIT_COMMITTER_EMAIL" stream &&
+ ! grep "$GIT_AUTHOR_NAME" stream &&
+ ! grep "$GIT_AUTHOR_EMAIL" stream
+'
+
+test_expect_success 'stream omits tag message' '
+ ! grep "annotated tag" stream
+'
+
+# NOTE: we chdir to the new, anonymized repository
+# after this. All further tests should assume this.
+test_expect_success 'import stream to new repository' '
+ git init new &&
+ cd new &&
+ git fast-import <../stream
+'
+
+test_expect_success 'result has two branches' '
+ git for-each-ref --format="%(refname)" refs/heads >branches &&
+ test_line_count = 2 branches &&
+ other_branch=$(grep -v refs/heads/master branches)
+'
+
+test_expect_success 'repo has original shape and timestamps' '
+ shape () {
+ git log --format="%m %ct" --left-right --boundary "$@"
+ } &&
+ (cd .. && shape master...other) >expect &&
+ shape master...$other_branch >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'root tree has original shape' '
+ # the output entries are not necessarily in the same
+ # order, but we know at least that we will have one tree
+ # and one blob, so just check the sorted order
+ cat >expect <<-\EOF &&
+ blob
+ tree
+ EOF
+ git ls-tree $other_branch >root &&
+ cut -d" " -f2 <root | sort >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'paths in subdir ended up in one tree' '
+ cat >expect <<-\EOF &&
+ blob
+ blob
+ EOF
+ tree=$(grep tree root | cut -f2) &&
+ git ls-tree $other_branch:$tree >tree &&
+ cut -d" " -f2 <tree >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'tag points to branch tip' '
+ git rev-parse $other_branch >expect &&
+ git for-each-ref --format="%(*objectname)" | grep . >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'idents are shared' '
+ git log --all --format="%an <%ae>" >authors &&
+ sort -u authors >unique &&
+ test_line_count = 1 unique &&
+ git log --all --format="%cn <%ce>" >committers &&
+ sort -u committers >unique &&
+ test_line_count = 1 unique &&
+ ! test_cmp authors committers
+'
+
+test_done
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index 36c457e..432c61d 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -20,22 +20,22 @@ then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
-"$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
+perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
test_done
}
-unset GIT_DIR GIT_CONFIG
-WORKDIR=$(pwd)
-SERVERDIR=$(pwd)/gitcvs.git
+WORKDIR=$PWD
+SERVERDIR=$PWD/gitcvs.git
git_config="$SERVERDIR/config"
CVSROOT=":fork:$SERVERDIR"
-CVSWORK="$(pwd)/cvswork"
+CVSWORK="$PWD/cvswork"
CVS_SERVER=git-cvsserver
export CVSROOT CVS_SERVER
rm -rf "$CVSWORK" "$SERVERDIR"
test_expect_success 'setup' '
+ git config push.default matching &&
echo >empty &&
git add empty &&
git commit -q -m "First Commit" &&
@@ -45,7 +45,8 @@ test_expect_success 'setup' '
touch secondrootfile &&
git add secondrootfile &&
git commit -m "second root") &&
- git pull secondroot master &&
+ git fetch secondroot master &&
+ git merge --allow-unrelated-histories FETCH_HEAD &&
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" &&
@@ -57,7 +58,7 @@ test_expect_success 'setup' '
# 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 | 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/"'
#------------------------
@@ -400,7 +401,7 @@ cat >expected.C <<EOF
Line 0
=======
LINE 0
->>>>>>> merge.3
+>>>>>>> merge.1.3
EOF
for i in 1 2 3 4 5 6 7 8
@@ -476,14 +477,14 @@ test_expect_success 'cvs status' '
cd cvswork &&
GIT_CONFIG="$git_config" cvs update &&
GIT_CONFIG="$git_config" cvs status | grep "^File: status.file" >../out &&
- test $(wc -l <../out) = 2
+ test_line_count = 2 ../out
'
cd "$WORKDIR"
test_expect_success 'cvs status (nonrecursive)' '
cd cvswork &&
GIT_CONFIG="$git_config" cvs status -l | grep "^File: status.file" >../out &&
- test $(wc -l <../out) = 1
+ test_line_count = 1 ../out
'
cd "$WORKDIR"
@@ -500,8 +501,78 @@ test_expect_success 'cvs status (no subdirs in header)' '
cd "$WORKDIR"
test_expect_success 'cvs co -c (shows module database)' '
GIT_CONFIG="$git_config" cvs co -c > out &&
- grep "^master[ ]\+master$" < out &&
- ! grep -v "^master[ ]\+master$" < out
+ grep "^master[ ][ ]*master$" <out &&
+ ! grep -v "^master[ ][ ]*master$" <out
+'
+
+#------------
+# CVS LOG
+#------------
+
+# Known issues with git-cvsserver current log output:
+# - Hard coded "lines: +2 -3" placeholder, instead of real numbers.
+# - CVS normally does not internally add a blank first line
+# or a last line with nothing but a space to log messages.
+# - The latest cvs 1.12.x server sends +0000 timezone (with some hidden "MT"
+# tagging in the protocol), and if cvs 1.12.x client sees the MT tags,
+# it converts to local time zone. git-cvsserver doesn't do the +0000
+# or the MT tags...
+# - The latest 1.12.x releases add a "commitid:" field on to the end of the
+# "date:" line (after "lines:"). Maybe we could stick git's commit id
+# in it? Or does CVS expect a certain number of bits (too few for
+# a full sha1)?
+#
+# Given the above, expect the following test to break if git-cvsserver's
+# log output is improved. The test is just to ensure it doesn't
+# accidentally get worse.
+
+sed -e 's/^x//' -e 's/SP$/ /' > "$WORKDIR/expect" <<EOF
+x
+xRCS file: $WORKDIR/gitcvs.git/master/merge,v
+xWorking file: merge
+xhead: 1.4
+xbranch:
+xlocks: strict
+xaccess list:
+xsymbolic names:
+xkeyword substitution: kv
+xtotal revisions: 4; selected revisions: 4
+xdescription:
+x----------------------------
+xrevision 1.4
+xdate: __DATE__; author: author; state: Exp; lines: +2 -3
+x
+xMerge test (no-op)
+xSP
+x----------------------------
+xrevision 1.3
+xdate: __DATE__; author: author; state: Exp; lines: +2 -3
+x
+xMerge test (conflict)
+xSP
+x----------------------------
+xrevision 1.2
+xdate: __DATE__; author: author; state: Exp; lines: +2 -3
+x
+xMerge test (merge)
+xSP
+x----------------------------
+xrevision 1.1
+xdate: __DATE__; author: author; state: Exp; lines: +2 -3
+x
+xMerge test (pre-merge)
+xSP
+x=============================================================================
+EOF
+expectStat="$?"
+
+cd "$WORKDIR"
+test_expect_success 'cvs log' '
+ cd cvswork &&
+ test x"$expectStat" = x"0" &&
+ GIT_CONFIG="$git_config" cvs log merge >../out &&
+ sed -e "s%2[0-9][0-9][0-9]/[01][0-9]/[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9]%__DATE__%" ../out > ../actual &&
+ test_cmp ../expect ../actual
'
#------------
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
index 925bd0f..f324b9f 100755
--- a/t/t9401-git-cvsserver-crlf.sh
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -38,6 +38,25 @@ not_present() {
fi
}
+check_status_options() {
+ (cd "$1" &&
+ GIT_CONFIG="$git_config" cvs -Q status "$2" > "${WORKDIR}/status.out" 2>&1
+ )
+ if [ x"$?" != x"0" ] ; then
+ echo "Error from cvs status: $1 $2" >> "${WORKDIR}/marked.log"
+ return 1;
+ fi
+ got="$(sed -n -e 's/^[ ]*Sticky Options:[ ]*//p' "${WORKDIR}/status.out")"
+ expect="$3"
+ if [ x"$expect" = x"" ] ; then
+ expect="(none)"
+ fi
+ test x"$got" = x"$expect"
+ stat=$?
+ echo "cvs status: $1 $2 $stat '$3' '$got'" >> "${WORKDIR}/marked.log"
+ return $stat
+}
+
cvs >/dev/null 2>&1
if test $? -ne 1
then
@@ -49,28 +68,29 @@ then
skip_all='skipping git-cvsserver tests, perl not available'
test_done
fi
-"$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
+perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
test_done
}
unset GIT_DIR GIT_CONFIG
-WORKDIR=$(pwd)
-SERVERDIR=$(pwd)/gitcvs.git
+WORKDIR=$PWD
+SERVERDIR=$PWD/gitcvs.git
git_config="$SERVERDIR/config"
CVSROOT=":fork:$SERVERDIR"
-CVSWORK="$(pwd)/cvswork"
+CVSWORK="$PWD/cvswork"
CVS_SERVER=git-cvsserver
export CVSROOT CVS_SERVER
rm -rf "$CVSWORK" "$SERVERDIR"
test_expect_success 'setup' '
+ git config push.default matching &&
echo "Simple text file" >textfile.c &&
echo "File with embedded NUL: Q <- there" | q_to_nul > binfile.bin &&
mkdir subdir &&
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 "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 &&
@@ -129,21 +149,22 @@ test_expect_success 'cvs co (use attributes)' '
'
test_expect_success 'adding files' '
- cd cvswork/subdir &&
+ (cd cvswork &&
+ (cd 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 .. &&
+ echo "psuedo-binary" > temp.bin
+ ) &&
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 &&
@@ -153,9 +174,9 @@ test_expect_success 'updating' '
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 .. &&
+ (cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update
+ ) &&
marked_as cvswork textfile.c "" &&
marked_as cvswork binfile.bin -kb &&
marked_as cvswork .gitattributes "" &&
@@ -232,36 +253,52 @@ test_expect_success 'cvs co another copy (guess)' '
marked_as cvswork2/subdir newfile.c ""
'
+test_expect_success 'cvs status - sticky options' '
+ check_status_options cvswork2 textfile.c "" &&
+ check_status_options cvswork2 binfile.bin -kb &&
+ check_status_options cvswork2 .gitattributes "" &&
+ check_status_options cvswork2 mixedUp.c -kb &&
+ check_status_options cvswork2 multiline.c -kb &&
+ check_status_options cvswork2 multilineTxt.c "" &&
+ check_status_options cvswork2/subdir withCr.bin -kb &&
+ check_status_options cvswork2 subdir/withCr.bin -kb &&
+ check_status_options cvswork2/subdir file.h "" &&
+ check_status_options cvswork2 subdir/file.h "" &&
+ check_status_options cvswork2/subdir unspecified.other "" &&
+ check_status_options cvswork2/subdir newfile.bin "" &&
+ check_status_options cvswork2/subdir newfile.c ""
+'
+
test_expect_success 'add text (guess)' '
- cd cvswork &&
+ (cd cvswork &&
echo "simpleText" > simpleText.c &&
- GIT_CONFIG="$git_config" cvs -Q add simpleText.c &&
- cd .. &&
+ GIT_CONFIG="$git_config" cvs -Q add simpleText.c
+ ) &&
marked_as cvswork simpleText.c ""
'
test_expect_success 'add bin (guess)' '
- cd cvswork &&
+ (cd cvswork &&
echo "simpleBin: NUL: Q <- there" | q_to_nul > simpleBin.bin &&
- GIT_CONFIG="$git_config" cvs -Q add simpleBin.bin &&
- cd .. &&
+ GIT_CONFIG="$git_config" cvs -Q add simpleBin.bin
+ ) &&
marked_as cvswork simpleBin.bin -kb
'
test_expect_success 'remove files (guess)' '
- cd cvswork &&
+ (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 ../.. &&
+ (cd subdir &&
+ GIT_CONFIG="$git_config" cvs -Q rm -f withCr.bin
+ )) &&
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 .. &&
+ (cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q ci -m "add/rm files" >cvs.log 2>&1
+ ) &&
marked_as cvswork textfile.c "" &&
marked_as cvswork binfile.bin -kb &&
marked_as cvswork .gitattributes "" &&
@@ -278,9 +315,9 @@ test_expect_success 'cvs ci (guess)' '
'
test_expect_success 'update subdir of other copy (guess)' '
- cd cvswork2/subdir &&
- GIT_CONFIG="$git_config" cvs -Q update &&
- cd ../.. &&
+ (cd cvswork2/subdir &&
+ GIT_CONFIG="$git_config" cvs -Q update
+ ) &&
marked_as cvswork2 textfile.c "" &&
marked_as cvswork2 binfile.bin -kb &&
marked_as cvswork2 .gitattributes "" &&
@@ -304,11 +341,11 @@ test_expect_success 'update/merge full other copy (guess)' '
git add multilineTxt.c &&
git commit -q -m "modify multiline file" >> "${WORKDIR}/marked.log" &&
git push gitcvs.git >/dev/null &&
- cd cvswork2 &&
+ (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 .. &&
+ GIT_CONFIG="$git_config" cvs update > cvs.log 2>&1
+ ) &&
marked_as cvswork2 textfile.c "" &&
marked_as cvswork2 binfile.bin -kb &&
marked_as cvswork2 .gitattributes "" &&
diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh
new file mode 100755
index 0000000..6d2d3c8
--- /dev/null
+++ b/t/t9402-git-cvsserver-refs.sh
@@ -0,0 +1,551 @@
+#!/bin/sh
+
+test_description='git-cvsserver and git refspecs
+
+tests ability for git-cvsserver to switch between and compare
+tags, branches and other git refspecs'
+
+. ./test-lib.sh
+
+#########
+
+check_start_tree() {
+ rm -f "$WORKDIR/list.expected"
+ echo "start $1" >>"${WORKDIR}/check.log"
+}
+
+check_file() {
+ sandbox="$1"
+ file="$2"
+ ver="$3"
+ GIT_DIR=$SERVERDIR git show "${ver}:${file}" \
+ >"$WORKDIR/check.got" 2>"$WORKDIR/check.stderr"
+ test_cmp "$WORKDIR/check.got" "$sandbox/$file"
+ stat=$?
+ echo "check_file $sandbox $file $ver : $stat" >>"$WORKDIR/check.log"
+ echo "$file" >>"$WORKDIR/list.expected"
+ return $stat
+}
+
+check_end_tree() {
+ sandbox="$1" &&
+ find "$sandbox" -name CVS -prune -o -type f -print >"$WORKDIR/list.actual" &&
+ sort <"$WORKDIR/list.expected" >expected &&
+ sort <"$WORKDIR/list.actual" | sed -e "s%cvswork/%%" >actual &&
+ test_cmp expected actual &&
+ rm expected actual
+}
+
+check_end_full_tree() {
+ sandbox="$1" &&
+ sort <"$WORKDIR/list.expected" >expected &&
+ find "$sandbox" -name CVS -prune -o -type f -print |
+ sed -e "s%$sandbox/%%" | sort >act1 &&
+ test_cmp expected act1 &&
+ git ls-tree --name-only -r "$2" | sort >act2 &&
+ test_cmp expected act2 &&
+ rm expected act1 act2
+}
+
+#########
+
+check_diff() {
+ diffFile="$1"
+ vOld="$2"
+ vNew="$3"
+ rm -rf diffSandbox
+ git clone -q -n . diffSandbox &&
+ (
+ cd diffSandbox &&
+ git checkout "$vOld" &&
+ git apply -p0 --index <"../$diffFile" &&
+ git diff --exit-code "$vNew"
+ ) >check_diff_apply.out 2>&1
+}
+
+#########
+
+cvs >/dev/null 2>&1
+if test $? -ne 1
+then
+ skip_all='skipping git-cvsserver tests, cvs not found'
+ test_done
+fi
+if ! test_have_prereq PERL
+then
+ skip_all='skipping git-cvsserver tests, perl not available'
+ test_done
+fi
+perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
+ skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
+ test_done
+}
+
+unset GIT_DIR GIT_CONFIG
+WORKDIR=$PWD
+SERVERDIR=$PWD/gitcvs.git
+git_config="$SERVERDIR/config"
+CVSROOT=":fork:$SERVERDIR"
+CVSWORK="$PWD/cvswork"
+CVS_SERVER=git-cvsserver
+export CVSROOT CVS_SERVER
+
+rm -rf "$CVSWORK" "$SERVERDIR"
+test_expect_success 'setup v1, b1' '
+ echo "Simple text file" >textfile.c &&
+ echo "t2" >t2 &&
+ mkdir adir &&
+ echo "adir/afile line1" >adir/afile &&
+ echo "adir/afile line2" >>adir/afile &&
+ echo "adir/afile line3" >>adir/afile &&
+ echo "adir/afile line4" >>adir/afile &&
+ echo "adir/a2file" >>adir/a2file &&
+ mkdir adir/bdir &&
+ echo "adir/bdir/bfile line 1" >adir/bdir/bfile &&
+ echo "adir/bdir/bfile line 2" >>adir/bdir/bfile &&
+ echo "adir/bdir/b2file" >adir/bdir/b2file &&
+ git add textfile.c t2 adir &&
+ git commit -q -m "First Commit (v1)" &&
+ git tag v1 &&
+ git branch b1 &&
+ git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
+ GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log"
+'
+
+rm -rf cvswork
+test_expect_success 'cvs co v1' '
+ cvs -f -Q co -r v1 -d cvswork master >cvs.log 2>&1 &&
+ check_start_tree cvswork &&
+ check_file cvswork textfile.c v1 &&
+ check_file cvswork t2 v1 &&
+ check_file cvswork adir/afile v1 &&
+ check_file cvswork adir/a2file v1 &&
+ check_file cvswork adir/bdir/bfile v1 &&
+ check_file cvswork adir/bdir/b2file v1 &&
+ check_end_tree cvswork
+'
+
+rm -rf cvswork
+test_expect_success 'cvs co b1' '
+ cvs -f co -r b1 -d cvswork master >cvs.log 2>&1 &&
+ check_start_tree cvswork &&
+ check_file cvswork textfile.c v1 &&
+ check_file cvswork t2 v1 &&
+ check_file cvswork adir/afile v1 &&
+ check_file cvswork adir/a2file v1 &&
+ check_file cvswork adir/bdir/bfile v1 &&
+ check_file cvswork adir/bdir/b2file v1 &&
+ check_end_tree cvswork
+'
+
+test_expect_success 'cvs co b1 [cvswork3]' '
+ cvs -f co -r b1 -d cvswork3 master >cvs.log 2>&1 &&
+ check_start_tree cvswork3 &&
+ check_file cvswork3 textfile.c v1 &&
+ check_file cvswork3 t2 v1 &&
+ check_file cvswork3 adir/afile v1 &&
+ check_file cvswork3 adir/a2file v1 &&
+ check_file cvswork3 adir/bdir/bfile v1 &&
+ check_file cvswork3 adir/bdir/b2file v1 &&
+ check_end_full_tree cvswork3 v1
+'
+
+test_expect_success 'edit cvswork3 and save diff' '
+ (
+ cd cvswork3 &&
+ sed -e "s/line1/line1 - data/" adir/afile >adir/afileNEW &&
+ mv -f adir/afileNEW adir/afile &&
+ echo "afile5" >adir/afile5 &&
+ rm t2 &&
+ cvs -f add adir/afile5 &&
+ cvs -f rm t2 &&
+ ! cvs -f diff -N -u >"$WORKDIR/cvswork3edit.diff"
+ )
+'
+
+test_expect_success 'setup v1.2 on b1' '
+ git checkout b1 &&
+ echo "new v1.2" >t3 &&
+ rm t2 &&
+ sed -e "s/line3/line3 - more data/" adir/afile >adir/afileNEW &&
+ mv -f adir/afileNEW adir/afile &&
+ rm adir/a2file &&
+ echo "a3file" >>adir/a3file &&
+ echo "bfile line 3" >>adir/bdir/bfile &&
+ rm adir/bdir/b2file &&
+ echo "b3file" >adir/bdir/b3file &&
+ mkdir cdir &&
+ echo "cdir/cfile" >cdir/cfile &&
+ git add -A cdir adir t3 t2 &&
+ git commit -q -m 'v1.2' &&
+ git tag v1.2 &&
+ git push --tags gitcvs.git b1:b1
+'
+
+test_expect_success 'cvs -f up (on b1 adir)' '
+ ( cd cvswork/adir && cvs -f up -d ) >cvs.log 2>&1 &&
+ check_start_tree cvswork &&
+ check_file cvswork textfile.c v1 &&
+ check_file cvswork t2 v1 &&
+ check_file cvswork adir/afile v1.2 &&
+ check_file cvswork adir/a3file v1.2 &&
+ check_file cvswork adir/bdir/bfile v1.2 &&
+ check_file cvswork adir/bdir/b3file v1.2 &&
+ check_end_tree cvswork
+'
+
+test_expect_success 'cvs up (on b1 /)' '
+ ( cd cvswork && cvs -f up -d ) >cvs.log 2>&1 &&
+ check_start_tree cvswork &&
+ check_file cvswork textfile.c v1.2 &&
+ check_file cvswork t3 v1.2 &&
+ check_file cvswork adir/afile v1.2 &&
+ check_file cvswork adir/a3file v1.2 &&
+ check_file cvswork adir/bdir/bfile v1.2 &&
+ check_file cvswork adir/bdir/b3file v1.2 &&
+ check_file cvswork cdir/cfile v1.2 &&
+ check_end_tree cvswork
+'
+
+# Make sure "CVS/Tag" files didn't get messed up:
+test_expect_success 'cvs up (on b1 /) (again; check CVS/Tag files)' '
+ ( cd cvswork && cvs -f up -d ) >cvs.log 2>&1 &&
+ check_start_tree cvswork &&
+ check_file cvswork textfile.c v1.2 &&
+ check_file cvswork t3 v1.2 &&
+ check_file cvswork adir/afile v1.2 &&
+ check_file cvswork adir/a3file v1.2 &&
+ check_file cvswork adir/bdir/bfile v1.2 &&
+ check_file cvswork adir/bdir/b3file v1.2 &&
+ check_file cvswork cdir/cfile v1.2 &&
+ check_end_tree cvswork
+'
+
+# update to another version:
+test_expect_success 'cvs up -r v1' '
+ ( cd cvswork && cvs -f up -r v1 ) >cvs.log 2>&1 &&
+ check_start_tree cvswork &&
+ check_file cvswork textfile.c v1 &&
+ check_file cvswork t2 v1 &&
+ check_file cvswork adir/afile v1 &&
+ check_file cvswork adir/a2file v1 &&
+ check_file cvswork adir/bdir/bfile v1 &&
+ check_file cvswork adir/bdir/b2file v1 &&
+ check_end_tree cvswork
+'
+
+test_expect_success 'cvs up' '
+ ( cd cvswork && cvs -f up ) >cvs.log 2>&1 &&
+ check_start_tree cvswork &&
+ check_file cvswork textfile.c v1 &&
+ check_file cvswork t2 v1 &&
+ check_file cvswork adir/afile v1 &&
+ check_file cvswork adir/a2file v1 &&
+ check_file cvswork adir/bdir/bfile v1 &&
+ check_file cvswork adir/bdir/b2file v1 &&
+ check_end_tree cvswork
+'
+
+test_expect_success 'cvs up (again; check CVS/Tag files)' '
+ ( cd cvswork && cvs -f up -d ) >cvs.log 2>&1 &&
+ check_start_tree cvswork &&
+ check_file cvswork textfile.c v1 &&
+ check_file cvswork t2 v1 &&
+ check_file cvswork adir/afile v1 &&
+ check_file cvswork adir/a2file v1 &&
+ check_file cvswork adir/bdir/bfile v1 &&
+ check_file cvswork adir/bdir/b2file v1 &&
+ check_end_tree cvswork
+'
+
+test_expect_success 'setup simple b2' '
+ git branch b2 v1 &&
+ git push --tags gitcvs.git b2:b2
+'
+
+test_expect_success 'cvs co b2 [into cvswork2]' '
+ cvs -f co -r b2 -d cvswork2 master >cvs.log 2>&1 &&
+ check_start_tree cvswork &&
+ check_file cvswork textfile.c v1 &&
+ check_file cvswork t2 v1 &&
+ check_file cvswork adir/afile v1 &&
+ check_file cvswork adir/a2file v1 &&
+ check_file cvswork adir/bdir/bfile v1 &&
+ check_file cvswork adir/bdir/b2file v1 &&
+ check_end_tree cvswork
+'
+
+test_expect_success 'root dir edit [cvswork2]' '
+ (
+ cd cvswork2 && echo "Line 2" >>textfile.c &&
+ ! cvs -f diff -u >"$WORKDIR/cvsEdit1.diff" &&
+ cvs -f commit -m "edit textfile.c" textfile.c
+ ) >cvsEdit1.log 2>&1
+'
+
+test_expect_success 'root dir rm file [cvswork2]' '
+ (
+ cd cvswork2 &&
+ cvs -f rm -f t2 &&
+ cvs -f diff -u >../cvsEdit2-empty.diff &&
+ ! cvs -f diff -N -u >"$WORKDIR/cvsEdit2-N.diff" &&
+ cvs -f commit -m "rm t2"
+ ) >cvsEdit2.log 2>&1
+'
+
+test_expect_success 'subdir edit/add/rm files [cvswork2]' '
+ (
+ cd cvswork2 &&
+ sed -e "s/line 1/line 1 (v2)/" adir/bdir/bfile >adir/bdir/bfileNEW &&
+ mv -f adir/bdir/bfileNEW adir/bdir/bfile &&
+ rm adir/bdir/b2file &&
+ cd adir &&
+ cvs -f rm bdir/b2file &&
+ echo "4th file" >bdir/b4file &&
+ cvs -f add bdir/b4file &&
+ ! cvs -f diff -N -u >"$WORKDIR/cvsEdit3.diff" &&
+ git fetch gitcvs.git b2:b2 &&
+ (
+ cd .. &&
+ ! cvs -f diff -u -N -r v1.2 >"$WORKDIR/cvsEdit3-v1.2.diff" &&
+ ! cvs -f diff -u -N -r v1.2 -r v1 >"$WORKDIR/cvsEdit3-v1.2-v1.diff"
+ ) &&
+ cvs -f commit -m "various add/rm/edit"
+ ) >cvs.log 2>&1
+'
+
+test_expect_success 'validate result of edits [cvswork2]' '
+ git fetch gitcvs.git b2:b2 &&
+ git tag v2 b2 &&
+ git push --tags gitcvs.git b2:b2 &&
+ check_start_tree cvswork2 &&
+ check_file cvswork2 textfile.c v2 &&
+ check_file cvswork2 adir/afile v2 &&
+ check_file cvswork2 adir/a2file v2 &&
+ check_file cvswork2 adir/bdir/bfile v2 &&
+ check_file cvswork2 adir/bdir/b4file v2 &&
+ check_end_full_tree cvswork2 v2
+'
+
+test_expect_success 'validate basic diffs saved during above cvswork2 edits' '
+ test $(grep Index: cvsEdit1.diff | wc -l) = 1 &&
+ test_must_be_empty cvsEdit2-empty.diff &&
+ test $(grep Index: cvsEdit2-N.diff | wc -l) = 1 &&
+ test $(grep Index: cvsEdit3.diff | wc -l) = 3 &&
+ rm -rf diffSandbox &&
+ git clone -q -n . diffSandbox &&
+ (
+ cd diffSandbox &&
+ git checkout v1 &&
+ git apply -p0 --index <"$WORKDIR/cvsEdit1.diff" &&
+ git apply -p0 --index <"$WORKDIR/cvsEdit2-N.diff" &&
+ git apply -p0 --directory=adir --index <"$WORKDIR/cvsEdit3.diff" &&
+ git diff --exit-code v2
+ ) >"check_diff_apply.out" 2>&1
+'
+
+test_expect_success 'validate v1.2 diff saved during last cvswork2 edit' '
+ test $(grep Index: cvsEdit3-v1.2.diff | wc -l) = 9 &&
+ check_diff cvsEdit3-v1.2.diff v1.2 v2
+'
+
+test_expect_success 'validate v1.2 v1 diff saved during last cvswork2 edit' '
+ test $(grep Index: cvsEdit3-v1.2-v1.diff | wc -l) = 9 &&
+ check_diff cvsEdit3-v1.2-v1.diff v1.2 v1
+'
+
+test_expect_success 'cvs up [cvswork2]' '
+ ( cd cvswork2 && cvs -f up ) >cvs.log 2>&1 &&
+ check_start_tree cvswork2 &&
+ check_file cvswork2 textfile.c v2 &&
+ check_file cvswork2 adir/afile v2 &&
+ check_file cvswork2 adir/a2file v2 &&
+ check_file cvswork2 adir/bdir/bfile v2 &&
+ check_file cvswork2 adir/bdir/b4file v2 &&
+ check_end_full_tree cvswork2 v2
+'
+
+test_expect_success 'cvs up -r b2 [back to cvswork]' '
+ ( cd cvswork && cvs -f up -r b2 ) >cvs.log 2>&1 &&
+ check_start_tree cvswork &&
+ check_file cvswork textfile.c v2 &&
+ check_file cvswork adir/afile v2 &&
+ check_file cvswork adir/a2file v2 &&
+ check_file cvswork adir/bdir/bfile v2 &&
+ check_file cvswork adir/bdir/b4file v2 &&
+ check_end_full_tree cvswork v2
+'
+
+test_expect_success 'cvs up -r b1' '
+ ( cd cvswork && cvs -f up -r b1 ) >cvs.log 2>&1 &&
+ check_start_tree cvswork &&
+ check_file cvswork textfile.c v1.2 &&
+ check_file cvswork t3 v1.2 &&
+ check_file cvswork adir/afile v1.2 &&
+ check_file cvswork adir/a3file v1.2 &&
+ check_file cvswork adir/bdir/bfile v1.2 &&
+ check_file cvswork adir/bdir/b3file v1.2 &&
+ check_file cvswork cdir/cfile v1.2 &&
+ check_end_full_tree cvswork v1.2
+'
+
+test_expect_success 'cvs up -A' '
+ ( cd cvswork && cvs -f up -A ) >cvs.log 2>&1 &&
+ check_start_tree cvswork &&
+ check_file cvswork textfile.c v1 &&
+ check_file cvswork t2 v1 &&
+ check_file cvswork adir/afile v1 &&
+ check_file cvswork adir/a2file v1 &&
+ check_file cvswork adir/bdir/bfile v1 &&
+ check_file cvswork adir/bdir/b2file v1 &&
+ check_end_full_tree cvswork v1
+'
+
+test_expect_success 'cvs up (check CVS/Tag files)' '
+ ( cd cvswork && cvs -f up ) >cvs.log 2>&1 &&
+ check_start_tree cvswork &&
+ check_file cvswork textfile.c v1 &&
+ check_file cvswork t2 v1 &&
+ check_file cvswork adir/afile v1 &&
+ check_file cvswork adir/a2file v1 &&
+ check_file cvswork adir/bdir/bfile v1 &&
+ check_file cvswork adir/bdir/b2file v1 &&
+ check_end_full_tree cvswork v1
+'
+
+# This is not really legal CVS, but it seems to work anyway:
+test_expect_success 'cvs up -r heads/b1' '
+ ( cd cvswork && cvs -f up -r heads/b1 ) >cvs.log 2>&1 &&
+ check_start_tree cvswork &&
+ check_file cvswork textfile.c v1.2 &&
+ check_file cvswork t3 v1.2 &&
+ check_file cvswork adir/afile v1.2 &&
+ check_file cvswork adir/a3file v1.2 &&
+ check_file cvswork adir/bdir/bfile v1.2 &&
+ check_file cvswork adir/bdir/b3file v1.2 &&
+ check_file cvswork cdir/cfile v1.2 &&
+ check_end_full_tree cvswork v1.2
+'
+
+# But this should work even if CVS client checks -r more carefully:
+test_expect_success 'cvs up -r heads_-s-b2 (cvsserver escape mechanism)' '
+ ( cd cvswork && cvs -f up -r heads_-s-b2 ) >cvs.log 2>&1 &&
+ check_start_tree cvswork &&
+ check_file cvswork textfile.c v2 &&
+ check_file cvswork adir/afile v2 &&
+ check_file cvswork adir/a2file v2 &&
+ check_file cvswork adir/bdir/bfile v2 &&
+ check_file cvswork adir/bdir/b4file v2 &&
+ check_end_full_tree cvswork v2
+'
+
+v1hash=$(git rev-parse v1)
+test_expect_success 'cvs up -r $(git rev-parse v1)' '
+ test -n "$v1hash" &&
+ ( cd cvswork && cvs -f up -r "$v1hash" ) >cvs.log 2>&1 &&
+ check_start_tree cvswork &&
+ check_file cvswork textfile.c v1 &&
+ check_file cvswork t2 v1 &&
+ check_file cvswork adir/afile v1 &&
+ check_file cvswork adir/a2file v1 &&
+ check_file cvswork adir/bdir/bfile v1 &&
+ check_file cvswork adir/bdir/b2file v1 &&
+ check_end_full_tree cvswork v1
+'
+
+test_expect_success 'cvs diff -r v1 -u' '
+ ( cd cvswork && cvs -f diff -r v1 -u ) >cvsDiff.out 2>cvs.log &&
+ test_must_be_empty cvsDiff.out &&
+ test_must_be_empty cvs.log
+'
+
+test_expect_success 'cvs diff -N -r v2 -u' '
+ ( cd cvswork && ! cvs -f diff -N -r v2 -u ) >cvsDiff.out 2>cvs.log &&
+ test_must_be_empty cvs.log &&
+ test -s cvsDiff.out &&
+ check_diff cvsDiff.out v2 v1 >check_diff.out 2>&1
+'
+
+test_expect_success 'cvs diff -N -r v2 -r v1.2' '
+ ( cd cvswork && ! cvs -f diff -N -r v2 -r v1.2 -u ) >cvsDiff.out 2>cvs.log &&
+ test_must_be_empty cvs.log &&
+ test -s cvsDiff.out &&
+ check_diff cvsDiff.out v2 v1.2 >check_diff.out 2>&1
+'
+
+test_expect_success 'apply early [cvswork3] diff to b3' '
+ git clone -q . gitwork3 &&
+ (
+ cd gitwork3 &&
+ git checkout -b b3 v1 &&
+ git apply -p0 --index <"$WORKDIR/cvswork3edit.diff" &&
+ git commit -m "cvswork3 edits applied"
+ ) &&
+ git fetch gitwork3 b3:b3 &&
+ git tag v3 b3
+'
+
+test_expect_success 'check [cvswork3] diff' '
+ ( cd cvswork3 && ! cvs -f diff -N -u ) >"$WORKDIR/cvsDiff.out" 2>cvs.log &&
+ test_must_be_empty cvs.log &&
+ test -s cvsDiff.out &&
+ test $(grep Index: cvsDiff.out | wc -l) = 3 &&
+ test_cmp cvsDiff.out cvswork3edit.diff &&
+ check_diff cvsDiff.out v1 v3 >check_diff.out 2>&1
+'
+
+test_expect_success 'merge early [cvswork3] b3 with b1' '
+ ( cd gitwork3 && git merge -m "message" b1 ) &&
+ git fetch gitwork3 b3:b3 &&
+ git tag v3merged b3 &&
+ git push --tags gitcvs.git b3:b3
+'
+
+# This test would fail if cvsserver properly created a ".#afile"* file
+# for the merge.
+# TODO: Validate that the .# file was saved properly, and then
+# delete/ignore it when checking the tree.
+test_expect_success 'cvs up dirty [cvswork3]' '
+ (
+ cd cvswork3 &&
+ cvs -f up &&
+ ! cvs -f diff -N -u >"$WORKDIR/cvsDiff.out"
+ ) >cvs.log 2>&1 &&
+ test -s cvsDiff.out &&
+ test $(grep Index: cvsDiff.out | wc -l) = 2 &&
+ check_start_tree cvswork3 &&
+ check_file cvswork3 textfile.c v3merged &&
+ check_file cvswork3 t3 v3merged &&
+ check_file cvswork3 adir/afile v3merged &&
+ check_file cvswork3 adir/a3file v3merged &&
+ check_file cvswork3 adir/afile5 v3merged &&
+ check_file cvswork3 adir/bdir/bfile v3merged &&
+ check_file cvswork3 adir/bdir/b3file v3merged &&
+ check_file cvswork3 cdir/cfile v3merged &&
+ check_end_full_tree cvswork3 v3merged
+'
+
+# TODO: test cvs status
+
+test_expect_success 'cvs commit [cvswork3]' '
+ (
+ cd cvswork3 &&
+ cvs -f commit -m "dirty sandbox after auto-merge"
+ ) >cvs.log 2>&1 &&
+ check_start_tree cvswork3 &&
+ check_file cvswork3 textfile.c v3merged &&
+ check_file cvswork3 t3 v3merged &&
+ check_file cvswork3 adir/afile v3merged &&
+ check_file cvswork3 adir/a3file v3merged &&
+ check_file cvswork3 adir/afile5 v3merged &&
+ check_file cvswork3 adir/bdir/bfile v3merged &&
+ check_file cvswork3 adir/bdir/b3file v3merged &&
+ check_file cvswork3 cdir/cfile v3merged &&
+ check_end_full_tree cvswork3 v3merged &&
+ git fetch gitcvs.git b3:b4 &&
+ git tag v4.1 b4 &&
+ git diff --exit-code v4.1 v3merged >check_diff_apply.out 2>&1
+'
+
+test_done
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index 4f2b9b0..e94b2f1 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -18,42 +18,34 @@ or warnings to log.'
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'
# ----------------------------------------------------------------------
@@ -69,52 +61,42 @@ test_expect_success \
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
@@ -122,37 +104,30 @@ test_debug 'cat gitweb.log'
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'
# ----------------------------------------------------------------------
@@ -161,7 +136,6 @@ test_debug 'cat gitweb.log'
test_expect_success \
'commitdiff(0): root' \
'gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
test_expect_success \
'commitdiff(0): file added' \
@@ -169,29 +143,25 @@ test_expect_success \
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 \
+test_expect_success \
'commitdiff(0): file to symlink' \
'rm renamed_file &&
- ln -s file renamed_file &&
+ test_ln_s_add file renamed_file &&
git commit -a -m "File to symlink." &&
gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
test_expect_success \
'commitdiff(0): file deleted' \
@@ -199,7 +169,6 @@ test_expect_success \
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' \
@@ -207,7 +176,6 @@ test_expect_success \
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' \
@@ -215,7 +183,6 @@ test_expect_success \
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' \
@@ -233,7 +200,6 @@ EOF
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' \
@@ -242,20 +208,18 @@ test_expect_success \
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' '
+test_expect_success 'setup typechange commits' '
echo "hello world" > foo &&
echo "hi planet" > bar &&
git update-index --add foo bar &&
git commit -m initial &&
git branch initial &&
rm -f foo &&
- ln -s bar foo &&
- git update-index foo &&
+ test_ln_s_add bar foo &&
git commit -m "foo symlinked to bar" &&
git branch foo-symlinked-to-bar &&
rm -f foo &&
@@ -279,42 +243,81 @@ test_expect_success SYMLINKS 'setup typechange commits' '
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'
+
+# ----------------------------------------------------------------------
+# commitdiff testing (incomplete lines)
+
+test_expect_success 'setup incomplete lines' '
+ cat >file<<-\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.
+ CHANGE_ME
+ EOF
+ git commit -a -m "Preparing for incomplete lines" &&
+ echo "incomplete" | tr -d "\\012" >>file &&
+ git commit -a -m "Add incomplete line" &&
+ git tag incomplete_lines_add &&
+ sed -e s/CHANGE_ME/change_me/ <file >file+ &&
+ mv -f file+ file &&
+ git commit -a -m "Incomplete context line" &&
+ git tag incomplete_lines_ctx &&
+ echo "Dominus regit me," >file &&
+ echo "incomplete line" | tr -d "\\012" >>file &&
+ git commit -a -m "Change incomplete line" &&
+ git tag incomplete_lines_chg &&
+ echo "Dominus regit me," >file &&
+ git commit -a -m "Remove incomplete line" &&
+ git tag incomplete_lines_rem
+'
+
+test_expect_success 'commitdiff(1): addition of incomplete line' '
+ gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_add"
+'
+
+test_expect_success 'commitdiff(1): incomplete line as context line' '
+ gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_ctx"
+'
+
+test_expect_success 'commitdiff(1): change incomplete line' '
+ gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_chg"
+'
+
+test_expect_success 'commitdiff(1): removal of incomplete line' '
+ gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_rem"
+'
# ----------------------------------------------------------------------
# commit, commitdiff: merge, large
@@ -325,17 +328,16 @@ test_expect_success \
git add b &&
git commit -a -m "On branch" &&
git checkout master &&
- git pull . b'
+ git merge b &&
+ git tag merge_commit'
test_expect_success \
'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' \
@@ -358,11 +360,7 @@ test_expect_success \
echo "Changed" >> 04-rename-to &&
test_chmod +x 05-mode-change &&
rm -f 06-file-or-symlink &&
- if test_have_prereq SYMLINKS; then
- ln -s 01-change 06-file-or-symlink
- else
- printf %s 01-change > 06-file-or-symlink
- fi &&
+ test_ln_s_add 01-change 06-file-or-symlink &&
echo "Changed and have mode changed" > 07-change-mode-change &&
test_chmod +x 07-change-mode-change &&
git commit -a -m "Large commit" &&
@@ -371,12 +369,33 @@ test_expect_success \
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'
+
+# ----------------------------------------------------------------------
+# side-by-side diff
+
+test_expect_success 'side-by-side: addition of incomplete line' '
+ gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_add;ds=sidebyside"
+'
+
+test_expect_success 'side-by-side: incomplete line as context line' '
+ gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_ctx;ds=sidebyside"
+'
+
+test_expect_success 'side-by-side: changed incomplete line' '
+ gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_chg;ds=sidebyside"
+'
+
+test_expect_success 'side-by-side: removal of incomplete line' '
+ gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_rem;ds=sidebyside"
+'
+
+test_expect_success 'side-by-side: merge commit' '
+ gitweb_run "p=.git;a=commitdiff;h=merge_commit;ds=sidebyside"
+'
# ----------------------------------------------------------------------
# tags testing
@@ -394,17 +413,14 @@ test_expect_success \
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
@@ -412,22 +428,18 @@ test_debug 'cat gitweb.log'
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)' \
@@ -438,55 +450,53 @@ test_expect_success \
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 (non-existent)' \
+ 'gitweb_run "" "/.git/non-existent"'
+
+test_expect_success \
+ 'path_info: project/branch:filename (non-existent branch)' \
+ 'gitweb_run "" "/.git/non-existent:non-existent"'
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'
# ----------------------------------------------------------------------
@@ -495,17 +505,14 @@ test_debug 'cat gitweb.log'
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
@@ -513,27 +520,27 @@ test_debug 'cat gitweb.log'
test_expect_success \
'encode(commit): utf8' \
'. "$TEST_DIRECTORY"/t3901-utf8.txt &&
+ test_when_finished "GIT_AUTHOR_NAME=\"A U Thor\"" &&
+ test_when_finished "GIT_COMMITTER_NAME=\"C O Mitter\"" &&
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 &&
+ test_when_finished "GIT_AUTHOR_NAME=\"A U Thor\"" &&
+ test_when_finished "GIT_COMMITTER_NAME=\"C O Mitter\"" &&
echo "ISO-8859-1" >> file &&
git add file &&
- git config i18n.commitencoding ISO-8859-1 &&
+ test_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
@@ -541,27 +548,22 @@ test_debug 'cat gitweb.log'
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
@@ -569,14 +571,12 @@ test_debug 'cat gitweb.log'
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
@@ -586,7 +586,6 @@ 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
@@ -604,12 +603,10 @@ 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' \
@@ -617,14 +614,12 @@ test_expect_success \
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
@@ -635,7 +630,45 @@ 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'
+
+# ----------------------------------------------------------------------
+# searching
+
+cat >>gitweb_config.perl <<\EOF
+
+# enable search
+$feature{'search'}{'default'} = [1];
+$feature{'grep'}{'default'} = [1];
+$feature{'pickaxe'}{'default'} = [1];
+EOF
+
+test_expect_success \
+ 'search: preparation' \
+ 'echo "1st MATCH" >>file &&
+ echo "2nd MATCH" >>file &&
+ echo "MATCH" >>bar &&
+ git add file bar &&
+ git commit -m "Added MATCH word"'
+
+test_expect_success \
+ 'search: commit author' \
+ 'gitweb_run "p=.git;a=search;h=HEAD;st=author;s=A+U+Thor"'
+
+test_expect_success \
+ 'search: commit message' \
+ 'gitweb_run "p=.git;a=search;h=HEAD;st=commitr;s=MATCH"'
+
+test_expect_success \
+ 'search: grep' \
+ 'gitweb_run "p=.git;a=search;h=HEAD;st=grep;s=MATCH"'
+
+test_expect_success \
+ 'search: pickaxe' \
+ 'gitweb_run "p=.git;a=search;h=HEAD;st=pickaxe;s=MATCH"'
+
+test_expect_success \
+ 'search: projects' \
+ 'gitweb_run "a=project_list;s=.git"'
# ----------------------------------------------------------------------
# non-ASCII in README.html
@@ -645,35 +678,112 @@ test_expect_success \
'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
+highlight_version=$(highlight --version </dev/null 2>/dev/null)
if [ $? -eq 127 ]; then
- say "Skipping syntax highlighting test, because 'highlight' was not found"
+ say "Skipping syntax highlighting tests: 'highlight' not found"
+elif test -z "$highlight_version"; then
+ say "Skipping syntax highlighting tests: incorrect 'highlight' found"
else
test_set_prereq HIGHLIGHT
+ cat >>gitweb_config.perl <<-\EOF
+ our $highlight_bin = "highlight";
+ $feature{'highlight'}{'override'} = 1;
+ EOF
fi
test_expect_success HIGHLIGHT \
- 'syntax highlighting (no highlight)' \
+ 'syntax highlighting (no highlight, unknown syntax)' \
'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)' \
+ 'syntax highlighting (highlighted, shell script)' \
'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'
+
+# ----------------------------------------------------------------------
+# forks of projects
+
+cat >>gitweb_config.perl <<\EOF &&
+$feature{'forks'}{'default'} = [1];
+EOF
+
+test_expect_success \
+ 'forks: prepare' \
+ 'git init --bare foo.git &&
+ git --git-dir=foo.git --work-tree=. add file &&
+ git --git-dir=foo.git --work-tree=. commit -m "Initial commit" &&
+ echo "foo" > foo.git/description &&
+ mkdir -p foo &&
+ (cd foo &&
+ git clone --shared --bare ../foo.git foo-forked.git &&
+ echo "fork of foo" > foo-forked.git/description)'
+
+test_expect_success \
+ 'forks: projects list' \
+ 'gitweb_run'
+
+test_expect_success \
+ 'forks: forks action' \
+ 'gitweb_run "p=foo.git;a=forks"'
+
+# ----------------------------------------------------------------------
+# content tags (tag cloud)
+
+cat >>gitweb_config.perl <<-\EOF &&
+# we don't test _setting_ content tags, so any true value is good
+$feature{'ctags'}{'default'} = ['ctags_script.cgi'];
+EOF
+
+test_expect_success \
+ 'ctags: tag cloud in projects list' \
+ 'mkdir .git/ctags &&
+ echo "2" > .git/ctags/foo &&
+ echo "1" > .git/ctags/bar &&
+ gitweb_run'
+
+test_expect_success \
+ 'ctags: search projects by existing tag' \
+ 'gitweb_run "by_tag=foo"'
+
+test_expect_success \
+ 'ctags: search projects by non existent tag' \
+ 'gitweb_run "by_tag=non-existent"'
+
+test_expect_success \
+ 'ctags: malformed tag weights' \
+ 'mkdir -p .git/ctags &&
+ echo "not-a-number" > .git/ctags/nan &&
+ echo "not-a-number-2" > .git/ctags/nan2 &&
+ echo "0.1" >.git/ctags/floating-point &&
+ gitweb_run'
+
+# ----------------------------------------------------------------------
+# categories
+
+test_expect_success \
+ 'categories: projects list, only default category' \
+ 'echo "\$projects_list_group_categories = 1;" >>gitweb_config.perl &&
+ gitweb_run'
+
+# ----------------------------------------------------------------------
+# unborn branches
+
+test_expect_success \
+ 'unborn HEAD: "summary" page (with "heads" subview)' \
+ '{
+ git checkout orphan_branch ||
+ git checkout --orphan orphan_branch
+ } &&
+ test_when_finished "git checkout master" &&
+ gitweb_run "p=.git;a=summary"'
test_done
diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh
index 2487da1..2a0ffed 100755
--- a/t/t9501-gitweb-standalone-http-status.sh
+++ b/t/t9501-gitweb-standalone-http-status.sh
@@ -12,11 +12,18 @@ code and message.'
. ./gitweb-lib.sh
+#
+# Gitweb only provides the functionality tested by the 'modification times'
+# tests if it can access a date parser from one of these modules:
+#
+perl -MHTTP::Date -e 0 >/dev/null 2>&1 && test_set_prereq DATE_PARSER
+perl -MTime::ParseDate -e 0 >/dev/null 2>&1 && test_set_prereq DATE_PARSER
+
# ----------------------------------------------------------------------
# snapshot settings
test_expect_success 'setup' "
- test_commit 'SnapshotTests' 'i can has snapshot?'
+ test_commit 'SnapshotTests' 'i can has snapshot'
"
@@ -92,15 +99,15 @@ 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` &&
+ test_tick && git commit -m "Object to be tagged" &&
+ git tag tagged-object $(git hash-object tag-object) &&
gitweb_run "p=.git;a=snapshot;h=tagged-object;sf=tgz" &&
grep "400 - Object is not a tree-ish" gitweb.output
'
test_debug 'cat gitweb.output'
test_expect_success 'snapshots: good object id' '
- ID=`git rev-parse --verify HEAD` &&
+ ID=$(git rev-parse --verify HEAD) &&
gitweb_run "p=.git;a=snapshot;h=$ID;sf=tgz" &&
grep "Status: 200 OK" gitweb.output
'
@@ -112,6 +119,69 @@ test_expect_success 'snapshots: bad object id' '
'
test_debug 'cat gitweb.output'
+# ----------------------------------------------------------------------
+# modification times (Last-Modified and If-Modified-Since)
+
+test_expect_success DATE_PARSER 'modification: feed last-modified' '
+ gitweb_run "p=.git;a=atom;h=master" &&
+ grep "Status: 200 OK" gitweb.headers &&
+ grep "Last-modified: Thu, 7 Apr 2005 22:14:13 +0000" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success DATE_PARSER 'modification: feed if-modified-since (modified)' '
+ HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
+ export HTTP_IF_MODIFIED_SINCE &&
+ test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
+ gitweb_run "p=.git;a=atom;h=master" &&
+ grep "Status: 200 OK" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success DATE_PARSER 'modification: feed if-modified-since (unmodified)' '
+ HTTP_IF_MODIFIED_SINCE="Thu, 7 Apr 2005 22:14:13 +0000" &&
+ export HTTP_IF_MODIFIED_SINCE &&
+ test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
+ gitweb_run "p=.git;a=atom;h=master" &&
+ grep "Status: 304 Not Modified" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success DATE_PARSER 'modification: snapshot last-modified' '
+ gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
+ grep "Status: 200 OK" gitweb.headers &&
+ grep "Last-modified: Thu, 7 Apr 2005 22:14:13 +0000" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success DATE_PARSER 'modification: snapshot if-modified-since (modified)' '
+ HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
+ export HTTP_IF_MODIFIED_SINCE &&
+ test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
+ gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
+ grep "Status: 200 OK" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success DATE_PARSER 'modification: snapshot if-modified-since (unmodified)' '
+ HTTP_IF_MODIFIED_SINCE="Thu, 7 Apr 2005 22:14:13 +0000" &&
+ export HTTP_IF_MODIFIED_SINCE &&
+ test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
+ gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
+ grep "Status: 304 Not Modified" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success DATE_PARSER 'modification: tree snapshot' '
+ ID=$(git rev-parse --verify HEAD^{tree}) &&
+ HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
+ export HTTP_IF_MODIFIED_SINCE &&
+ test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
+ gitweb_run "p=.git;a=snapshot;h=$ID;sf=tgz" &&
+ grep "Status: 200 OK" gitweb.headers &&
+ ! grep -i "last-modified" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
# ----------------------------------------------------------------------
# load checking
@@ -126,7 +196,6 @@ test_expect_success 'load checking: load too high (default action)' '
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
@@ -135,4 +204,14 @@ our $maxload = undef;
EOF
+# ----------------------------------------------------------------------
+# invalid arguments
+
+test_expect_success 'invalid arguments: invalid regexp (in project search)' '
+ gitweb_run "a=project_list;s=*\.git;sr=1" &&
+ grep "Status: 400" gitweb.headers &&
+ grep "400 - Invalid.*regexp" gitweb.body
+'
+test_debug 'cat gitweb.headers'
+
test_done
diff --git a/t/t9502-gitweb-standalone-parse-output.sh b/t/t9502-gitweb-standalone-parse-output.sh
index dd83890..0796a43 100755
--- a/t/t9502-gitweb-standalone-parse-output.sh
+++ b/t/t9502-gitweb-standalone-parse-output.sh
@@ -40,7 +40,7 @@ check_snapshot () {
echo "basename=$basename"
grep "filename=.*$basename.tar" gitweb.headers >/dev/null 2>&1 &&
"$TAR" tf gitweb.body >file_list &&
- ! grep -v "^$prefix/" file_list
+ ! grep -v -e "^$prefix$" -e "^$prefix/" -e "^pax_global_header$" file_list
}
test_expect_success setup '
@@ -112,4 +112,95 @@ test_expect_success 'snapshot: hierarchical branch name (xx/test)' '
'
test_debug 'cat gitweb.headers'
+# ----------------------------------------------------------------------
+# forks of projects
+
+test_expect_success 'forks: setup' '
+ git init --bare foo.git &&
+ echo file > file &&
+ git --git-dir=foo.git --work-tree=. add file &&
+ git --git-dir=foo.git --work-tree=. commit -m "Initial commit" &&
+ echo "foo" > foo.git/description &&
+ git clone --bare foo.git foo.bar.git &&
+ echo "foo.bar" > foo.bar.git/description &&
+ git clone --bare foo.git foo_baz.git &&
+ echo "foo_baz" > foo_baz.git/description &&
+ rm -fr foo &&
+ mkdir -p foo &&
+ (
+ cd foo &&
+ git clone --shared --bare ../foo.git foo-forked.git &&
+ echo "fork of foo" > foo-forked.git/description
+ )
+'
+
+test_expect_success 'forks: not skipped unless "forks" feature enabled' '
+ gitweb_run "a=project_list" &&
+ grep -q ">\\.git<" gitweb.body &&
+ grep -q ">foo\\.git<" gitweb.body &&
+ grep -q ">foo_baz\\.git<" gitweb.body &&
+ grep -q ">foo\\.bar\\.git<" gitweb.body &&
+ grep -q ">foo_baz\\.git<" gitweb.body &&
+ grep -q ">foo/foo-forked\\.git<" gitweb.body &&
+ grep -q ">fork of .*<" gitweb.body
+'
+
+test_expect_success 'enable forks feature' '
+ cat >>gitweb_config.perl <<-\EOF
+ $feature{"forks"}{"default"} = [1];
+ EOF
+'
+
+test_expect_success 'forks: forks skipped if "forks" feature enabled' '
+ gitweb_run "a=project_list" &&
+ grep -q ">\\.git<" gitweb.body &&
+ grep -q ">foo\\.git<" gitweb.body &&
+ grep -q ">foo_baz\\.git<" gitweb.body &&
+ grep -q ">foo\\.bar\\.git<" gitweb.body &&
+ grep -q ">foo_baz\\.git<" gitweb.body &&
+ grep -v ">foo/foo-forked\\.git<" gitweb.body &&
+ grep -v ">fork of .*<" gitweb.body
+'
+
+test_expect_success 'forks: "forks" action for forked repository' '
+ gitweb_run "p=foo.git;a=forks" &&
+ grep -q ">foo/foo-forked\\.git<" gitweb.body &&
+ grep -q ">fork of foo<" gitweb.body
+'
+
+test_expect_success 'forks: can access forked repository' '
+ gitweb_run "p=foo/foo-forked.git;a=summary" &&
+ grep -q "200 OK" gitweb.headers &&
+ grep -q ">fork of foo<" gitweb.body
+'
+
+test_expect_success 'forks: project_index lists all projects (incl. forks)' '
+ cat >expected <<-\EOF &&
+ .git
+ foo.bar.git
+ foo.git
+ foo/foo-forked.git
+ foo_baz.git
+ EOF
+ gitweb_run "a=project_index" &&
+ sed -e "s/ .*//" <gitweb.body | sort >actual &&
+ test_cmp expected actual
+'
+
+xss() {
+ echo >&2 "Checking $1..." &&
+ gitweb_run "$1" &&
+ if grep "$TAG" gitweb.body; then
+ echo >&2 "xss: $TAG should have been quoted in output"
+ return 1
+ fi
+ return 0
+}
+
+test_expect_success 'xss checks' '
+ TAG="<magic-xss-tag>" &&
+ xss "a=rss&p=$TAG" &&
+ xss "a=rss&p=foo.git&f=$TAG"
+'
+
test_done
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
index 2eff9cd..4c384ff 100755
--- a/t/t9600-cvsimport.sh
+++ b/t/t9600-cvsimport.sh
@@ -3,21 +3,18 @@
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 PERL 'setup cvsroot environment' '
+ CVSROOT=$(pwd)/cvsroot &&
+ export CVSROOT
+'
-test_expect_success 'setup cvsroot' '$CVS init'
+test_expect_success PERL 'setup cvsroot' '$CVS init'
-test_expect_success 'setup a cvs module' '
+test_expect_success PERL 'setup a cvs module' '
mkdir "$CVSROOT/module" &&
$CVS co -d module-cvs module &&
- cd module-cvs &&
+ (cd module-cvs &&
cat <<EOF >o_fortuna &&
O Fortuna
velut luna
@@ -41,29 +38,28 @@ add "O Fortuna" lyrics
These public domain lyrics make an excellent sample text.
EOF
- $CVS commit -F message &&
- cd ..
+ $CVS commit -F message
+ )
'
-test_expect_success 'import a trivial module' '
+test_expect_success PERL '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 PERL 'pack refs' '(cd module-git && git gc)'
-test_expect_success 'initial import has correct .git/cvs-revisions' '
+test_expect_success PERL '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 &&
+test_expect_success PERL 'update cvs module' '
+ (cd module-cvs &&
cat <<EOF >o_fortuna &&
O Fortune,
like the moon
@@ -86,21 +82,22 @@ translate to English
My Latin is terrible.
EOF
- $CVS commit -F message &&
- cd ..
+ $CVS commit -F message
+ )
'
-test_expect_success 'update git module' '
+test_expect_success PERL 'update git module' '
- cd module-git &&
- git cvsimport -a -R -z 0 module &&
- git merge origin &&
- cd .. &&
+ (cd module-git &&
+ git config cvsimport.trackRevisions true &&
+ git cvsimport -a -z 0 module &&
+ git merge origin
+ ) &&
test_cmp module-cvs/o_fortuna module-git/o_fortuna
'
-test_expect_success 'update has correct .git/cvs-revisions' '
+test_expect_success PERL 'update has correct .git/cvs-revisions' '
(cd module-git &&
git log --format="o_fortuna 1.1 %H" -1 HEAD^ &&
@@ -108,28 +105,28 @@ test_expect_success 'update has correct .git/cvs-revisions' '
test_cmp expected module-git/.git/cvs-revisions
'
-test_expect_success 'update cvs module' '
+test_expect_success PERL 'update cvs module' '
- cd module-cvs &&
+ (cd module-cvs &&
echo 1 >tick &&
$CVS add tick &&
$CVS commit -m 1
- cd ..
-
+ )
'
-test_expect_success 'cvsimport.module config works' '
+test_expect_success PERL 'cvsimport.module config works' '
- cd module-git &&
+ (cd module-git &&
git config cvsimport.module module &&
- git cvsimport -a -R -z0 &&
- git merge origin &&
- cd .. &&
+ git config cvsimport.trackRevisions true &&
+ git cvsimport -a -z0 &&
+ git merge origin
+ ) &&
test_cmp module-cvs/tick module-git/tick
'
-test_expect_success 'second update has correct .git/cvs-revisions' '
+test_expect_success PERL 'second update has correct .git/cvs-revisions' '
(cd module-git &&
git log --format="o_fortuna 1.1 %H" -1 HEAD^^ &&
@@ -138,24 +135,25 @@ test_expect_success 'second update has correct .git/cvs-revisions' '
test_cmp expected module-git/.git/cvs-revisions
'
-test_expect_success 'import from a CVS working tree' '
+test_expect_success PERL 'import from a CVS working tree' '
$CVS co -d import-from-wt module &&
- cd import-from-wt &&
+ (cd import-from-wt &&
+ git config cvsimport.trackRevisions false &&
git cvsimport -a -z0 &&
echo 1 >expect &&
git log -1 --pretty=format:%s%n >actual &&
- test_cmp actual expect &&
- cd ..
+ test_cmp actual expect
+ )
'
-test_expect_success 'no .git/cvs-revisions created by default' '
+test_expect_success PERL '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_expect_success PERL '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
index 3afaf56..827d39f 100755
--- a/t/t9601-cvsimport-vendor-branch.sh
+++ b/t/t9601-cvsimport-vendor-branch.sh
@@ -34,50 +34,49 @@
test_description='git cvsimport handling of vendor branches'
. ./lib-cvs.sh
-CVSROOT="$TEST_DIRECTORY"/t9601/cvsroot
-export CVSROOT
+setup_cvs_test_repository t9601
-test_expect_success 'import a module with a vendor branch' '
+test_expect_success PERL '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 PERL '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 PERL 'check master out of git repository' 'test_git_co master'
-test_expect_success 'check a file that was imported once' '
+test_expect_success PERL '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_expect_failure PERL '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_expect_success PERL '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_expect_success PERL '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_expect_success PERL '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_expect_success PERL 'a vendor branch whose tag has been removed' '
test_cmp_branch_file master imported-anonymously.txt
diff --git a/t/t9602-cvsimport-branches-tags.sh b/t/t9602-cvsimport-branches-tags.sh
index 67878b2..e1db323 100755
--- a/t/t9602-cvsimport-branches-tags.sh
+++ b/t/t9602-cvsimport-branches-tags.sh
@@ -6,70 +6,69 @@
test_description='git cvsimport handling of branches and tags'
. ./lib-cvs.sh
-CVSROOT="$TEST_DIRECTORY"/t9602/cvsroot
-export CVSROOT
+setup_cvs_test_repository t9602
-test_expect_success 'import module' '
+test_expect_success PERL 'import module' '
git cvsimport -C module-git module
'
-test_expect_success 'test branch master' '
+test_expect_success PERL 'test branch master' '
test_cmp_branch_tree master
'
-test_expect_success 'test branch vendorbranch' '
+test_expect_success PERL 'test branch vendorbranch' '
test_cmp_branch_tree vendorbranch
'
-test_expect_failure 'test branch B_FROM_INITIALS' '
+test_expect_failure PERL 'test branch B_FROM_INITIALS' '
test_cmp_branch_tree B_FROM_INITIALS
'
-test_expect_failure 'test branch B_FROM_INITIALS_BUT_ONE' '
+test_expect_failure PERL 'test branch B_FROM_INITIALS_BUT_ONE' '
test_cmp_branch_tree B_FROM_INITIALS_BUT_ONE
'
-test_expect_failure 'test branch B_MIXED' '
+test_expect_failure PERL 'test branch B_MIXED' '
test_cmp_branch_tree B_MIXED
'
-test_expect_success 'test branch B_SPLIT' '
+test_expect_success PERL 'test branch B_SPLIT' '
test_cmp_branch_tree B_SPLIT
'
-test_expect_failure 'test tag vendortag' '
+test_expect_failure PERL 'test tag vendortag' '
test_cmp_branch_tree vendortag
'
-test_expect_success 'test tag T_ALL_INITIAL_FILES' '
+test_expect_success PERL '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_expect_failure PERL '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_expect_failure PERL 'test tag T_MIXED' '
test_cmp_branch_tree T_MIXED
diff --git a/t/t9603-cvsimport-patchsets.sh b/t/t9603-cvsimport-patchsets.sh
index 958bdce..c4c3c49 100755
--- a/t/t9603-cvsimport-patchsets.sh
+++ b/t/t9603-cvsimport-patchsets.sh
@@ -14,18 +14,17 @@
test_description='git cvsimport testing for correct patchset estimation'
. ./lib-cvs.sh
-CVSROOT="$TEST_DIRECTORY"/t9603/cvsroot
-export CVSROOT
+setup_cvs_test_repository t9603
-test_expect_failure 'import with criss cross times on revisions' '
+test_expect_failure PERL 'import with criss cross times on revisions' '
git cvsimport -p"-x" -C module-git module &&
- cd module-git &&
+ (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 "" >> ../actual-A
+ ) &&
echo "Rev 4
Rev 3
Rev 2
diff --git a/t/t9604-cvsimport-timestamps.sh b/t/t9604-cvsimport-timestamps.sh
new file mode 100755
index 0000000..a4b3db2
--- /dev/null
+++ b/t/t9604-cvsimport-timestamps.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+test_description='git cvsimport timestamps'
+. ./lib-cvs.sh
+
+setup_cvs_test_repository t9604
+
+test_expect_success PERL 'check timestamps are UTC (TZ=CST6CDT)' '
+
+ TZ=CST6CDT git cvsimport -p"-x" -C module-1 module &&
+ git cvsimport -p"-x" -C module-1 module &&
+ (
+ cd module-1 &&
+ git log --format="%s %ai"
+ ) >actual-1 &&
+ cat >expect-1 <<-EOF &&
+ Rev 16 2006-10-29 07:00:01 +0000
+ Rev 15 2006-10-29 06:59:59 +0000
+ Rev 14 2006-04-02 08:00:01 +0000
+ Rev 13 2006-04-02 07:59:59 +0000
+ Rev 12 2005-12-01 00:00:00 +0000
+ Rev 11 2005-11-01 00:00:00 +0000
+ Rev 10 2005-10-01 00:00:00 +0000
+ Rev 9 2005-09-01 00:00:00 +0000
+ Rev 8 2005-08-01 00:00:00 +0000
+ Rev 7 2005-07-01 00:00:00 +0000
+ Rev 6 2005-06-01 00:00:00 +0000
+ Rev 5 2005-05-01 00:00:00 +0000
+ Rev 4 2005-04-01 00:00:00 +0000
+ Rev 3 2005-03-01 00:00:00 +0000
+ Rev 2 2005-02-01 00:00:00 +0000
+ Rev 1 2005-01-01 00:00:00 +0000
+ EOF
+ test_cmp actual-1 expect-1
+'
+
+test_expect_success PERL 'check timestamps with author-specific timezones' '
+
+ cat >cvs-authors <<-EOF &&
+ user1=User One <user1@domain.org>
+ user2=User Two <user2@domain.org> CST6CDT
+ user3=User Three <user3@domain.org> EST5EDT
+ user4=User Four <user4@domain.org> MST7MDT
+ EOF
+ git cvsimport -p"-x" -A cvs-authors -C module-2 module &&
+ (
+ cd module-2 &&
+ git log --format="%s %ai %an"
+ ) >actual-2 &&
+ cat >expect-2 <<-EOF &&
+ Rev 16 2006-10-29 01:00:01 -0600 User Two
+ Rev 15 2006-10-29 01:59:59 -0500 User Two
+ Rev 14 2006-04-02 03:00:01 -0500 User Two
+ Rev 13 2006-04-02 01:59:59 -0600 User Two
+ Rev 12 2005-11-30 17:00:00 -0700 User Four
+ Rev 11 2005-10-31 19:00:00 -0500 User Three
+ Rev 10 2005-09-30 19:00:00 -0500 User Two
+ Rev 9 2005-09-01 00:00:00 +0000 User One
+ Rev 8 2005-07-31 18:00:00 -0600 User Four
+ Rev 7 2005-06-30 20:00:00 -0400 User Three
+ Rev 6 2005-05-31 19:00:00 -0500 User Two
+ Rev 5 2005-05-01 00:00:00 +0000 User One
+ Rev 4 2005-03-31 17:00:00 -0700 User Four
+ Rev 3 2005-02-28 19:00:00 -0500 User Three
+ Rev 2 2005-01-31 18:00:00 -0600 User Two
+ Rev 1 2005-01-01 00:00:00 +0000 User One
+ EOF
+ test_cmp actual-2 expect-2
+'
+
+test_done
diff --git a/t/t9604/cvsroot/.gitattributes b/t/t9604/cvsroot/.gitattributes
new file mode 100644
index 0000000..562b12e
--- /dev/null
+++ b/t/t9604/cvsroot/.gitattributes
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t9604/cvsroot/CVSROOT/.gitignore b/t/t9604/cvsroot/CVSROOT/.gitignore
new file mode 100644
index 0000000..3bb9b34
--- /dev/null
+++ b/t/t9604/cvsroot/CVSROOT/.gitignore
@@ -0,0 +1,2 @@
+history
+val-tags
diff --git a/t/t9604/cvsroot/module/a,v b/t/t9604/cvsroot/module/a,v
new file mode 100644
index 0000000..80658c3
--- /dev/null
+++ b/t/t9604/cvsroot/module/a,v
@@ -0,0 +1,264 @@
+head 1.16;
+access;
+symbols;
+locks; strict;
+comment @# @;
+
+
+1.16
+date 2006.10.29.07.00.01; author user2; state Exp;
+branches;
+next 1.15;
+
+1.15
+date 2006.10.29.06.59.59; author user2; state Exp;
+branches;
+next 1.14;
+
+1.14
+date 2006.04.02.08.00.01; author user2; state Exp;
+branches;
+next 1.13;
+
+1.13
+date 2006.04.02.07.59.59; author user2; state Exp;
+branches;
+next 1.12;
+
+1.12
+date 2005.12.01.00.00.00; author user4; state Exp;
+branches;
+next 1.11;
+
+1.11
+date 2005.11.01.00.00.00; author user3; state Exp;
+branches;
+next 1.10;
+
+1.10
+date 2005.10.01.00.00.00; author user2; state Exp;
+branches;
+next 1.9;
+
+1.9
+date 2005.09.01.00.00.00; author user1; state Exp;
+branches;
+next 1.8;
+
+1.8
+date 2005.08.01.00.00.00; author user4; state Exp;
+branches;
+next 1.7;
+
+1.7
+date 2005.07.01.00.00.00; author user3; state Exp;
+branches;
+next 1.6;
+
+1.6
+date 2005.06.01.00.00.00; author user2; state Exp;
+branches;
+next 1.5;
+
+1.5
+date 2005.05.01.00.00.00; author user1; state Exp;
+branches;
+next 1.4;
+
+1.4
+date 2005.04.01.00.00.00; author user4; state Exp;
+branches;
+next 1.3;
+
+1.3
+date 2005.03.01.00.00.00; author user3; state Exp;
+branches;
+next 1.2;
+
+1.2
+date 2005.02.01.00.00.00; author user2; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2005.01.01.00.00.00; author user1; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.16
+log
+@Rev 16
+@
+text
+@Rev 16
+@
+
+
+1.15
+log
+@Rev 15
+@
+text
+@d1 1
+a1 1
+Rev 15
+@
+
+
+1.14
+log
+@Rev 14
+@
+text
+@d1 1
+a1 1
+Rev 14
+@
+
+
+1.13
+log
+@Rev 13
+@
+text
+@d1 1
+a1 1
+Rev 13
+@
+
+
+1.12
+log
+@Rev 12
+@
+text
+@d1 1
+a1 1
+Rev 12
+@
+
+
+1.11
+log
+@Rev 11
+@
+text
+@d1 1
+a1 1
+Rev 11
+@
+
+
+1.10
+log
+@Rev 10
+@
+text
+@d1 1
+a1 1
+Rev 10
+@
+
+
+1.9
+log
+@Rev 9
+@
+text
+@d1 1
+a1 1
+Rev 9
+@
+
+
+1.8
+log
+@Rev 8
+@
+text
+@d1 1
+a1 1
+Rev 8
+@
+
+
+1.7
+log
+@Rev 7
+@
+text
+@d1 1
+a1 1
+Rev 7
+@
+
+
+1.6
+log
+@Rev 6
+@
+text
+@d1 1
+a1 1
+Rev 6
+@
+
+
+1.5
+log
+@Rev 5
+@
+text
+@d1 1
+a1 1
+Rev 5
+@
+
+
+1.4
+log
+@Rev 4
+@
+text
+@d1 1
+a1 1
+Rev 4
+@
+
+
+1.3
+log
+@Rev 3
+@
+text
+@d1 1
+a1 1
+Rev 3
+@
+
+
+1.2
+log
+@Rev 2
+@
+text
+@d1 1
+a1 1
+Rev 2
+@
+
+
+1.1
+log
+@Rev 1
+@
+text
+@d1 1
+a1 1
+Rev 1
+@
diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh
index 3787186..102c133 100755
--- a/t/t9700-perl-git.sh
+++ b/t/t9700-perl-git.sh
@@ -11,7 +11,7 @@ if ! test_have_prereq PERL; then
test_done
fi
-"$PERL_PATH" -MTest::More -e 0 2>/dev/null || {
+perl -MTest::More -e 0 2>/dev/null || {
skip_all="Perl Test::More unavailable, skipping test"
test_done
}
@@ -43,7 +43,11 @@ test_expect_success \
git config --add test.booltrue true &&
git config --add test.boolfalse no &&
git config --add test.boolother other &&
- git config --add test.int 2k
+ git config --add test.int 2k &&
+ git config --add test.path "~/foo" &&
+ git config --add test.pathexpanded "$HOME/foo" &&
+ git config --add test.pathmulti foo &&
+ git config --add test.pathmulti bar
'
# The external test will outputs its own plan
@@ -51,6 +55,6 @@ test_external_has_tap=1
test_external_without_stderr \
'Perl API' \
- "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl
+ perl "$TEST_DIRECTORY"/t9700/test.pl
test_done
diff --git a/t/t9700/test.pl b/t/t9700/test.pl
index 671f38d..1b75c91 100755
--- a/t/t9700/test.pl
+++ b/t/t9700/test.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
use lib (split(/:/, $ENV{GITPERLLIB}));
-use 5.006002;
+use 5.008;
use warnings;
use strict;
@@ -17,6 +17,12 @@ BEGIN {
use Cwd;
use File::Basename;
+sub adjust_dirsep {
+ my $path = shift;
+ $path =~ s{\\}{/}g;
+ return $path;
+}
+
BEGIN { use_ok('Git') }
# set up
@@ -33,6 +39,10 @@ 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");
+is(adjust_dirsep($r->config_path("test.path")), $r->config("test.pathexpanded"),
+ "config_path: ~/foo expansion");
+is_deeply([$r->config_path("test.pathmulti")], ["foo", "bar"],
+ "config_path: multiple values");
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
@@ -41,9 +51,9 @@ is($r->get_color("color.test.slot1", "red"), $ansi_green, "get_color");
# Failure cases for config:
# Save and restore STDERR; we will probably extract this into a
# "dies_ok" method and possibly move the STDERR handling to Git.pm.
-open our $tmpstderr, ">&STDERR" or die "cannot save STDERR"; close STDERR;
-eval { $r->config("test.dupstring") };
-ok($@, "config: duplicate entry in scalar context fails");
+open our $tmpstderr, ">&STDERR" or die "cannot save STDERR";
+open STDERR, ">", "/dev/null" or die "cannot redirect STDERR to /dev/null";
+is($r->config("test.dupstring"), "value2", "config: multivar");
eval { $r->config_bool("test.boolother") };
ok($@, "config_bool: non-boolean values fail");
open STDERR, ">&", $tmpstderr or die "cannot restore STDERR";
@@ -113,6 +123,16 @@ 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');
+# commands outside working tree
+chdir($abs_repo_dir . '/..');
+my $r3 = Git->repository(Directory => $abs_repo_dir);
+my $tmpfile3 = "$abs_repo_dir/file3.tmp";
+open TEMPFILE3, "+>$tmpfile3" or die "Can't open $tmpfile3: $!";
+is($r3->cat_blob($file1hash, \*TEMPFILE3), 15, "cat_blob(outside): size");
+close TEMPFILE3;
+unlink $tmpfile3;
+chdir($abs_repo_dir);
+
printf "1..%d\n", Test::More->builder->current_test;
my $is_passing = eval { Test::More->is_passing };
diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh
new file mode 100755
index 0000000..0730f18
--- /dev/null
+++ b/t/t9800-git-p4-basic.sh
@@ -0,0 +1,264 @@
+#!/bin/sh
+
+test_description='git p4 tests'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'add p4 files' '
+ (
+ cd "$cli" &&
+ echo file1 >file1 &&
+ p4 add file1 &&
+ p4 submit -d "file1" &&
+ echo file2 >file2 &&
+ p4 add file2 &&
+ p4 submit -d "file2"
+ )
+'
+
+test_expect_success 'basic git p4 clone' '
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git log --oneline >lines &&
+ test_line_count = 1 lines
+ )
+'
+
+test_expect_success 'depot typo error' '
+ test_must_fail git p4 clone --dest="$git" /depot 2>errs &&
+ grep "Depot paths must start with" errs
+'
+
+test_expect_success 'git p4 clone @all' '
+ git p4 clone --dest="$git" //depot@all &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git log --oneline >lines &&
+ test_line_count = 2 lines
+ )
+'
+
+test_expect_success 'git p4 sync uninitialized repo' '
+ test_create_repo "$git" &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ test_must_fail git p4 sync 2>errs &&
+ test_i18ngrep "Perhaps you never did" errs
+ )
+'
+
+#
+# Create a git repo by hand. Add a commit so that HEAD is valid.
+# Test imports a new p4 repository into a new git branch.
+#
+test_expect_success 'git p4 sync new branch' '
+ test_create_repo "$git" &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ test_commit head &&
+ git p4 sync --branch=refs/remotes/p4/depot //depot@all &&
+ git log --oneline p4/depot >lines &&
+ test_line_count = 2 lines
+ )
+'
+
+test_expect_success 'clone two dirs' '
+ (
+ cd "$cli" &&
+ mkdir sub1 sub2 &&
+ echo sub1/f1 >sub1/f1 &&
+ echo sub2/f2 >sub2/f2 &&
+ p4 add sub1/f1 &&
+ p4 submit -d "sub1/f1" &&
+ p4 add sub2/f2 &&
+ p4 submit -d "sub2/f2"
+ ) &&
+ git p4 clone --dest="$git" //depot/sub1 //depot/sub2 &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git ls-files >lines &&
+ test_line_count = 2 lines &&
+ git log --oneline p4/master >lines &&
+ test_line_count = 1 lines
+ )
+'
+
+test_expect_success 'clone two dirs, @all' '
+ (
+ cd "$cli" &&
+ echo sub1/f3 >sub1/f3 &&
+ p4 add sub1/f3 &&
+ p4 submit -d "sub1/f3"
+ ) &&
+ git p4 clone --dest="$git" //depot/sub1@all //depot/sub2@all &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git ls-files >lines &&
+ test_line_count = 3 lines &&
+ git log --oneline p4/master >lines &&
+ test_line_count = 3 lines
+ )
+'
+
+test_expect_success 'clone two dirs, @all, conflicting files' '
+ (
+ cd "$cli" &&
+ echo sub2/f3 >sub2/f3 &&
+ p4 add sub2/f3 &&
+ p4 submit -d "sub2/f3"
+ ) &&
+ git p4 clone --dest="$git" //depot/sub1@all //depot/sub2@all &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git ls-files >lines &&
+ test_line_count = 3 lines &&
+ git log --oneline p4/master >lines &&
+ test_line_count = 4 lines &&
+ echo sub2/f3 >expected &&
+ test_cmp expected f3
+ )
+'
+
+revision_ranges="2000/01/01,#head \
+ 1,2080/01/01 \
+ 2000/01/01,2080/01/01 \
+ 2000/01/01,1000 \
+ 1,1000"
+
+test_expect_success 'clone using non-numeric revision ranges' '
+ test_when_finished cleanup_git &&
+ for r in $revision_ranges
+ do
+ rm -fr "$git" &&
+ test ! -d "$git" &&
+ git p4 clone --dest="$git" //depot@$r &&
+ (
+ cd "$git" &&
+ git ls-files >lines &&
+ test_line_count = 6 lines
+ )
+ done
+'
+
+test_expect_success 'clone with date range, excluding some changes' '
+ test_when_finished cleanup_git &&
+ before=$(date +%Y/%m/%d:%H:%M:%S) &&
+ sleep 2 &&
+ (
+ cd "$cli" &&
+ :>date_range_test &&
+ p4 add date_range_test &&
+ p4 submit -d "Adding file"
+ ) &&
+ git p4 clone --dest="$git" //depot@1,$before &&
+ (
+ cd "$git" &&
+ test_path_is_missing date_range_test
+ )
+'
+
+test_expect_success 'exit when p4 fails to produce marshaled output' '
+ mkdir badp4dir &&
+ test_when_finished "rm badp4dir/p4 && rmdir badp4dir" &&
+ cat >badp4dir/p4 <<-EOF &&
+ #!$SHELL_PATH
+ exit 1
+ EOF
+ chmod 755 badp4dir/p4 &&
+ (
+ PATH="$TRASH_DIRECTORY/badp4dir:$PATH" &&
+ export PATH &&
+ test_expect_code 1 git p4 clone --dest="$git" //depot >errs 2>&1
+ ) &&
+ cat errs &&
+ test_i18ngrep ! Traceback errs
+'
+
+# Hide a file from p4d, make sure we catch its complaint. This won't fail in
+# p4 changes, files, or describe; just in p4 print. If P4CLIENT is unset, the
+# message will include "Librarian checkout".
+test_expect_success 'exit gracefully for p4 server errors' '
+ test_when_finished "mv \"$db\"/depot/file1,v,hidden \"$db\"/depot/file1,v" &&
+ mv "$db"/depot/file1,v "$db"/depot/file1,v,hidden &&
+ test_when_finished cleanup_git &&
+ test_expect_code 1 git p4 clone --dest="$git" //depot@1 >out 2>err &&
+ test_i18ngrep "Error from p4 print" err
+'
+
+test_expect_success 'clone --bare should make a bare repository' '
+ rm -rf "$git" &&
+ git p4 clone --dest="$git" --bare //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ test_path_is_missing .git &&
+ git config --get --bool core.bare true &&
+ git rev-parse --verify refs/remotes/p4/master &&
+ git rev-parse --verify refs/remotes/p4/HEAD &&
+ git rev-parse --verify refs/heads/master &&
+ git rev-parse --verify HEAD
+ )
+'
+
+# Sleep a bit so that the top-most p4 change did not happen "now". Then
+# import the repo and make sure that the initial import has the same time
+# as the top-most change.
+test_expect_success 'initial import time from top change time' '
+ p4change=$(p4 -G changes -m 1 //depot/... | marshal_dump change) &&
+ p4time=$(p4 -G changes -m 1 //depot/... | marshal_dump time) &&
+ sleep 3 &&
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ gittime=$(git show -s --raw --pretty=format:%at HEAD) &&
+ echo $p4time $gittime &&
+ test $p4time = $gittime
+ )
+'
+
+test_expect_success 'unresolvable host in P4PORT should display error' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ P4PORT=nosuchhost:65537 &&
+ export P4PORT &&
+ test_expect_code 1 git p4 sync >out 2>err &&
+ grep "connect to nosuchhost" err
+ )
+'
+
+test_expect_success 'submit from detached head' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git checkout p4/master &&
+ >detached_head_test &&
+ git add detached_head_test &&
+ git commit -m "add detached_head" &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit &&
+ git p4 rebase &&
+ git log p4/master | grep detached_head
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh
new file mode 100755
index 0000000..0aafd03
--- /dev/null
+++ b/t/t9801-git-p4-branch.sh
@@ -0,0 +1,617 @@
+#!/bin/sh
+
+test_description='git p4 tests for p4 branches'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+#
+# 1: //depot/main/f1
+# 2: //depot/main/f2
+# 3: integrate //depot/main/... -> //depot/branch1/...
+# 4: //depot/main/f4
+# 5: //depot/branch1/f5
+# .: named branch branch2
+# 6: integrate -b branch2
+# 7: //depot/branch2/f7
+# 8: //depot/main/f8
+#
+test_expect_success 'basic p4 branches' '
+ (
+ cd "$cli" &&
+ mkdir -p main &&
+
+ echo f1 >main/f1 &&
+ p4 add main/f1 &&
+ p4 submit -d "main/f1" &&
+
+ echo f2 >main/f2 &&
+ p4 add main/f2 &&
+ p4 submit -d "main/f2" &&
+
+ p4 integrate //depot/main/... //depot/branch1/... &&
+ p4 submit -d "integrate main to branch1" &&
+
+ echo f4 >main/f4 &&
+ p4 add main/f4 &&
+ p4 submit -d "main/f4" &&
+
+ echo f5 >branch1/f5 &&
+ p4 add branch1/f5 &&
+ p4 submit -d "branch1/f5" &&
+
+ p4 branch -i <<-EOF &&
+ Branch: branch2
+ View: //depot/main/... //depot/branch2/...
+ EOF
+
+ p4 integrate -b branch2 &&
+ p4 submit -d "integrate main to branch2" &&
+
+ echo f7 >branch2/f7 &&
+ p4 add branch2/f7 &&
+ p4 submit -d "branch2/f7" &&
+
+ echo f8 >main/f8 &&
+ p4 add main/f8 &&
+ p4 submit -d "main/f8"
+ )
+'
+
+test_expect_success 'import main, no branch detection' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot/main@all &&
+ (
+ cd "$git" &&
+ git log --oneline --graph --decorate --all &&
+ git rev-list master >wc &&
+ test_line_count = 4 wc
+ )
+'
+
+test_expect_success 'import branch1, no branch detection' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot/branch1@all &&
+ (
+ cd "$git" &&
+ git log --oneline --graph --decorate --all &&
+ git rev-list master >wc &&
+ test_line_count = 2 wc
+ )
+'
+
+test_expect_success 'import branch2, no branch detection' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot/branch2@all &&
+ (
+ cd "$git" &&
+ git log --oneline --graph --decorate --all &&
+ git rev-list master >wc &&
+ test_line_count = 2 wc
+ )
+'
+
+test_expect_success 'import depot, no branch detection' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log --oneline --graph --decorate --all &&
+ git rev-list master >wc &&
+ test_line_count = 8 wc
+ )
+'
+
+test_expect_success 'import depot, branch detection' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" --detect-branches //depot@all &&
+ (
+ cd "$git" &&
+
+ git log --oneline --graph --decorate --all &&
+
+ # 4 main commits
+ git rev-list master >wc &&
+ test_line_count = 4 wc &&
+
+ # 3 main, 1 integrate, 1 on branch2
+ git rev-list p4/depot/branch2 >wc &&
+ test_line_count = 5 wc &&
+
+ # no branch1, since no p4 branch created for it
+ test_must_fail git show-ref p4/depot/branch1
+ )
+'
+
+test_expect_success 'import depot, branch detection, branchList branch definition' '
+ test_when_finished cleanup_git &&
+ test_create_repo "$git" &&
+ (
+ cd "$git" &&
+ git config git-p4.branchList main:branch1 &&
+ git p4 clone --dest=. --detect-branches //depot@all &&
+
+ git log --oneline --graph --decorate --all &&
+
+ # 4 main commits
+ git rev-list master >wc &&
+ test_line_count = 4 wc &&
+
+ # 3 main, 1 integrate, 1 on branch2
+ git rev-list p4/depot/branch2 >wc &&
+ test_line_count = 5 wc &&
+
+ # 2 main, 1 integrate, 1 on branch1
+ git rev-list p4/depot/branch1 >wc &&
+ test_line_count = 4 wc
+ )
+'
+
+test_expect_success 'restart p4d' '
+ kill_p4d &&
+ start_p4d
+'
+
+#
+# 1: //depot/branch1/file1
+# //depot/branch1/file2
+# 2: integrate //depot/branch1/... -> //depot/branch2/...
+# 3: //depot/branch1/file3
+# 4: //depot/branch1/file2 (edit)
+# 5: integrate //depot/branch1/... -> //depot/branch3/...
+#
+## Create a simple branch structure in P4 depot.
+test_expect_success 'add simple p4 branches' '
+ (
+ cd "$cli" &&
+ mkdir branch1 &&
+ cd branch1 &&
+ echo file1 >file1 &&
+ echo file2 >file2 &&
+ p4 add file1 file2 &&
+ p4 submit -d "Create branch1" &&
+ p4 integrate //depot/branch1/... //depot/branch2/... &&
+ p4 submit -d "Integrate branch2 from branch1" &&
+ echo file3 >file3 &&
+ p4 add file3 &&
+ p4 submit -d "add file3 in branch1" &&
+ p4 open file2 &&
+ echo update >>file2 &&
+ p4 submit -d "update file2 in branch1" &&
+ p4 integrate //depot/branch1/... //depot/branch3/... &&
+ p4 submit -d "Integrate branch3 from branch1"
+ )
+'
+
+# Configure branches through git-config and clone them.
+# All files are tested to make sure branches were cloned correctly.
+# Finally, make an update to branch1 on P4 side to check if it is imported
+# correctly by git p4.
+test_expect_success 'git p4 clone simple branches' '
+ test_when_finished cleanup_git &&
+ test_create_repo "$git" &&
+ (
+ cd "$git" &&
+ git config git-p4.branchList branch1:branch2 &&
+ git config --add git-p4.branchList branch1:branch3 &&
+ git p4 clone --dest=. --detect-branches //depot@all &&
+ git log --all --graph --decorate --stat &&
+ git reset --hard p4/depot/branch1 &&
+ test -f file1 &&
+ test -f file2 &&
+ test -f file3 &&
+ grep update file2 &&
+ git reset --hard p4/depot/branch2 &&
+ test -f file1 &&
+ test -f file2 &&
+ test ! -f file3 &&
+ ! grep update file2 &&
+ git reset --hard p4/depot/branch3 &&
+ test -f file1 &&
+ test -f file2 &&
+ test -f file3 &&
+ grep update file2 &&
+ cd "$cli" &&
+ cd branch1 &&
+ p4 edit file2 &&
+ echo file2_ >>file2 &&
+ p4 submit -d "update file2 in branch1" &&
+ cd "$git" &&
+ git reset --hard p4/depot/branch1 &&
+ git p4 rebase &&
+ grep file2_ file2
+ )
+'
+
+# Create a complex branch structure in P4 depot to check if they are correctly
+# cloned. The branches are created from older changelists to check if git p4 is
+# able to correctly detect them.
+# The final expected structure is:
+# `branch1
+# | `- file1
+# | `- file2 (updated)
+# | `- file3
+# `branch2
+# | `- file1
+# | `- file2
+# `branch3
+# | `- file1
+# | `- file2 (updated)
+# | `- file3
+# `branch4
+# | `- file1
+# | `- file2
+# `branch5
+# `- file1
+# `- file2
+# `- file3
+test_expect_success 'git p4 add complex branches' '
+ (
+ cd "$cli" &&
+ changelist=$(p4 changes -m1 //depot/... | cut -d" " -f2) &&
+ changelist=$(($changelist - 5)) &&
+ p4 integrate //depot/branch1/...@$changelist //depot/branch4/... &&
+ p4 submit -d "Integrate branch4 from branch1@${changelist}" &&
+ changelist=$(($changelist + 2)) &&
+ p4 integrate //depot/branch1/...@$changelist //depot/branch5/... &&
+ p4 submit -d "Integrate branch5 from branch1@${changelist}"
+ )
+'
+
+# Configure branches through git-config and clone them. git p4 will only be able
+# to clone the original structure if it is able to detect the origin changelist
+# of each branch.
+test_expect_success 'git p4 clone complex branches' '
+ test_when_finished cleanup_git &&
+ test_create_repo "$git" &&
+ (
+ cd "$git" &&
+ git config git-p4.branchList branch1:branch2 &&
+ git config --add git-p4.branchList branch1:branch3 &&
+ git config --add git-p4.branchList branch1:branch4 &&
+ git config --add git-p4.branchList branch1:branch5 &&
+ git p4 clone --dest=. --detect-branches //depot@all &&
+ git log --all --graph --decorate --stat &&
+ git reset --hard p4/depot/branch1 &&
+ test_path_is_file file1 &&
+ test_path_is_file file2 &&
+ test_path_is_file file3 &&
+ grep update file2 &&
+ git reset --hard p4/depot/branch2 &&
+ test_path_is_file file1 &&
+ test_path_is_file file2 &&
+ test_path_is_missing file3 &&
+ ! grep update file2 &&
+ git reset --hard p4/depot/branch3 &&
+ test_path_is_file file1 &&
+ test_path_is_file file2 &&
+ test_path_is_file file3 &&
+ grep update file2 &&
+ git reset --hard p4/depot/branch4 &&
+ test_path_is_file file1 &&
+ test_path_is_file file2 &&
+ test_path_is_missing file3 &&
+ ! grep update file2 &&
+ git reset --hard p4/depot/branch5 &&
+ test_path_is_file file1 &&
+ test_path_is_file file2 &&
+ test_path_is_file file3 &&
+ ! grep update file2 &&
+ test_path_is_missing .git/git-p4-tmp
+ )
+'
+
+# Move branch3/file3 to branch4/file3 in a single changelist
+test_expect_success 'git p4 submit to two branches in a single changelist' '
+ (
+ cd "$cli" &&
+ p4 integrate //depot/branch3/file3 //depot/branch4/file3 &&
+ p4 delete //depot/branch3/file3 &&
+ p4 submit -d "Move branch3/file3 to branch4/file3"
+ )
+'
+
+# Confirm that changes to two branches done in a single changelist
+# are correctly imported by git p4
+test_expect_success 'git p4 sync changes to two branches in the same changelist' '
+ test_when_finished cleanup_git &&
+ test_create_repo "$git" &&
+ (
+ cd "$git" &&
+ git config git-p4.branchList branch1:branch2 &&
+ git config --add git-p4.branchList branch1:branch3 &&
+ git config --add git-p4.branchList branch1:branch4 &&
+ git config --add git-p4.branchList branch1:branch5 &&
+ git p4 clone --dest=. --detect-branches //depot@all &&
+ git log --all --graph --decorate --stat &&
+ git reset --hard p4/depot/branch1 &&
+ test_path_is_file file1 &&
+ test_path_is_file file2 &&
+ test_path_is_file file3 &&
+ grep update file2 &&
+ git reset --hard p4/depot/branch2 &&
+ test_path_is_file file1 &&
+ test_path_is_file file2 &&
+ test_path_is_missing file3 &&
+ ! grep update file2 &&
+ git reset --hard p4/depot/branch3 &&
+ test_path_is_file file1 &&
+ test_path_is_file file2 &&
+ test_path_is_missing file3 &&
+ grep update file2 &&
+ git reset --hard p4/depot/branch4 &&
+ test_path_is_file file1 &&
+ test_path_is_file file2 &&
+ test_path_is_file file3 &&
+ ! grep update file2 &&
+ git reset --hard p4/depot/branch5 &&
+ test_path_is_file file1 &&
+ test_path_is_file file2 &&
+ test_path_is_file file3 &&
+ ! grep update file2 &&
+ test_path_is_missing .git/git-p4-tmp
+ )
+'
+
+# Create a branch by integrating a single file
+test_expect_success 'git p4 file subset branch' '
+ (
+ cd "$cli" &&
+ p4 integrate //depot/branch1/file1 //depot/branch6/file1 &&
+ p4 submit -d "Integrate file1 alone from branch1 to branch6"
+ )
+'
+
+# Check if git p4 creates a new branch containing a single file,
+# instead of keeping the old files from the original branch
+test_expect_failure 'git p4 clone file subset branch' '
+ test_when_finished cleanup_git &&
+ test_create_repo "$git" &&
+ (
+ cd "$git" &&
+ git config git-p4.branchList branch1:branch2 &&
+ git config --add git-p4.branchList branch1:branch3 &&
+ git config --add git-p4.branchList branch1:branch4 &&
+ git config --add git-p4.branchList branch1:branch5 &&
+ git config --add git-p4.branchList branch1:branch6 &&
+ git p4 clone --dest=. --detect-branches //depot@all &&
+ git log --all --graph --decorate --stat &&
+ git reset --hard p4/depot/branch1 &&
+ test_path_is_file file1 &&
+ test_path_is_file file2 &&
+ test_path_is_file file3 &&
+ grep update file2 &&
+ git reset --hard p4/depot/branch2 &&
+ test_path_is_file file1 &&
+ test_path_is_file file2 &&
+ test_path_is_missing file3 &&
+ ! grep update file2 &&
+ git reset --hard p4/depot/branch3 &&
+ test_path_is_file file1 &&
+ test_path_is_file file2 &&
+ test_path_is_missing file3 &&
+ grep update file2 &&
+ git reset --hard p4/depot/branch4 &&
+ test_path_is_file file1 &&
+ test_path_is_file file2 &&
+ test_path_is_file file3 &&
+ ! grep update file2 &&
+ git reset --hard p4/depot/branch5 &&
+ test_path_is_file file1 &&
+ test_path_is_file file2 &&
+ test_path_is_file file3 &&
+ ! grep update file2 &&
+ git reset --hard p4/depot/branch6 &&
+ test_path_is_file file1 &&
+ test_path_is_missing file2 &&
+ test_path_is_missing file3
+ )
+'
+
+# From a report in http://stackoverflow.com/questions/11893688
+# where --use-client-spec caused branch prefixes not to be removed;
+# every file in git appeared into a subdirectory of the branch name.
+test_expect_success 'use-client-spec detect-branches setup' '
+ rm -rf "$cli" &&
+ mkdir "$cli" &&
+ (
+ cd "$cli" &&
+ client_view "//depot/usecs/... //client/..." &&
+ mkdir b1 &&
+ echo b1/b1-file1 >b1/b1-file1 &&
+ p4 add b1/b1-file1 &&
+ p4 submit -d "b1/b1-file1" &&
+
+ p4 integrate //depot/usecs/b1/... //depot/usecs/b2/... &&
+ p4 submit -d "b1 -> b2" &&
+ p4 branch -i <<-EOF &&
+ Branch: b2
+ View: //depot/usecs/b1/... //depot/usecs/b2/...
+ EOF
+
+ echo b2/b2-file2 >b2/b2-file2 &&
+ p4 add b2/b2-file2 &&
+ p4 submit -d "b2/b2-file2"
+ )
+'
+
+test_expect_success 'use-client-spec detect-branches files in top-level' '
+ test_when_finished cleanup_git &&
+ test_create_repo "$git" &&
+ (
+ cd "$git" &&
+ git p4 sync --detect-branches --use-client-spec //depot/usecs@all &&
+ git checkout -b master p4/usecs/b1 &&
+ test_path_is_file b1-file1 &&
+ test_path_is_missing b2-file2 &&
+ test_path_is_missing b1 &&
+ test_path_is_missing b2 &&
+
+ git checkout -b b2 p4/usecs/b2 &&
+ test_path_is_file b1-file1 &&
+ test_path_is_file b2-file2 &&
+ test_path_is_missing b1 &&
+ test_path_is_missing b2
+ )
+'
+
+test_expect_success 'use-client-spec detect-branches skips branches setup' '
+ (
+ cd "$cli" &&
+
+ p4 integrate //depot/usecs/b1/... //depot/usecs/b3/... &&
+ p4 submit -d "b1 -> b3" &&
+ p4 branch -i <<-EOF &&
+ Branch: b3
+ View: //depot/usecs/b1/... //depot/usecs/b3/...
+ EOF
+
+ echo b3/b3-file3_1 >b3/b3-file3_1 &&
+ echo b3/b3-file3_2 >b3/b3-file3_2 &&
+ p4 add b3/b3-file3_1 &&
+ p4 add b3/b3-file3_2 &&
+ p4 submit -d "b3/b3-file3_1 b3/b3-file3_2"
+ )
+'
+
+test_expect_success 'use-client-spec detect-branches skips branches' '
+ client_view "//depot/usecs/... //client/..." \
+ "-//depot/usecs/b3/... //client/b3/..." &&
+ test_when_finished cleanup_git &&
+ test_create_repo "$git" &&
+ (
+ cd "$git" &&
+ git p4 sync --detect-branches --use-client-spec //depot/usecs@all &&
+ test_must_fail git rev-parse refs/remotes/p4/usecs/b3
+ )
+'
+
+test_expect_success 'use-client-spec detect-branches skips files in branches' '
+ client_view "//depot/usecs/... //client/..." \
+ "-//depot/usecs/b3/b3-file3_1 //client/b3/b3-file3_1" &&
+ test_when_finished cleanup_git &&
+ test_create_repo "$git" &&
+ (
+ cd "$git" &&
+ git p4 sync --detect-branches --use-client-spec //depot/usecs@all &&
+ git checkout -b master p4/usecs/b3 &&
+ test_path_is_file b1-file1 &&
+ test_path_is_file b3-file3_2 &&
+ test_path_is_missing b3-file3_1
+ )
+'
+
+test_expect_success 'restart p4d' '
+ kill_p4d &&
+ start_p4d
+'
+
+#
+# 1: //depot/branch1/base/file1
+# //depot/branch1/base/file2
+# //depot/branch1/base/dir/sub_file1
+# 2: integrate //depot/branch1/base/... -> //depot/branch2/base/...
+# 3: //depot/branch1/base/file3
+# 4: //depot/branch1/base/file2 (edit)
+# 5: integrate //depot/branch1/base/... -> //depot/branch3/base/...
+#
+# Note: the client view removes the "base" folder from the workspace
+# and moves sub_file1 one level up.
+test_expect_success 'add simple p4 branches with common base folder on each branch' '
+ (
+ cd "$cli" &&
+ client_view "//depot/branch1/base/... //client/branch1/..." \
+ "//depot/branch1/base/dir/sub_file1 //client/branch1/sub_file1" \
+ "//depot/branch2/base/... //client/branch2/..." \
+ "//depot/branch3/base/... //client/branch3/..." &&
+ mkdir -p branch1 &&
+ cd branch1 &&
+ echo file1 >file1 &&
+ echo file2 >file2 &&
+ mkdir dir &&
+ echo sub_file1 >sub_file1 &&
+ p4 add file1 file2 sub_file1 &&
+ p4 submit -d "Create branch1" &&
+ p4 integrate //depot/branch1/base/... //depot/branch2/base/... &&
+ p4 submit -d "Integrate branch2 from branch1" &&
+ echo file3 >file3 &&
+ p4 add file3 &&
+ p4 submit -d "add file3 in branch1" &&
+ p4 open file2 &&
+ echo update >>file2 &&
+ p4 submit -d "update file2 in branch1" &&
+ p4 integrate //depot/branch1/base/... //depot/branch3/base/... &&
+ p4 submit -d "Integrate branch3 from branch1"
+ )
+'
+
+# Configure branches through git-config and clone them.
+# All files are tested to make sure branches were cloned correctly.
+# Finally, make an update to branch1 on P4 side to check if it is imported
+# correctly by git p4.
+# git p4 is expected to use the client view to also not include the common
+# "base" folder in the imported directory structure.
+test_expect_success 'git p4 clone simple branches with base folder on server side' '
+ test_create_repo "$git" &&
+ (
+ cd "$git" &&
+ git config git-p4.branchList branch1:branch2 &&
+ git config --add git-p4.branchList branch1:branch3 &&
+ git p4 clone --dest=. --use-client-spec --detect-branches //depot@all &&
+ git log --all --graph --decorate --stat &&
+ git reset --hard p4/depot/branch1 &&
+ test -f file1 &&
+ test -f file2 &&
+ test -f file3 &&
+ test -f sub_file1 &&
+ grep update file2 &&
+ git reset --hard p4/depot/branch2 &&
+ test -f file1 &&
+ test -f file2 &&
+ test ! -f file3 &&
+ test -f sub_file1 &&
+ ! grep update file2 &&
+ git reset --hard p4/depot/branch3 &&
+ test -f file1 &&
+ test -f file2 &&
+ test -f file3 &&
+ test -f sub_file1 &&
+ grep update file2 &&
+ cd "$cli" &&
+ cd branch1 &&
+ p4 edit file2 &&
+ echo file2_ >>file2 &&
+ p4 submit -d "update file2 in branch1" &&
+ cd "$git" &&
+ git reset --hard p4/depot/branch1 &&
+ git p4 rebase &&
+ grep file2_ file2
+ )
+'
+
+# Now update a file in one of the branches in git and submit to P4
+test_expect_success 'Update a file in git side and submit to P4 using client view' '
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git reset --hard p4/depot/branch1 &&
+ echo "client spec" >> file1 &&
+ git add -u . &&
+ git commit -m "update file1 in branch1" &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit --verbose &&
+ cd "$cli" &&
+ p4 sync ... &&
+ cd branch1 &&
+ grep "client spec" file1
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh
new file mode 100755
index 0000000..eb9a8ed
--- /dev/null
+++ b/t/t9802-git-p4-filetype.sh
@@ -0,0 +1,340 @@
+#!/bin/sh
+
+test_description='git p4 filetype tests'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+#
+# This series of tests checks newline handling Both p4 and
+# git store newlines as \n, and have options to choose how
+# newlines appear in checked-out files.
+#
+test_expect_success 'p4 client newlines, unix' '
+ (
+ cd "$cli" &&
+ p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i &&
+ printf "unix\ncrlf\n" >f-unix &&
+ printf "unix\r\ncrlf\r\n" >f-unix-as-crlf &&
+ p4 add -t text f-unix &&
+ p4 submit -d f-unix &&
+
+ # LineEnd: unix; should be no change after sync
+ cp f-unix f-unix-orig &&
+ p4 sync -f &&
+ test_cmp f-unix-orig f-unix &&
+
+ # make sure stored in repo as unix newlines
+ # use sed to eat python-appended newline
+ p4 -G print //depot/f-unix | marshal_dump data 2 |\
+ sed \$d >f-unix-p4-print &&
+ test_cmp f-unix-orig f-unix-p4-print &&
+
+ # switch to win, make sure lf -> crlf
+ p4 client -o | sed "/LineEnd/s/:.*/:win/" | p4 client -i &&
+ p4 sync -f &&
+ test_cmp f-unix-as-crlf f-unix
+ )
+'
+
+test_expect_success 'p4 client newlines, win' '
+ (
+ cd "$cli" &&
+ p4 client -o | sed "/LineEnd/s/:.*/:win/" | p4 client -i &&
+ printf "win\r\ncrlf\r\n" >f-win &&
+ printf "win\ncrlf\n" >f-win-as-lf &&
+ p4 add -t text f-win &&
+ p4 submit -d f-win &&
+
+ # LineEnd: win; should be no change after sync
+ cp f-win f-win-orig &&
+ p4 sync -f &&
+ test_cmp f-win-orig f-win &&
+
+ # make sure stored in repo as unix newlines
+ # use sed to eat python-appened newline
+ p4 -G print //depot/f-win | marshal_dump data 2 |\
+ sed \$d >f-win-p4-print &&
+ test_cmp f-win-as-lf f-win-p4-print &&
+
+ # switch to unix, make sure lf -> crlf
+ p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i &&
+ p4 sync -f &&
+ test_cmp f-win-as-lf f-win
+ )
+'
+
+test_expect_success 'ensure blobs store only lf newlines' '
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init &&
+ git p4 sync //depot@all &&
+
+ # verify the files in .git are stored only with newlines
+ o=$(git ls-tree p4/master -- f-unix | cut -f1 | cut -d\ -f3) &&
+ git cat-file blob $o >f-unix-blob &&
+ test_cmp "$cli"/f-unix-orig f-unix-blob &&
+
+ o=$(git ls-tree p4/master -- f-win | cut -f1 | cut -d\ -f3) &&
+ git cat-file blob $o >f-win-blob &&
+ test_cmp "$cli"/f-win-as-lf f-win-blob &&
+
+ rm f-unix-blob f-win-blob
+ )
+'
+
+test_expect_success 'gitattributes setting eol=lf produces lf newlines' '
+ test_when_finished cleanup_git &&
+ (
+ # checkout the files and make sure core.eol works as planned
+ cd "$git" &&
+ git init &&
+ echo "* eol=lf" >.gitattributes &&
+ git p4 sync //depot@all &&
+ git checkout -b master p4/master &&
+ test_cmp "$cli"/f-unix-orig f-unix &&
+ test_cmp "$cli"/f-win-as-lf f-win
+ )
+'
+
+test_expect_success 'gitattributes setting eol=crlf produces crlf newlines' '
+ test_when_finished cleanup_git &&
+ (
+ # checkout the files and make sure core.eol works as planned
+ cd "$git" &&
+ git init &&
+ echo "* eol=crlf" >.gitattributes &&
+ git p4 sync //depot@all &&
+ git checkout -b master p4/master &&
+ test_cmp "$cli"/f-unix-as-crlf f-unix &&
+ test_cmp "$cli"/f-win-orig f-win
+ )
+'
+
+test_expect_success 'crlf cleanup' '
+ (
+ cd "$cli" &&
+ rm f-unix-orig f-unix-as-crlf &&
+ rm f-win-orig f-win-as-lf &&
+ p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i &&
+ p4 sync -f
+ )
+'
+
+test_expect_success 'utf-16 file create' '
+ (
+ cd "$cli" &&
+
+ # p4 saves this verbatim
+ printf "three\nline\ntext\n" >f-ascii &&
+ p4 add -t text f-ascii &&
+
+ # p4 adds \377\376 header
+ cp f-ascii f-ascii-as-utf16 &&
+ p4 add -t utf16 f-ascii-as-utf16 &&
+
+ # p4 saves this exactly as iconv produced it
+ printf "three\nline\ntext\n" | iconv -f ascii -t utf-16 >f-utf16 &&
+ p4 add -t utf16 f-utf16 &&
+
+ # this also is unchanged
+ cp f-utf16 f-utf16-as-text &&
+ p4 add -t text f-utf16-as-text &&
+
+ p4 submit -d "f files" &&
+
+ # force update of client files
+ p4 sync -f
+ )
+'
+
+test_expect_success 'utf-16 file test' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+
+ test_cmp "$cli/f-ascii" f-ascii &&
+ test_cmp "$cli/f-ascii-as-utf16" f-ascii-as-utf16 &&
+ test_cmp "$cli/f-utf16" f-utf16 &&
+ test_cmp "$cli/f-utf16-as-text" f-utf16-as-text
+ )
+'
+
+test_expect_success 'keyword file create' '
+ (
+ cd "$cli" &&
+
+ printf "id\n\$Id\$\n\$Author\$\ntext\n" >k-text-k &&
+ p4 add -t text+k k-text-k &&
+
+ cp k-text-k k-text-ko &&
+ p4 add -t text+ko k-text-ko &&
+
+ cat k-text-k | iconv -f ascii -t utf-16 >k-utf16-k &&
+ p4 add -t utf16+k k-utf16-k &&
+
+ cp k-utf16-k k-utf16-ko &&
+ p4 add -t utf16+ko k-utf16-ko &&
+
+ p4 submit -d "k files" &&
+ p4 sync -f
+ )
+'
+
+build_smush() {
+ cat >k_smush.py <<-\EOF &&
+ import re, sys
+ sys.stdout.write(re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$', r'$\1$', sys.stdin.read()))
+ EOF
+ cat >ko_smush.py <<-\EOF
+ import re, sys
+ sys.stdout.write(re.sub(r'(?i)\$(Id|Header):[^$]*\$', r'$\1$', sys.stdin.read()))
+ EOF
+}
+
+test_expect_success 'keyword file test' '
+ build_smush &&
+ test_when_finished rm -f k_smush.py ko_smush.py &&
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+
+ # text, ensure unexpanded
+ "$PYTHON_PATH" "$TRASH_DIRECTORY/k_smush.py" <"$cli/k-text-k" >cli-k-text-k-smush &&
+ test_cmp cli-k-text-k-smush k-text-k &&
+ "$PYTHON_PATH" "$TRASH_DIRECTORY/ko_smush.py" <"$cli/k-text-ko" >cli-k-text-ko-smush &&
+ test_cmp cli-k-text-ko-smush k-text-ko &&
+
+ # utf16, even though p4 expands keywords, git p4 does not
+ # try to undo that
+ test_cmp "$cli/k-utf16-k" k-utf16-k &&
+ test_cmp "$cli/k-utf16-ko" k-utf16-ko
+ )
+'
+
+build_gendouble() {
+ cat >gendouble.py <<-\EOF
+ import sys
+ import struct
+
+ s = struct.pack(b">LL18s",
+ 0x00051607, # AppleDouble
+ 0x00020000, # version 2
+ b"" # pad to 26 bytes
+ )
+ getattr(sys.stdout, 'buffer', sys.stdout).write(s)
+ EOF
+}
+
+test_expect_success 'ignore apple' '
+ test_when_finished rm -f gendouble.py &&
+ build_gendouble &&
+ (
+ cd "$cli" &&
+ test-genrandom apple 1024 >double.png &&
+ "$PYTHON_PATH" "$TRASH_DIRECTORY/gendouble.py" >%double.png &&
+ p4 add -t apple double.png &&
+ p4 submit -d appledouble
+ ) &&
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ test ! -f double.png
+ )
+'
+
+test_expect_success SYMLINKS 'create p4 symlink' '
+ cd "$cli" &&
+ ln -s symlink-target symlink &&
+ p4 add symlink &&
+ p4 submit -d "add symlink"
+'
+
+test_expect_success SYMLINKS 'ensure p4 symlink parsed correctly' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ test -L symlink &&
+ test $(readlink symlink) = symlink-target
+ )
+'
+
+test_expect_success SYMLINKS 'empty symlink target' '
+ (
+ # first create the file as a file
+ cd "$cli" &&
+ >empty-symlink &&
+ p4 add empty-symlink &&
+ p4 submit -d "add empty-symlink as a file"
+ ) &&
+ (
+ # now change it to be a symlink to "target1"
+ cd "$cli" &&
+ p4 edit empty-symlink &&
+ p4 reopen -t symlink empty-symlink &&
+ rm empty-symlink &&
+ ln -s target1 empty-symlink &&
+ p4 add empty-symlink &&
+ p4 submit -d "make empty-symlink point to target1"
+ ) &&
+ (
+ # Hack the p4 depot to make the symlink point to nothing;
+ # this should not happen in reality, but shows up
+ # in p4 repos in the wild.
+ #
+ # The sed expression changes this:
+ # @@
+ # text
+ # @target1
+ # @
+ # to this:
+ # @@
+ # text
+ # @@
+ #
+ cd "$db/depot" &&
+ sed "/@target1/{; s/target1/@/; n; d; }" \
+ empty-symlink,v >empty-symlink,v.tmp &&
+ mv empty-symlink,v.tmp empty-symlink,v
+ ) &&
+ (
+ # Make sure symlink really is empty. Asking
+ # p4 to sync here will make it generate errors.
+ cd "$cli" &&
+ p4 print -q //depot/empty-symlink#2 >out &&
+ test ! -s out
+ ) &&
+ test_when_finished cleanup_git &&
+
+ # make sure git p4 handles it without error
+ git p4 clone --dest="$git" //depot@all &&
+
+ # fix the symlink, make it point to "target2"
+ (
+ cd "$cli" &&
+ p4 open empty-symlink &&
+ rm empty-symlink &&
+ ln -s target2 empty-symlink &&
+ p4 submit -d "make empty-symlink point to target2"
+ ) &&
+ cleanup_git &&
+ git p4 clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ test $(readlink empty-symlink) = target2
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9803-git-p4-shell-metachars.sh b/t/t9803-git-p4-shell-metachars.sh
new file mode 100755
index 0000000..d950c7d
--- /dev/null
+++ b/t/t9803-git-p4-shell-metachars.sh
@@ -0,0 +1,112 @@
+#!/bin/sh
+
+test_description='git p4 transparency to shell metachars in filenames'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+ echo file1 >file1 &&
+ p4 add file1 &&
+ p4 submit -d "file1"
+ )
+'
+
+test_expect_success 'shell metachars in filenames' '
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEditCheck true &&
+ echo f1 >foo\$bar &&
+ git add foo\$bar &&
+ echo f2 >"file with spaces" &&
+ git add "file with spaces" &&
+ git commit -m "add files" &&
+ P4EDITOR="test-chmtime +5" git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ p4 sync ... &&
+ test -e "file with spaces" &&
+ test -e "foo\$bar"
+ )
+'
+
+test_expect_success 'deleting with shell metachars' '
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEditCheck true &&
+ git rm foo\$bar &&
+ git rm file\ with\ spaces &&
+ git commit -m "remove files" &&
+ P4EDITOR="test-chmtime +5" git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ p4 sync ... &&
+ test ! -e "file with spaces" &&
+ test ! -e foo\$bar
+ )
+'
+
+# Create a branch with a shell metachar in its name
+#
+# 1. //depot/main
+# 2. //depot/branch$3
+
+test_expect_success 'branch with shell char' '
+ test_when_finished cleanup_git &&
+ test_create_repo "$git" &&
+ (
+ cd "$cli" &&
+
+ mkdir -p main &&
+
+ echo f1 >main/f1 &&
+ p4 add main/f1 &&
+ p4 submit -d "main/f1" &&
+
+ p4 integrate //depot/main/... //depot/branch\$3/... &&
+ p4 submit -d "integrate main to branch\$3" &&
+
+ echo f1 >branch\$3/shell_char_branch_file &&
+ p4 add branch\$3/shell_char_branch_file &&
+ p4 submit -d "branch\$3/shell_char_branch_file" &&
+
+ p4 branch -i <<-EOF &&
+ Branch: branch\$3
+ View: //depot/main/... //depot/branch\$3/...
+ EOF
+
+ p4 edit main/f1 &&
+ echo "a change" >> main/f1 &&
+ p4 submit -d "a change" main/f1 &&
+
+ p4 integrate -b branch\$3 &&
+ p4 resolve -am branch\$3/... &&
+ p4 submit -d "integrate main to branch\$3" &&
+
+ cd "$git" &&
+
+ git config git-p4.branchList main:branch\$3 &&
+ git p4 clone --dest=. --detect-branches //depot@all &&
+ git log --all --graph --decorate --stat &&
+ git reset --hard p4/depot/branch\$3 &&
+ test -f shell_char_branch_file &&
+ test -f f1
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9804-git-p4-label.sh b/t/t9804-git-p4-label.sh
new file mode 100755
index 0000000..e30f80e
--- /dev/null
+++ b/t/t9804-git-p4-label.sh
@@ -0,0 +1,115 @@
+#!/bin/sh
+
+test_description='git p4 label tests'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+# Basic p4 label tests.
+#
+# Note: can't have more than one label per commit - others
+# are silently discarded.
+#
+test_expect_success 'basic p4 labels' '
+ test_when_finished cleanup_git &&
+ (
+ cd "$cli" &&
+ mkdir -p main &&
+
+ echo f1 >main/f1 &&
+ p4 add main/f1 &&
+ p4 submit -d "main/f1" &&
+
+ echo f2 >main/f2 &&
+ p4 add main/f2 &&
+ p4 submit -d "main/f2" &&
+
+ echo f3 >main/file_with_\$metachar &&
+ p4 add main/file_with_\$metachar &&
+ p4 submit -d "file with metachar" &&
+
+ p4 tag -l tag_f1_only main/f1 &&
+ p4 tag -l tag_with\$_shell_char main/... &&
+
+ echo f4 >main/f4 &&
+ p4 add main/f4 &&
+ p4 submit -d "main/f4" &&
+
+ p4 label -i <<-EOF &&
+ Label: long_label
+ Description:
+ A Label first line
+ A Label second line
+ View: //depot/...
+ EOF
+
+ p4 tag -l long_label ... &&
+
+ p4 labels ... &&
+
+ git p4 clone --dest="$git" --detect-labels //depot@all &&
+ cd "$git" &&
+
+ git tag &&
+ git tag >taglist &&
+ test_line_count = 3 taglist &&
+
+ cd main &&
+ git checkout tag_tag_f1_only &&
+ ! test -f f2 &&
+ git checkout tag_tag_with\$_shell_char &&
+ test -f f1 && test -f f2 && test -f file_with_\$metachar &&
+
+ git show tag_long_label | grep -q "A Label second line"
+ )
+'
+
+# Test some label corner cases:
+#
+# - two tags on the same file; both should be available
+# - a tag that is only on one file; this kind of tag
+# cannot be imported (at least not easily).
+
+test_expect_failure 'two labels on the same changelist' '
+ test_when_finished cleanup_git &&
+ (
+ cd "$cli" &&
+ mkdir -p main &&
+
+ p4 edit main/f1 main/f2 &&
+ echo "hello world" >main/f1 &&
+ echo "not in the tag" >main/f2 &&
+ p4 submit -d "main/f[12]: testing two labels" &&
+
+ p4 tag -l tag_f1_1 main/... &&
+ p4 tag -l tag_f1_2 main/... &&
+
+ p4 labels ... &&
+
+ git p4 clone --dest="$git" --detect-labels //depot@all &&
+ cd "$git" &&
+
+ git tag | grep tag_f1 &&
+ git tag | grep -q tag_f1_1 &&
+ git tag | grep -q tag_f1_2 &&
+
+ cd main &&
+
+ git checkout tag_tag_f1_1 &&
+ ls &&
+ test -f f1 &&
+
+ git checkout tag_tag_f1_2 &&
+ ls &&
+ test -f f1
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9805-git-p4-skip-submit-edit.sh b/t/t9805-git-p4-skip-submit-edit.sh
new file mode 100755
index 0000000..5fbf904
--- /dev/null
+++ b/t/t9805-git-p4-skip-submit-edit.sh
@@ -0,0 +1,105 @@
+#!/bin/sh
+
+test_description='git p4 skipSubmitEdit config variables'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+ echo file1 >file1 &&
+ p4 add file1 &&
+ p4 submit -d "change 1"
+ )
+'
+
+# this works because P4EDITOR is set to true
+test_expect_success 'no config, unedited, say yes' '
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ echo line >>file1 &&
+ git commit -a -m "change 2" &&
+ echo y | git p4 submit &&
+ p4 changes //depot/... >wc &&
+ test_line_count = 2 wc
+ )
+'
+
+test_expect_success 'no config, unedited, say no' '
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ echo line >>file1 &&
+ git commit -a -m "change 3 (not really)" &&
+ printf "bad response\nn\n" | test_expect_code 1 git p4 submit &&
+ p4 changes //depot/... >wc &&
+ test_line_count = 2 wc
+ )
+'
+
+test_expect_success 'skipSubmitEdit' '
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ # will fail if editor is even invoked
+ git config core.editor /bin/false &&
+ echo line >>file1 &&
+ git commit -a -m "change 3" &&
+ git p4 submit &&
+ p4 changes //depot/... >wc &&
+ test_line_count = 3 wc
+ )
+'
+
+test_expect_success 'skipSubmitEditCheck' '
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEditCheck true &&
+ echo line >>file1 &&
+ git commit -a -m "change 4" &&
+ git p4 submit &&
+ p4 changes //depot/... >wc &&
+ test_line_count = 4 wc
+ )
+'
+
+# check the normal case, where the template really is edited
+test_expect_success 'no config, edited' '
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ test_when_finished "rm ed.sh" &&
+ cat >ed.sh <<-EOF &&
+ #!$SHELL_PATH
+ sleep 1
+ touch "\$1"
+ exit 0
+ EOF
+ chmod 755 ed.sh &&
+ (
+ cd "$git" &&
+ echo line >>file1 &&
+ git commit -a -m "change 5" &&
+ P4EDITOR="\"$TRASH_DIRECTORY/ed.sh\"" &&
+ export P4EDITOR &&
+ git p4 submit &&
+ p4 changes //depot/... >wc &&
+ test_line_count = 5 wc
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh
new file mode 100755
index 0000000..254d428
--- /dev/null
+++ b/t/t9806-git-p4-options.sh
@@ -0,0 +1,276 @@
+#!/bin/sh
+
+test_description='git p4 options'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+ echo file1 >file1 &&
+ p4 add file1 &&
+ p4 submit -d "change 1" &&
+ echo file2 >file2 &&
+ p4 add file2 &&
+ p4 submit -d "change 2" &&
+ echo file3 >file3 &&
+ p4 add file3 &&
+ p4 submit -d "change 3"
+ )
+'
+
+test_expect_success 'clone no --git-dir' '
+ test_must_fail git p4 clone --git-dir=xx //depot
+'
+
+test_expect_success 'clone --branch should checkout master' '
+ git p4 clone --branch=refs/remotes/p4/sb --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git rev-parse refs/remotes/p4/sb >sb &&
+ git rev-parse refs/heads/master >master &&
+ test_cmp sb master &&
+ git rev-parse HEAD >head &&
+ test_cmp sb head
+ )
+'
+
+test_expect_success 'sync when no master branch prints a nice error' '
+ test_when_finished cleanup_git &&
+ git p4 clone --branch=refs/remotes/p4/sb --dest="$git" //depot@2 &&
+ (
+ cd "$git" &&
+ test_must_fail git p4 sync 2>err &&
+ grep "Error: no branch refs/remotes/p4/master" err
+ )
+'
+
+test_expect_success 'sync --branch builds the full ref name correctly' '
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init &&
+
+ git p4 sync --branch=b1 //depot &&
+ git rev-parse --verify refs/remotes/p4/b1 &&
+ git p4 sync --branch=p4/b2 //depot &&
+ git rev-parse --verify refs/remotes/p4/b2 &&
+
+ git p4 sync --import-local --branch=h1 //depot &&
+ git rev-parse --verify refs/heads/p4/h1 &&
+ git p4 sync --import-local --branch=p4/h2 //depot &&
+ git rev-parse --verify refs/heads/p4/h2 &&
+
+ git p4 sync --branch=refs/stuff //depot &&
+ git rev-parse --verify refs/stuff
+ )
+'
+
+# engages --detect-branches code, which will do filename filtering so
+# no sync to either b1 or b2
+test_expect_success 'sync when two branches but no master should noop' '
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init &&
+ git p4 sync --branch=refs/remotes/p4/b1 //depot@2 &&
+ git p4 sync --branch=refs/remotes/p4/b2 //depot@2 &&
+ git p4 sync &&
+ git show -s --format=%s refs/remotes/p4/b1 >show &&
+ grep "Initial import" show &&
+ git show -s --format=%s refs/remotes/p4/b2 >show &&
+ grep "Initial import" show
+ )
+'
+
+test_expect_success 'sync --branch updates specific branch, no detection' '
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init &&
+ git p4 sync --branch=b1 //depot@2 &&
+ git p4 sync --branch=b2 //depot@2 &&
+ git p4 sync --branch=b2 &&
+ git show -s --format=%s refs/remotes/p4/b1 >show &&
+ grep "Initial import" show &&
+ git show -s --format=%s refs/remotes/p4/b2 >show &&
+ grep "change 3" show
+ )
+'
+
+# allows using the refname "p4" as a short name for p4/master
+test_expect_success 'clone creates HEAD symbolic reference' '
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git rev-parse --verify refs/remotes/p4/master >master &&
+ git rev-parse --verify p4 >p4 &&
+ test_cmp master p4
+ )
+'
+
+test_expect_success 'clone --branch creates HEAD symbolic reference' '
+ git p4 clone --branch=refs/remotes/p4/sb --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git rev-parse --verify refs/remotes/p4/sb >sb &&
+ git rev-parse --verify p4 >p4 &&
+ test_cmp sb p4
+ )
+'
+
+test_expect_success 'clone --changesfile' '
+ test_when_finished "rm cf" &&
+ printf "1\n3\n" >cf &&
+ git p4 clone --changesfile="$TRASH_DIRECTORY/cf" --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git log --oneline p4/master >lines &&
+ test_line_count = 2 lines
+ test_path_is_file file1 &&
+ test_path_is_missing file2 &&
+ test_path_is_file file3
+ )
+'
+
+test_expect_success 'clone --changesfile, @all' '
+ test_when_finished "rm cf" &&
+ printf "1\n3\n" >cf &&
+ test_must_fail git p4 clone --changesfile="$TRASH_DIRECTORY/cf" --dest="$git" //depot@all
+'
+
+# imports both master and p4/master in refs/heads
+# requires --import-local on sync to find p4 refs/heads
+# does not update master on sync, just p4/master
+test_expect_success 'clone/sync --import-local' '
+ git p4 clone --import-local --dest="$git" //depot@1,2 &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git log --oneline refs/heads/master >lines &&
+ test_line_count = 2 lines &&
+ git log --oneline refs/heads/p4/master >lines &&
+ test_line_count = 2 lines &&
+ test_must_fail git p4 sync &&
+
+ git p4 sync --import-local &&
+ git log --oneline refs/heads/master >lines &&
+ test_line_count = 2 lines &&
+ git log --oneline refs/heads/p4/master >lines &&
+ test_line_count = 3 lines
+ )
+'
+
+test_expect_success 'clone --max-changes' '
+ git p4 clone --dest="$git" --max-changes 2 //depot@all &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git log --oneline refs/heads/master >lines &&
+ test_line_count = 2 lines
+ )
+'
+
+test_expect_success 'clone --keep-path' '
+ (
+ cd "$cli" &&
+ mkdir -p sub/dir &&
+ echo f4 >sub/dir/f4 &&
+ p4 add sub/dir/f4 &&
+ p4 submit -d "change 4"
+ ) &&
+ git p4 clone --dest="$git" --keep-path //depot/sub/dir@all &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ test_path_is_missing f4 &&
+ test_path_is_file sub/dir/f4
+ ) &&
+ cleanup_git &&
+ git p4 clone --dest="$git" //depot/sub/dir@all &&
+ (
+ cd "$git" &&
+ test_path_is_file f4 &&
+ test_path_is_missing sub/dir/f4
+ )
+'
+
+# clone --use-client-spec must still specify a depot path
+# if given, it should rearrange files according to client spec
+# when it has view lines that match the depot path
+# XXX: should clone/sync just use the client spec exactly, rather
+# than needing depot paths?
+test_expect_success 'clone --use-client-spec' '
+ (
+ # big usage message
+ exec >/dev/null &&
+ test_must_fail git p4 clone --dest="$git" --use-client-spec
+ ) &&
+ # build a different client
+ cli2="$TRASH_DIRECTORY/cli2" &&
+ mkdir -p "$cli2" &&
+ test_when_finished "rmdir \"$cli2\"" &&
+ test_when_finished cleanup_git &&
+ (
+ # group P4CLIENT and cli changes in a sub-shell
+ P4CLIENT=client2 &&
+ cli="$cli2" &&
+ client_view "//depot/sub/... //client2/bus/..." &&
+ git p4 clone --dest="$git" --use-client-spec //depot/... &&
+ (
+ cd "$git" &&
+ test_path_is_file bus/dir/f4 &&
+ test_path_is_missing file1
+ ) &&
+ cleanup_git &&
+ # same thing again, this time with variable instead of option
+ (
+ cd "$git" &&
+ git init &&
+ git config git-p4.useClientSpec true &&
+ git p4 sync //depot/... &&
+ git checkout -b master p4/master &&
+ test_path_is_file bus/dir/f4 &&
+ test_path_is_missing file1
+ )
+ )
+'
+
+test_expect_success 'submit works with no p4/master' '
+ test_when_finished cleanup_git &&
+ git p4 clone --branch=b1 //depot@1,2 --destination="$git" &&
+ (
+ cd "$git" &&
+ test_commit submit-1-branch &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit --branch=b1
+ )
+'
+
+# The sync/rebase part post-submit will engage detect-branches
+# machinery which will not do anything in this particular test.
+test_expect_success 'submit works with two branches' '
+ test_when_finished cleanup_git &&
+ git p4 clone --branch=b1 //depot@1,2 --destination="$git" &&
+ (
+ cd "$git" &&
+ git p4 sync --branch=b2 //depot@1,3 &&
+ test_commit submit-2-branches &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh
new file mode 100755
index 0000000..5931528
--- /dev/null
+++ b/t/t9807-git-p4-submit.sh
@@ -0,0 +1,420 @@
+#!/bin/sh
+
+test_description='git p4 submit'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+ echo file1 >file1 &&
+ p4 add file1 &&
+ p4 submit -d "change 1"
+ )
+'
+
+test_expect_success 'is_cli_file_writeable function' '
+ (
+ cd "$cli" &&
+ echo a >a &&
+ is_cli_file_writeable a &&
+ ! is_cli_file_writeable file1 &&
+ rm a
+ )
+'
+
+test_expect_success 'submit with no client dir' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo file2 >file2 &&
+ git add file2 &&
+ git commit -m "git commit 2" &&
+ rm -rf "$cli" &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_file file1 &&
+ test_path_is_file file2
+ )
+'
+
+# make two commits, but tell it to apply only from HEAD^
+test_expect_success 'submit --origin' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ test_commit "file3" &&
+ test_commit "file4" &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit --origin=HEAD^
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_missing "file3.t" &&
+ test_path_is_file "file4.t"
+ )
+'
+
+test_expect_success 'submit --dry-run' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ test_commit "dry-run1" &&
+ test_commit "dry-run2" &&
+ git p4 submit --dry-run >out &&
+ test_i18ngrep "Would apply" out
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_missing "dry-run1.t" &&
+ test_path_is_missing "dry-run2.t"
+ )
+'
+
+test_expect_success 'submit --dry-run --export-labels' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo dry-run1 >dry-run1 &&
+ git add dry-run1 &&
+ git commit -m "dry-run1" dry-run1 &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit &&
+ echo dry-run2 >dry-run2 &&
+ git add dry-run2 &&
+ git commit -m "dry-run2" dry-run2 &&
+ git tag -m "dry-run-tag1" dry-run-tag1 HEAD^ &&
+ git p4 submit --dry-run --export-labels >out &&
+ test_i18ngrep "Would create p4 label" out
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_file "dry-run1" &&
+ test_path_is_missing "dry-run2"
+ )
+'
+
+test_expect_success 'submit with allowSubmit' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ test_commit "file5" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.allowSubmit "nobranch" &&
+ test_must_fail git p4 submit &&
+ git config git-p4.allowSubmit "nobranch,master" &&
+ git p4 submit
+ )
+'
+
+test_expect_success 'submit with master branch name from argv' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ test_commit "file6" &&
+ git config git-p4.skipSubmitEdit true &&
+ test_must_fail git p4 submit nobranch &&
+ git branch otherbranch &&
+ git reset --hard HEAD^ &&
+ test_commit "file7" &&
+ git p4 submit otherbranch
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_file "file6.t" &&
+ test_path_is_missing "file7.t"
+ )
+'
+
+#
+# Basic submit tests, the five handled cases
+#
+
+test_expect_success 'submit modify' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ echo line >>file1 &&
+ git add file1 &&
+ git commit -m file1 &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_file file1 &&
+ test_line_count = 2 file1
+ )
+'
+
+test_expect_success 'submit add' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ echo file13 >file13 &&
+ git add file13 &&
+ git commit -m file13 &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_file file13
+ )
+'
+
+test_expect_success 'submit delete' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ git rm file4.t &&
+ git commit -m "delete file4.t" &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_missing file4.t
+ )
+'
+
+test_expect_success 'submit copy' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.detectCopies true &&
+ git config git-p4.detectCopiesHarder true &&
+ cp file5.t file5.ta &&
+ git add file5.ta &&
+ git commit -m "copy to file5.ta" &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_file file5.ta &&
+ ! is_cli_file_writeable file5.ta
+ )
+'
+
+test_expect_success 'submit rename' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.detectRenames true &&
+ git mv file6.t file6.ta &&
+ git commit -m "rename file6.t to file6.ta" &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_missing file6.t &&
+ test_path_is_file file6.ta &&
+ ! is_cli_file_writeable file6.ta
+ )
+'
+
+#
+# Converting git commit message to p4 change description, including
+# parsing out the optional Jobs: line.
+#
+test_expect_success 'simple one-line description' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo desc2 >desc2 &&
+ git add desc2 &&
+ cat >msg <<-EOF &&
+ One-line description line for desc2.
+ EOF
+ git commit -F - <msg &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit &&
+ change=$(p4 -G changes -m 1 //depot/... | \
+ marshal_dump change) &&
+ # marshal_dump always adds a newline
+ p4 -G describe $change | marshal_dump desc | sed \$d >pmsg &&
+ test_cmp msg pmsg
+ )
+'
+
+test_expect_success 'description with odd formatting' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo desc3 >desc3 &&
+ git add desc3 &&
+ (
+ printf "subject line\n\n\tExtra tab\nline.\n\n" &&
+ printf "Description:\n\tBogus description marker\n\n" &&
+ # git commit eats trailing newlines; only use one
+ printf "Files:\n\tBogus descs marker\n"
+ ) >msg &&
+ git commit -F - <msg &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit &&
+ change=$(p4 -G changes -m 1 //depot/... | \
+ marshal_dump change) &&
+ # marshal_dump always adds a newline
+ p4 -G describe $change | marshal_dump desc | sed \$d >pmsg &&
+ test_cmp msg pmsg
+ )
+'
+
+make_job() {
+ name="$1" &&
+ tab="$(printf \\t)" &&
+ p4 job -o | \
+ sed -e "/^Job:/s/.*/Job: $name/" \
+ -e "/^Description/{ n; s/.*/$tab job text/; }" | \
+ p4 job -i
+}
+
+test_expect_success 'description with Jobs section at end' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo desc4 >desc4 &&
+ git add desc4 &&
+ echo 6060842 >jobname &&
+ (
+ printf "subject line\n\n\tExtra tab\nline.\n\n" &&
+ printf "Files:\n\tBogus files marker\n" &&
+ printf "Junk: 3164175\n" &&
+ printf "Jobs: $(cat jobname)\n"
+ ) >msg &&
+ git commit -F - <msg &&
+ git config git-p4.skipSubmitEdit true &&
+ # build a job
+ make_job $(cat jobname) &&
+ git p4 submit &&
+ change=$(p4 -G changes -m 1 //depot/... | \
+ marshal_dump change) &&
+ # marshal_dump always adds a newline
+ p4 -G describe $change | marshal_dump desc | sed \$d >pmsg &&
+ # make sure Jobs line and all following is gone
+ sed "/^Jobs:/,\$d" msg >jmsg &&
+ test_cmp jmsg pmsg &&
+ # make sure p4 knows about job
+ p4 -G describe $change | marshal_dump job0 >job0 &&
+ test_cmp jobname job0
+ )
+'
+
+test_expect_success 'description with Jobs and values on separate lines' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo desc5 >desc5 &&
+ git add desc5 &&
+ echo PROJ-6060842 >jobname1 &&
+ echo PROJ-6060847 >jobname2 &&
+ (
+ printf "subject line\n\n\tExtra tab\nline.\n\n" &&
+ printf "Files:\n\tBogus files marker\n" &&
+ printf "Junk: 3164175\n" &&
+ printf "Jobs:\n" &&
+ printf "\t$(cat jobname1)\n" &&
+ printf "\t$(cat jobname2)\n"
+ ) >msg &&
+ git commit -F - <msg &&
+ git config git-p4.skipSubmitEdit true &&
+ # build two jobs
+ make_job $(cat jobname1) &&
+ make_job $(cat jobname2) &&
+ git p4 submit &&
+ change=$(p4 -G changes -m 1 //depot/... | \
+ marshal_dump change) &&
+ # marshal_dump always adds a newline
+ p4 -G describe $change | marshal_dump desc | sed \$d >pmsg &&
+ # make sure Jobs line and all following is gone
+ sed "/^Jobs:/,\$d" msg >jmsg &&
+ test_cmp jmsg pmsg &&
+ # make sure p4 knows about the two jobs
+ p4 -G describe $change >change &&
+ (
+ marshal_dump job0 <change &&
+ marshal_dump job1 <change
+ ) | sort >jobs &&
+ cat jobname1 jobname2 | sort >expected &&
+ test_cmp expected jobs
+ )
+'
+
+test_expect_success 'description with Jobs section and bogus following text' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo desc6 >desc6 &&
+ git add desc6 &&
+ echo 6060843 >jobname &&
+ (
+ printf "subject line\n\n\tExtra tab\nline.\n\n" &&
+ printf "Files:\n\tBogus files marker\n" &&
+ printf "Junk: 3164175\n" &&
+ printf "Jobs: $(cat jobname)\n" &&
+ printf "MoreJunk: 3711\n"
+ ) >msg &&
+ git commit -F - <msg &&
+ git config git-p4.skipSubmitEdit true &&
+ # build a job
+ make_job $(cat jobname) &&
+ test_must_fail git p4 submit 2>err &&
+ test_i18ngrep "Unknown field name" err
+ ) &&
+ (
+ cd "$cli" &&
+ p4 revert desc6 &&
+ rm -f desc6
+ )
+'
+
+test_expect_success 'submit --prepare-p4-only' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo prep-only-add >prep-only-add &&
+ git add prep-only-add &&
+ git commit -m "prep only add" &&
+ git p4 submit --prepare-p4-only >out &&
+ test_i18ngrep "prepared for submission" out &&
+ test_i18ngrep "must be deleted" out &&
+ test_i18ngrep ! "everything below this line is just the diff" out
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_file prep-only-add &&
+ p4 fstat -T action prep-only-add | grep -w add
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9808-git-p4-chdir.sh b/t/t9808-git-p4-chdir.sh
new file mode 100755
index 0000000..11d2b51
--- /dev/null
+++ b/t/t9808-git-p4-chdir.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+test_description='git p4 relative chdir'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+ echo file1 >file1 &&
+ p4 add file1 &&
+ p4 submit -d "change 1"
+ )
+'
+
+# P4 reads from P4CONFIG file to find its server params, if the
+# environment variable is set
+test_expect_success 'P4CONFIG and absolute dir clone' '
+ printf "P4PORT=$P4PORT\nP4CLIENT=$P4CLIENT\n" >p4config &&
+ test_when_finished "rm p4config" &&
+ test_when_finished cleanup_git &&
+ (
+ P4CONFIG=p4config && export P4CONFIG &&
+ sane_unset P4PORT P4CLIENT &&
+ git p4 clone --verbose --dest="$git" //depot
+ )
+'
+
+# same thing, but with relative directory name, note missing $ on --dest
+test_expect_success 'P4CONFIG and relative dir clone' '
+ printf "P4PORT=$P4PORT\nP4CLIENT=$P4CLIENT\n" >p4config &&
+ test_when_finished "rm p4config" &&
+ test_when_finished cleanup_git &&
+ (
+ P4CONFIG=p4config && export P4CONFIG &&
+ sane_unset P4PORT P4CLIENT &&
+ git p4 clone --verbose --dest="git" //depot
+ )
+'
+
+# Common setup using .p4config to set P4CLIENT and P4PORT breaks
+# if clone destination is relative. Make sure that chdir() expands
+# the relative path in --dest to absolute.
+test_expect_success 'p4 client root would be relative due to clone --dest' '
+ test_when_finished cleanup_git &&
+ (
+ echo P4PORT=$P4PORT >git/.p4config &&
+ P4CONFIG=.p4config &&
+ export P4CONFIG &&
+ unset P4PORT &&
+ git p4 clone --dest="git" //depot
+ )
+'
+
+# When the p4 client Root is a symlink, make sure chdir() does not use
+# getcwd() to convert it to a physical path.
+test_expect_success SYMLINKS 'p4 client root symlink should stay symbolic' '
+ physical="$TRASH_DIRECTORY/physical" &&
+ symbolic="$TRASH_DIRECTORY/symbolic" &&
+ test_when_finished "rm -rf \"$physical\"" &&
+ test_when_finished "rm \"$symbolic\"" &&
+ mkdir -p "$physical" &&
+ ln -s "$physical" "$symbolic" &&
+ test_when_finished cleanup_git &&
+ (
+ P4CLIENT=client-sym &&
+ p4 client -i <<-EOF &&
+ Client: $P4CLIENT
+ Description: $P4CLIENT
+ Root: $symbolic
+ LineEnd: unix
+ View: //depot/... //$P4CLIENT/...
+ EOF
+ git p4 clone --dest="$git" //depot &&
+ cd "$git" &&
+ test_commit file2 &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9809-git-p4-client-view.sh b/t/t9809-git-p4-client-view.sh
new file mode 100755
index 0000000..897b3c3
--- /dev/null
+++ b/t/t9809-git-p4-client-view.sh
@@ -0,0 +1,843 @@
+#!/bin/sh
+
+test_description='git p4 client view'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+#
+# Verify these files exist, exactly. Caller creates
+# a list of files in file "files".
+#
+check_files_exist() {
+ ok=0 &&
+ num=$# &&
+ for arg ; do
+ test_path_is_file "$arg" &&
+ ok=$(($ok + 1))
+ done &&
+ test $ok -eq $num &&
+ test_line_count = $num files
+}
+
+#
+# Sync up the p4 client, make sure the given files (and only
+# those) exist.
+#
+client_verify() {
+ (
+ cd "$cli" &&
+ p4 sync &&
+ find . -type f ! -name files >files &&
+ check_files_exist "$@"
+ )
+}
+
+#
+# Make sure the named files, exactly, exist.
+#
+git_verify() {
+ (
+ cd "$git" &&
+ git ls-files >files &&
+ check_files_exist "$@"
+ )
+}
+
+# //depot
+# - dir1
+# - file11
+# - file12
+# - dir2
+# - file21
+# - file22
+init_depot() {
+ for d in 1 2 ; do
+ mkdir -p dir$d &&
+ for f in 1 2 ; do
+ echo dir$d/file$d$f >dir$d/file$d$f &&
+ p4 add dir$d/file$d$f &&
+ p4 submit -d "dir$d/file$d$f"
+ done
+ done &&
+ find . -type f ! -name files >files &&
+ check_files_exist dir1/file11 dir1/file12 \
+ dir2/file21 dir2/file22
+}
+
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+ init_depot
+ )
+'
+
+# double % for printf
+test_expect_success 'view wildcard %%n' '
+ client_view "//depot/%%%%1/sub/... //client/sub/%%%%1/..." &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot
+'
+
+test_expect_success 'view wildcard *' '
+ client_view "//depot/*/bar/... //client/*/bar/..." &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot
+'
+
+test_expect_success 'wildcard ... in the middle' '
+ client_view "//depot/.../file11 //client/.../file11" &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot
+'
+
+test_expect_success 'wildcard ... in the middle and at the end' '
+ client_view "//depot/.../a/... //client/.../a/..." &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot
+'
+
+test_expect_success 'basic map' '
+ client_view "//depot/dir1/... //client/cli1/..." &&
+ files="cli1/file11 cli1/file12" &&
+ client_verify $files &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify $files
+'
+
+test_expect_success 'client view with no mappings' '
+ client_view &&
+ client_verify &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify
+'
+
+test_expect_success 'single file map' '
+ client_view "//depot/dir1/file11 //client/file11" &&
+ files="file11" &&
+ client_verify $files &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify $files
+'
+
+test_expect_success 'later mapping takes precedence (entire repo)' '
+ client_view "//depot/dir1/... //client/cli1/..." \
+ "//depot/... //client/cli2/..." &&
+ files="cli2/dir1/file11 cli2/dir1/file12
+ cli2/dir2/file21 cli2/dir2/file22" &&
+ client_verify $files &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify $files
+'
+
+test_expect_success 'later mapping takes precedence (partial repo)' '
+ client_view "//depot/dir1/... //client/..." \
+ "//depot/dir2/... //client/..." &&
+ files="file21 file22" &&
+ client_verify $files &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify $files
+'
+
+# Reading the view backwards,
+# dir2 goes to cli12
+# dir1 cannot go to cli12 since it was filled by dir2
+# dir1 also does not go to cli3, since the second rule
+# noticed that it matched, but was already filled
+test_expect_success 'depot path matching rejected client path' '
+ client_view "//depot/dir1/... //client/cli3/..." \
+ "//depot/dir1/... //client/cli12/..." \
+ "//depot/dir2/... //client/cli12/..." &&
+ files="cli12/file21 cli12/file22" &&
+ client_verify $files &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify $files
+'
+
+# since both have the same //client/..., the exclusion
+# rule keeps everything out
+test_expect_success 'exclusion wildcard, client rhs same (odd)' '
+ client_view "//depot/... //client/..." \
+ "-//depot/dir2/... //client/..." &&
+ client_verify &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify
+'
+
+test_expect_success 'exclusion wildcard, client rhs different (normal)' '
+ client_view "//depot/... //client/..." \
+ "-//depot/dir2/... //client/dir2/..." &&
+ files="dir1/file11 dir1/file12" &&
+ client_verify $files &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify $files
+'
+
+test_expect_success 'exclusion single file' '
+ client_view "//depot/... //client/..." \
+ "-//depot/dir2/file22 //client/file22" &&
+ files="dir1/file11 dir1/file12 dir2/file21" &&
+ client_verify $files &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify $files
+'
+
+test_expect_success 'overlay wildcard' '
+ client_view "//depot/dir1/... //client/cli/..." \
+ "+//depot/dir2/... //client/cli/..." &&
+ files="cli/file11 cli/file12 cli/file21 cli/file22" &&
+ client_verify $files &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify $files
+'
+
+test_expect_success 'overlay single file' '
+ client_view "//depot/dir1/... //client/cli/..." \
+ "+//depot/dir2/file21 //client/cli/file21" &&
+ files="cli/file11 cli/file12 cli/file21" &&
+ client_verify $files &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify $files
+'
+
+test_expect_success 'exclusion with later inclusion' '
+ client_view "//depot/... //client/..." \
+ "-//depot/dir2/... //client/dir2/..." \
+ "//depot/dir2/... //client/dir2incl/..." &&
+ files="dir1/file11 dir1/file12 dir2incl/file21 dir2incl/file22" &&
+ client_verify $files &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify $files
+'
+
+test_expect_success 'quotes on rhs only' '
+ client_view "//depot/dir1/... \"//client/cdir 1/...\"" &&
+ client_verify "cdir 1/file11" "cdir 1/file12" &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify "cdir 1/file11" "cdir 1/file12"
+'
+
+#
+# Submit tests
+#
+
+# clone sets variable
+test_expect_success 'clone --use-client-spec sets useClientSpec' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git config --bool git-p4.useClientSpec >actual &&
+ echo true >true &&
+ test_cmp actual true
+ )
+'
+
+# clone just a subdir of the client spec
+test_expect_success 'subdir clone' '
+ client_view "//depot/... //client/..." &&
+ files="dir1/file11 dir1/file12 dir2/file21 dir2/file22" &&
+ client_verify $files &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
+ git_verify dir1/file11 dir1/file12
+'
+
+#
+# submit back, see what happens: five cases
+#
+test_expect_success 'subdir clone, submit modify' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ echo line >>dir1/file12 &&
+ git add dir1/file12 &&
+ git commit -m dir1/file12 &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_file dir1/file12 &&
+ test_line_count = 2 dir1/file12
+ )
+'
+
+test_expect_success 'subdir clone, submit add' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ echo file13 >dir1/file13 &&
+ git add dir1/file13 &&
+ git commit -m dir1/file13 &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_file dir1/file13
+ )
+'
+
+test_expect_success 'subdir clone, submit delete' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ git rm dir1/file12 &&
+ git commit -m "delete dir1/file12" &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_missing dir1/file12
+ )
+'
+
+test_expect_success 'subdir clone, submit copy' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.detectCopies true &&
+ cp dir1/file11 dir1/file11a &&
+ git add dir1/file11a &&
+ git commit -m "copy to dir1/file11a" &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_file dir1/file11a &&
+ ! is_cli_file_writeable dir1/file11a
+ )
+'
+
+test_expect_success 'subdir clone, submit rename' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.detectRenames true &&
+ git mv dir1/file13 dir1/file13a &&
+ git commit -m "rename dir1/file13 to dir1/file13a" &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_missing dir1/file13 &&
+ test_path_is_file dir1/file13a &&
+ ! is_cli_file_writeable dir1/file13a
+ )
+'
+
+# see t9800 for the non-client-spec case, and the rest of the wildcard tests
+test_expect_success 'wildcard files submit back to p4, client-spec case' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
+ (
+ cd "$git" &&
+ echo git-wild-hash >dir1/git-wild#hash &&
+ if test_have_prereq !MINGW,!CYGWIN
+ then
+ echo git-wild-star >dir1/git-wild\*star
+ fi &&
+ echo git-wild-at >dir1/git-wild@at &&
+ echo git-wild-percent >dir1/git-wild%percent &&
+ git add dir1/git-wild* &&
+ git commit -m "add some wildcard filenames" &&
+ git config git-p4.skipSubmitEditCheck true &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_file dir1/git-wild#hash &&
+ if test_have_prereq !MINGW,!CYGWIN
+ then
+ test_path_is_file dir1/git-wild\*star
+ fi &&
+ test_path_is_file dir1/git-wild@at &&
+ test_path_is_file dir1/git-wild%percent
+ ) &&
+ (
+ # delete these carefully, cannot just do "p4 delete"
+ # on files with wildcards; but git-p4 knows how
+ cd "$git" &&
+ git rm dir1/git-wild* &&
+ git commit -m "clean up the wildcards" &&
+ git p4 submit
+ )
+'
+
+test_expect_success 'reinit depot' '
+ (
+ cd "$cli" &&
+ rm files &&
+ p4 delete */* &&
+ p4 submit -d "delete all files" &&
+ init_depot
+ )
+'
+
+#
+# What happens when two files of the same name are overlayed together?
+# The last-listed file should take preference.
+#
+# //depot
+# - dir1
+# - file11
+# - file12
+# - filecollide
+# - dir2
+# - file21
+# - file22
+# - filecollide
+#
+test_expect_success 'overlay collision setup' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ echo dir1/filecollide >dir1/filecollide &&
+ p4 add dir1/filecollide &&
+ p4 submit -d dir1/filecollide &&
+ echo dir2/filecollide >dir2/filecollide &&
+ p4 add dir2/filecollide &&
+ p4 submit -d dir2/filecollide
+ )
+'
+
+test_expect_success 'overlay collision 1 to 2' '
+ client_view "//depot/dir1/... //client/..." \
+ "+//depot/dir2/... //client/..." &&
+ files="file11 file12 file21 file22 filecollide" &&
+ echo dir2/filecollide >actual &&
+ client_verify $files &&
+ test_cmp actual "$cli"/filecollide &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify $files &&
+ test_cmp actual "$git"/filecollide
+'
+
+test_expect_failure 'overlay collision 2 to 1' '
+ client_view "//depot/dir2/... //client/..." \
+ "+//depot/dir1/... //client/..." &&
+ files="file11 file12 file21 file22 filecollide" &&
+ echo dir1/filecollide >actual &&
+ client_verify $files &&
+ test_cmp actual "$cli"/filecollide &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify $files &&
+ test_cmp actual "$git"/filecollide
+'
+
+test_expect_success 'overlay collision delete 2' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ p4 delete dir2/filecollide &&
+ p4 submit -d "remove dir2/filecollide"
+ )
+'
+
+# no filecollide, got deleted with dir2
+test_expect_failure 'overlay collision 1 to 2, but 2 deleted' '
+ client_view "//depot/dir1/... //client/..." \
+ "+//depot/dir2/... //client/..." &&
+ files="file11 file12 file21 file22" &&
+ client_verify $files &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify $files
+'
+
+test_expect_success 'overlay collision update 1' '
+ client_view "//depot/dir1/... //client/dir1/..." &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ p4 open dir1/filecollide &&
+ echo dir1/filecollide update >dir1/filecollide &&
+ p4 submit -d "update dir1/filecollide"
+ )
+'
+
+# still no filecollide, dir2 still wins with the deletion even though the
+# change to dir1 is more recent
+test_expect_failure 'overlay collision 1 to 2, but 2 deleted, then 1 updated' '
+ client_view "//depot/dir1/... //client/..." \
+ "+//depot/dir2/... //client/..." &&
+ files="file11 file12 file21 file22" &&
+ client_verify $files &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify $files
+'
+
+test_expect_success 'overlay collision delete filecollides' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ p4 delete dir1/filecollide dir2/filecollide &&
+ p4 submit -d "remove filecollides"
+ )
+'
+
+#
+# Overlays as part of sync, rather than initial checkout:
+# 1. add a file in dir1
+# 2. sync to include it
+# 3. add same file in dir2
+# 4. sync, make sure content switches as dir2 has priority
+# 5. add another file in dir1
+# 6. sync
+# 7. add/delete same file in dir2
+# 8. sync, make sure it disappears, again dir2 wins
+# 9. cleanup
+#
+# //depot
+# - dir1
+# - file11
+# - file12
+# - colA
+# - colB
+# - dir2
+# - file21
+# - file22
+# - colA
+# - colB
+#
+test_expect_success 'overlay sync: add colA in dir1' '
+ client_view "//depot/dir1/... //client/dir1/..." &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ echo dir1/colA >dir1/colA &&
+ p4 add dir1/colA &&
+ p4 submit -d dir1/colA
+ )
+'
+
+test_expect_success 'overlay sync: initial git checkout' '
+ client_view "//depot/dir1/... //client/..." \
+ "+//depot/dir2/... //client/..." &&
+ files="file11 file12 file21 file22 colA" &&
+ echo dir1/colA >actual &&
+ client_verify $files &&
+ test_cmp actual "$cli"/colA &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify $files &&
+ test_cmp actual "$git"/colA
+'
+
+test_expect_success 'overlay sync: add colA in dir2' '
+ client_view "//depot/dir2/... //client/dir2/..." &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ echo dir2/colA >dir2/colA &&
+ p4 add dir2/colA &&
+ p4 submit -d dir2/colA
+ )
+'
+
+test_expect_success 'overlay sync: colA content switch' '
+ client_view "//depot/dir1/... //client/..." \
+ "+//depot/dir2/... //client/..." &&
+ files="file11 file12 file21 file22 colA" &&
+ echo dir2/colA >actual &&
+ client_verify $files &&
+ test_cmp actual "$cli"/colA &&
+ (
+ cd "$git" &&
+ git p4 sync --use-client-spec &&
+ git merge --ff-only p4/master
+ ) &&
+ git_verify $files &&
+ test_cmp actual "$git"/colA
+'
+
+test_expect_success 'overlay sync: add colB in dir1' '
+ client_view "//depot/dir1/... //client/dir1/..." &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ echo dir1/colB >dir1/colB &&
+ p4 add dir1/colB &&
+ p4 submit -d dir1/colB
+ )
+'
+
+test_expect_success 'overlay sync: colB appears' '
+ client_view "//depot/dir1/... //client/..." \
+ "+//depot/dir2/... //client/..." &&
+ files="file11 file12 file21 file22 colA colB" &&
+ echo dir1/colB >actual &&
+ client_verify $files &&
+ test_cmp actual "$cli"/colB &&
+ (
+ cd "$git" &&
+ git p4 sync --use-client-spec &&
+ git merge --ff-only p4/master
+ ) &&
+ git_verify $files &&
+ test_cmp actual "$git"/colB
+'
+
+test_expect_success 'overlay sync: add/delete colB in dir2' '
+ client_view "//depot/dir2/... //client/dir2/..." &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ echo dir2/colB >dir2/colB &&
+ p4 add dir2/colB &&
+ p4 submit -d dir2/colB &&
+ p4 delete dir2/colB &&
+ p4 submit -d "delete dir2/colB"
+ )
+'
+
+test_expect_success 'overlay sync: colB disappears' '
+ client_view "//depot/dir1/... //client/..." \
+ "+//depot/dir2/... //client/..." &&
+ files="file11 file12 file21 file22 colA" &&
+ client_verify $files &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git p4 sync --use-client-spec &&
+ git merge --ff-only p4/master
+ ) &&
+ git_verify $files
+'
+
+test_expect_success 'overlay sync: cleanup' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ p4 delete dir1/colA dir2/colA dir1/colB &&
+ p4 submit -d "remove overlay sync files"
+ )
+'
+
+#
+# Overlay tests again, but swapped so dir1 has priority.
+# 1. add a file in dir1
+# 2. sync to include it
+# 3. add same file in dir2
+# 4. sync, make sure content does not switch
+# 5. add another file in dir1
+# 6. sync
+# 7. add/delete same file in dir2
+# 8. sync, make sure it is still there
+# 9. cleanup
+#
+# //depot
+# - dir1
+# - file11
+# - file12
+# - colA
+# - colB
+# - dir2
+# - file21
+# - file22
+# - colA
+# - colB
+#
+test_expect_success 'overlay sync swap: add colA in dir1' '
+ client_view "//depot/dir1/... //client/dir1/..." &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ echo dir1/colA >dir1/colA &&
+ p4 add dir1/colA &&
+ p4 submit -d dir1/colA
+ )
+'
+
+test_expect_success 'overlay sync swap: initial git checkout' '
+ client_view "//depot/dir2/... //client/..." \
+ "+//depot/dir1/... //client/..." &&
+ files="file11 file12 file21 file22 colA" &&
+ echo dir1/colA >actual &&
+ client_verify $files &&
+ test_cmp actual "$cli"/colA &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify $files &&
+ test_cmp actual "$git"/colA
+'
+
+test_expect_success 'overlay sync swap: add colA in dir2' '
+ client_view "//depot/dir2/... //client/dir2/..." &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ echo dir2/colA >dir2/colA &&
+ p4 add dir2/colA &&
+ p4 submit -d dir2/colA
+ )
+'
+
+test_expect_failure 'overlay sync swap: colA no content switch' '
+ client_view "//depot/dir2/... //client/..." \
+ "+//depot/dir1/... //client/..." &&
+ files="file11 file12 file21 file22 colA" &&
+ echo dir1/colA >actual &&
+ client_verify $files &&
+ test_cmp actual "$cli"/colA &&
+ (
+ cd "$git" &&
+ git p4 sync --use-client-spec &&
+ git merge --ff-only p4/master
+ ) &&
+ git_verify $files &&
+ test_cmp actual "$git"/colA
+'
+
+test_expect_success 'overlay sync swap: add colB in dir1' '
+ client_view "//depot/dir1/... //client/dir1/..." &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ echo dir1/colB >dir1/colB &&
+ p4 add dir1/colB &&
+ p4 submit -d dir1/colB
+ )
+'
+
+test_expect_success 'overlay sync swap: colB appears' '
+ client_view "//depot/dir2/... //client/..." \
+ "+//depot/dir1/... //client/..." &&
+ files="file11 file12 file21 file22 colA colB" &&
+ echo dir1/colB >actual &&
+ client_verify $files &&
+ test_cmp actual "$cli"/colB &&
+ (
+ cd "$git" &&
+ git p4 sync --use-client-spec &&
+ git merge --ff-only p4/master
+ ) &&
+ git_verify $files &&
+ test_cmp actual "$git"/colB
+'
+
+test_expect_success 'overlay sync swap: add/delete colB in dir2' '
+ client_view "//depot/dir2/... //client/dir2/..." &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ echo dir2/colB >dir2/colB &&
+ p4 add dir2/colB &&
+ p4 submit -d dir2/colB &&
+ p4 delete dir2/colB &&
+ p4 submit -d "delete dir2/colB"
+ )
+'
+
+test_expect_failure 'overlay sync swap: colB no change' '
+ client_view "//depot/dir2/... //client/..." \
+ "+//depot/dir1/... //client/..." &&
+ files="file11 file12 file21 file22 colA colB" &&
+ echo dir1/colB >actual &&
+ client_verify $files &&
+ test_cmp actual "$cli"/colB &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git p4 sync --use-client-spec &&
+ git merge --ff-only p4/master
+ ) &&
+ git_verify $files &&
+ test_cmp actual "$cli"/colB
+'
+
+test_expect_success 'overlay sync swap: cleanup' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ p4 delete dir1/colA dir2/colA dir1/colB &&
+ p4 submit -d "remove overlay sync files"
+ )
+'
+
+#
+# Rename directories to test quoting in depot-side mappings
+# //depot
+# - "dir 1"
+# - file11
+# - file12
+# - "dir 2"
+# - file21
+# - file22
+#
+test_expect_success 'rename files to introduce spaces' '
+ client_view "//depot/... //client/..." &&
+ client_verify dir1/file11 dir1/file12 \
+ dir2/file21 dir2/file22 &&
+ (
+ cd "$cli" &&
+ p4 open dir1/... &&
+ p4 move dir1/... "dir 1"/... &&
+ p4 open dir2/... &&
+ p4 move dir2/... "dir 2"/... &&
+ p4 submit -d "rename with spaces"
+ ) &&
+ client_verify "dir 1/file11" "dir 1/file12" \
+ "dir 2/file21" "dir 2/file22"
+'
+
+test_expect_success 'quotes on lhs only' '
+ client_view "\"//depot/dir 1/...\" //client/cdir1/..." &&
+ files="cdir1/file11 cdir1/file12" &&
+ client_verify $files &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ client_verify $files
+'
+
+test_expect_success 'quotes on both sides' '
+ client_view "\"//depot/dir 1/...\" \"//client/cdir 1/...\"" &&
+ client_verify "cdir 1/file11" "cdir 1/file12" &&
+ test_when_finished cleanup_git &&
+ git p4 clone --use-client-spec --dest="$git" //depot &&
+ git_verify "cdir 1/file11" "cdir 1/file12"
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9810-git-p4-rcs.sh b/t/t9810-git-p4-rcs.sh
new file mode 100755
index 0000000..8134ab4
--- /dev/null
+++ b/t/t9810-git-p4-rcs.sh
@@ -0,0 +1,367 @@
+#!/bin/sh
+
+test_description='git p4 rcs keywords'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+#
+# Make one file with keyword lines at the top, and
+# enough plain text to be able to test modifications
+# far away from the keywords.
+#
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+ cat <<-\EOF >filek &&
+ $Id$
+ /* $Revision$ */
+ # $Change$
+ line4
+ line5
+ line6
+ line7
+ line8
+ EOF
+ sed "s/Revision/Revision: do not scrub me/" <filek >fileko &&
+ sed "s/Id/Id: do not scrub me/" <fileko >file_text &&
+ p4 add -t text+k filek &&
+ p4 submit -d "filek" &&
+ p4 add -t text+ko fileko &&
+ p4 submit -d "fileko" &&
+ p4 add -t text file_text &&
+ p4 submit -d "file_text"
+ )
+'
+
+#
+# Generate these in a function to make it easy to use single quote marks.
+#
+write_scrub_scripts () {
+ cat >"$TRASH_DIRECTORY/scrub_k.py" <<-\EOF &&
+ import re, sys
+ sys.stdout.write(re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$', r'$\1$', sys.stdin.read()))
+ EOF
+ cat >"$TRASH_DIRECTORY/scrub_ko.py" <<-\EOF
+ import re, sys
+ sys.stdout.write(re.sub(r'(?i)\$(Id|Header):[^$]*\$', r'$\1$', sys.stdin.read()))
+ EOF
+}
+
+test_expect_success 'scrub scripts' '
+ write_scrub_scripts
+'
+
+#
+# Compare $cli/file to its scrubbed version, should be different.
+# Compare scrubbed $cli/file to $git/file, should be same.
+#
+scrub_k_check () {
+ file="$1" &&
+ scrub="$TRASH_DIRECTORY/$file" &&
+ "$PYTHON_PATH" "$TRASH_DIRECTORY/scrub_k.py" <"$git/$file" >"$scrub" &&
+ ! test_cmp "$cli/$file" "$scrub" &&
+ test_cmp "$git/$file" "$scrub" &&
+ rm "$scrub"
+}
+scrub_ko_check () {
+ file="$1" &&
+ scrub="$TRASH_DIRECTORY/$file" &&
+ "$PYTHON_PATH" "$TRASH_DIRECTORY/scrub_ko.py" <"$git/$file" >"$scrub" &&
+ ! test_cmp "$cli/$file" "$scrub" &&
+ test_cmp "$git/$file" "$scrub" &&
+ rm "$scrub"
+}
+
+#
+# Modify far away from keywords. If no RCS lines show up
+# in the diff, there is no conflict.
+#
+test_expect_success 'edit far away from RCS lines' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ sed "s/^line7/line7 edit/" <filek >filek.tmp &&
+ mv -f filek.tmp filek &&
+ git commit -m "filek line7 edit" filek &&
+ git p4 submit &&
+ scrub_k_check filek
+ )
+'
+
+#
+# Modify near the keywords. This will require RCS scrubbing.
+#
+test_expect_success 'edit near RCS lines' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.attemptRCSCleanup true &&
+ sed "s/^line4/line4 edit/" <filek >filek.tmp &&
+ mv -f filek.tmp filek &&
+ git commit -m "filek line4 edit" filek &&
+ git p4 submit &&
+ scrub_k_check filek
+ )
+'
+
+#
+# Modify the keywords themselves. This also will require RCS scrubbing.
+#
+test_expect_success 'edit keyword lines' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.attemptRCSCleanup true &&
+ sed "/Revision/d" <filek >filek.tmp &&
+ mv -f filek.tmp filek &&
+ git commit -m "filek remove Revision line" filek &&
+ git p4 submit &&
+ scrub_k_check filek
+ )
+'
+
+#
+# Scrubbing text+ko files should not alter all keywords, just Id, Header.
+#
+test_expect_success 'scrub ko files differently' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.attemptRCSCleanup true &&
+ sed "s/^line4/line4 edit/" <fileko >fileko.tmp &&
+ mv -f fileko.tmp fileko &&
+ git commit -m "fileko line4 edit" fileko &&
+ git p4 submit &&
+ scrub_ko_check fileko &&
+ ! scrub_k_check fileko
+ )
+'
+
+# hack; git p4 submit should do it on its own
+test_expect_success 'cleanup after failure' '
+ (
+ cd "$cli" &&
+ p4 revert ...
+ )
+'
+
+# perl $File:: bug check
+test_expect_success 'ktext expansion should not expand multi-line $File::' '
+ (
+ cd "$cli" &&
+ cat >lv.pm <<-\EOF
+ my $wanted = sub { my $f = $File::Find::name;
+ if ( -f && $f =~ /foo/ ) {
+ EOF
+ p4 add -t ktext lv.pm &&
+ p4 submit -d "lv.pm"
+ ) &&
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ test_cmp "$cli/lv.pm" lv.pm
+ )
+'
+
+#
+# Do not scrub anything but +k or +ko files. Sneak a change into
+# the cli file so that submit will get a conflict. Make sure that
+# scrubbing doesn't make a mess of things.
+#
+# This might happen only if the git repo is behind the p4 repo at
+# submit time, and there is a conflict.
+#
+test_expect_success 'do not scrub plain text' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.attemptRCSCleanup true &&
+ sed "s/^line4/line4 edit/" <file_text >file_text.tmp &&
+ mv -f file_text.tmp file_text &&
+ git commit -m "file_text line4 edit" file_text &&
+ (
+ cd "$cli" &&
+ p4 open file_text &&
+ sed "s/^line5/line5 p4 edit/" <file_text >file_text.tmp &&
+ mv -f file_text.tmp file_text &&
+ p4 submit -d "file5 p4 edit"
+ ) &&
+ echo s | test_expect_code 1 git p4 submit &&
+ (
+ # make sure the file is not left open
+ cd "$cli" &&
+ ! p4 fstat -T action file_text
+ )
+ )
+'
+
+# hack; git p4 submit should do it on its own
+test_expect_success 'cleanup after failure 2' '
+ (
+ cd "$cli" &&
+ p4 revert ...
+ )
+'
+
+create_kw_file () {
+ cat <<\EOF >"$1"
+/* A file
+ Id: $Id$
+ Revision: $Revision$
+ File: $File$
+ */
+int main(int argc, const char **argv) {
+ return 0;
+}
+EOF
+}
+
+test_expect_success 'add kwfile' '
+ (
+ cd "$cli" &&
+ echo file1 >file1 &&
+ p4 add file1 &&
+ p4 submit -d "file 1" &&
+ create_kw_file kwfile1.c &&
+ p4 add kwfile1.c &&
+ p4 submit -d "Add rcw kw file" kwfile1.c
+ )
+'
+
+p4_append_to_file () {
+ f="$1" &&
+ p4 edit -t ktext "$f" &&
+ echo "/* $(date) */" >>"$f" &&
+ p4 submit -d "appending a line in p4"
+}
+
+# Create some files with RCS keywords. If they get modified
+# elsewhere then the version number gets bumped which then
+# results in a merge conflict if we touch the RCS kw lines,
+# even though the change itself would otherwise apply cleanly.
+test_expect_success 'cope with rcs keyword expansion damage' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.attemptRCSCleanup true &&
+ (cd "$cli" && p4_append_to_file kwfile1.c) &&
+ old_lines=$(wc -l <kwfile1.c) &&
+ perl -n -i -e "print unless m/Revision:/" kwfile1.c &&
+ new_lines=$(wc -l <kwfile1.c) &&
+ test $new_lines = $(($old_lines - 1)) &&
+
+ git add kwfile1.c &&
+ git commit -m "Zap an RCS kw line" &&
+ git p4 submit &&
+ git p4 rebase &&
+ git diff p4/master &&
+ git p4 commit &&
+ echo "try modifying in both" &&
+ cd "$cli" &&
+ p4 edit kwfile1.c &&
+ echo "line from p4" >>kwfile1.c &&
+ p4 submit -d "add a line in p4" kwfile1.c &&
+ cd "$git" &&
+ echo "line from git at the top" | cat - kwfile1.c >kwfile1.c.new &&
+ mv kwfile1.c.new kwfile1.c &&
+ git commit -m "Add line in git at the top" kwfile1.c &&
+ git p4 rebase &&
+ git p4 submit
+ )
+'
+
+test_expect_success 'cope with rcs keyword file deletion' '
+ test_when_finished cleanup_git &&
+ (
+ cd "$cli" &&
+ echo "\$Revision\$" >kwdelfile.c &&
+ p4 add -t ktext kwdelfile.c &&
+ p4 submit -d "Add file to be deleted" &&
+ cat kwdelfile.c &&
+ grep 1 kwdelfile.c
+ ) &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ grep Revision kwdelfile.c &&
+ git rm -f kwdelfile.c &&
+ git commit -m "Delete a file containing RCS keywords" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.attemptRCSCleanup true &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ ! test -f kwdelfile.c
+ )
+'
+
+# If you add keywords in git of the form $Header$ then everything should
+# work fine without any special handling.
+test_expect_success 'Add keywords in git which match the default p4 values' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo "NewKW: \$Revision\$" >>kwfile1.c &&
+ git add kwfile1.c &&
+ git commit -m "Adding RCS keywords in git" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.attemptRCSCleanup true &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ test -f kwfile1.c &&
+ grep "NewKW.*Revision.*[0-9]" kwfile1.c
+
+ )
+'
+
+# If you add keywords in git of the form $Header:#1$ then things will fail
+# unless git-p4 takes steps to scrub the *git* commit.
+#
+test_expect_failure 'Add keywords in git which do not match the default p4 values' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo "NewKW2: \$Revision:1\$" >>kwfile1.c &&
+ git add kwfile1.c &&
+ git commit -m "Adding RCS keywords in git" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.attemptRCSCleanup true &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ p4 sync &&
+ grep "NewKW2.*Revision.*[0-9]" kwfile1.c
+
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9811-git-p4-label-import.sh b/t/t9811-git-p4-label-import.sh
new file mode 100755
index 0000000..decb66b
--- /dev/null
+++ b/t/t9811-git-p4-label-import.sh
@@ -0,0 +1,267 @@
+#!/bin/sh
+
+test_description='git p4 label tests'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+# Basic p4 label import tests.
+#
+test_expect_success 'basic p4 labels' '
+ test_when_finished cleanup_git &&
+ (
+ cd "$cli" &&
+ mkdir -p main &&
+
+ echo f1 >main/f1 &&
+ p4 add main/f1 &&
+ p4 submit -d "main/f1" &&
+
+ echo f2 >main/f2 &&
+ p4 add main/f2 &&
+ p4 submit -d "main/f2" &&
+
+ echo f3 >main/file_with_\$metachar &&
+ p4 add main/file_with_\$metachar &&
+ p4 submit -d "file with metachar" &&
+
+ p4 tag -l TAG_F1_ONLY main/f1 &&
+ p4 tag -l TAG_WITH\$_SHELL_CHAR main/... &&
+ p4 tag -l this_tag_will_be\ skipped main/... &&
+
+ echo f4 >main/f4 &&
+ p4 add main/f4 &&
+ p4 submit -d "main/f4" &&
+
+ p4 label -i <<-EOF &&
+ Label: TAG_LONG_LABEL
+ Description:
+ A Label first line
+ A Label second line
+ View: //depot/...
+ EOF
+
+ p4 tag -l TAG_LONG_LABEL ... &&
+
+ p4 labels ... &&
+
+ git p4 clone --dest="$git" //depot@all &&
+ cd "$git" &&
+ git config git-p4.labelImportRegexp ".*TAG.*" &&
+ git p4 sync --import-labels --verbose &&
+
+ git tag &&
+ git tag >taglist &&
+ test_line_count = 3 taglist &&
+
+ cd main &&
+ git checkout TAG_F1_ONLY &&
+ ! test -f f2 &&
+ git checkout TAG_WITH\$_SHELL_CHAR &&
+ test -f f1 && test -f f2 && test -f file_with_\$metachar &&
+
+ git show TAG_LONG_LABEL | grep -q "A Label second line"
+ )
+'
+# Test some label corner cases:
+#
+# - two tags on the same file; both should be available
+# - a tag that is only on one file; this kind of tag
+# cannot be imported (at least not easily).
+
+test_expect_success 'two labels on the same changelist' '
+ test_when_finished cleanup_git &&
+ (
+ cd "$cli" &&
+ mkdir -p main &&
+
+ p4 edit main/f1 main/f2 &&
+ echo "hello world" >main/f1 &&
+ echo "not in the tag" >main/f2 &&
+ p4 submit -d "main/f[12]: testing two labels" &&
+
+ p4 tag -l TAG_F1_1 main/... &&
+ p4 tag -l TAG_F1_2 main/... &&
+
+ p4 labels ... &&
+
+ git p4 clone --dest="$git" //depot@all &&
+ cd "$git" &&
+ git p4 sync --import-labels &&
+
+ git tag | grep TAG_F1 &&
+ git tag | grep -q TAG_F1_1 &&
+ git tag | grep -q TAG_F1_2 &&
+
+ cd main &&
+
+ git checkout TAG_F1_1 &&
+ ls &&
+ test -f f1 &&
+
+ git checkout TAG_F1_2 &&
+ ls &&
+ test -f f1
+ )
+'
+
+# Export some git tags to p4
+test_expect_success 'export git tags to p4' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git tag -m "A tag created in git:xyzzy" GIT_TAG_1 &&
+ echo "hello world" >main/f10 &&
+ git add main/f10 &&
+ git commit -m "Adding file for export test" &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit &&
+ git tag -m "Another git tag" GIT_TAG_2 &&
+ git tag LIGHTWEIGHT_TAG &&
+ git p4 rebase --import-labels --verbose &&
+ git p4 submit --export-labels --verbose
+ ) &&
+ (
+ cd "$cli" &&
+ p4 sync ... &&
+ p4 labels ... | grep GIT_TAG_1 &&
+ p4 labels ... | grep GIT_TAG_2 &&
+ p4 labels ... | grep LIGHTWEIGHT_TAG &&
+ p4 label -o GIT_TAG_1 | grep "tag created in git:xyzzy" &&
+ p4 sync ...@GIT_TAG_1 &&
+ ! test -f main/f10
+ p4 sync ...@GIT_TAG_2 &&
+ test -f main/f10
+ )
+'
+
+# Export a tag from git where an affected file is deleted later on
+# Need to create git tags after rebase, since only then can the
+# git commits be mapped to p4 changelists.
+test_expect_success 'export git tags to p4 with deletion' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git p4 sync --import-labels &&
+ echo "deleted file" >main/deleted_file &&
+ git add main/deleted_file &&
+ git commit -m "create deleted file" &&
+ git rm main/deleted_file &&
+ echo "new file" >main/f11 &&
+ git add main/f11 &&
+ git commit -m "delete the deleted file" &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit &&
+ git p4 rebase --import-labels --verbose &&
+ git tag -m "tag on deleted file" GIT_TAG_ON_DELETED HEAD~1 &&
+ git tag -m "tag after deletion" GIT_TAG_AFTER_DELETION HEAD &&
+ git p4 submit --export-labels --verbose
+ ) &&
+ (
+ cd "$cli" &&
+ p4 sync ... &&
+ p4 sync ...@GIT_TAG_ON_DELETED &&
+ test -f main/deleted_file &&
+ p4 sync ...@GIT_TAG_AFTER_DELETION &&
+ ! test -f main/deleted_file &&
+ echo "checking label contents" &&
+ p4 label -o GIT_TAG_ON_DELETED | grep "tag on deleted file"
+ )
+'
+
+# Create a tag in git that cannot be exported to p4
+test_expect_success 'tag that cannot be exported' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git checkout -b a_branch &&
+ echo "hello" >main/f12 &&
+ git add main/f12 &&
+ git commit -m "adding f12" &&
+ git tag -m "tag on a_branch" GIT_TAG_ON_A_BRANCH &&
+ git checkout master &&
+ git p4 submit --export-labels
+ ) &&
+ (
+ cd "$cli" &&
+ p4 sync ... &&
+ !(p4 labels | grep GIT_TAG_ON_A_BRANCH)
+ )
+'
+
+test_expect_success 'use git config to enable import/export of tags' '
+ git p4 clone --verbose --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git config git-p4.exportLabels true &&
+ git config git-p4.importLabels true &&
+ git tag CFG_A_GIT_TAG &&
+ git p4 rebase --verbose &&
+ git p4 submit --verbose &&
+ git tag &&
+ git tag | grep TAG_F1_1
+ ) &&
+ (
+ cd "$cli" &&
+ p4 labels &&
+ p4 labels | grep CFG_A_GIT_TAG
+ )
+'
+
+p4_head_revision() {
+ p4 changes -m 1 "$@" | awk '{print $2}'
+}
+
+# Importing a label that references a P4 commit that
+# has not been seen. The presence of a label on a commit
+# we haven't seen should not cause git-p4 to fail. It should
+# merely skip that label, and still import other labels.
+test_expect_success 'importing labels with missing revisions' '
+ test_when_finished cleanup_git &&
+ (
+ rm -fr "$cli" "$git" &&
+ mkdir "$cli" &&
+ P4CLIENT=missing-revision &&
+ client_view "//depot/missing-revision/... //missing-revision/..." &&
+ cd "$cli" &&
+ >f1 && p4 add f1 && p4 submit -d "start" &&
+
+ p4 tag -l TAG_S0 ... &&
+
+ >f2 && p4 add f2 && p4 submit -d "second" &&
+
+ startrev=$(p4_head_revision //depot/missing-revision/...) &&
+
+ >f3 && p4 add f3 && p4 submit -d "third" &&
+
+ p4 edit f2 && date >f2 && p4 submit -d "change" f2 &&
+
+ endrev=$(p4_head_revision //depot/missing-revision/...) &&
+
+ p4 tag -l TAG_S1 ... &&
+
+ # we should skip TAG_S0 since it is before our startpoint,
+ # but pick up TAG_S1.
+
+ git p4 clone --dest="$git" --import-labels -v \
+ //depot/missing-revision/...@$startrev,$endrev &&
+ (
+ cd "$git" &&
+ git rev-parse TAG_S1 &&
+ ! git rev-parse TAG_S0
+ )
+ )
+'
+
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9812-git-p4-wildcards.sh b/t/t9812-git-p4-wildcards.sh
new file mode 100755
index 0000000..0206771
--- /dev/null
+++ b/t/t9812-git-p4-wildcards.sh
@@ -0,0 +1,218 @@
+#!/bin/sh
+
+test_description='git p4 wildcards'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'add p4 files with wildcards in the names' '
+ (
+ cd "$cli" &&
+ printf "file2\nhas\nsome\nrandom\ntext\n" >file2 &&
+ p4 add file2 &&
+ echo file-wild-hash >file-wild#hash &&
+ if test_have_prereq !MINGW,!CYGWIN
+ then
+ echo file-wild-star >file-wild\*star
+ fi &&
+ echo file-wild-at >file-wild@at &&
+ echo file-wild-percent >file-wild%percent &&
+ p4 add -f file-wild* &&
+ p4 submit -d "file wildcards"
+ )
+'
+
+test_expect_success 'wildcard files git p4 clone' '
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ test -f file-wild#hash &&
+ if test_have_prereq !MINGW,!CYGWIN
+ then
+ test -f file-wild\*star
+ fi &&
+ test -f file-wild@at &&
+ test -f file-wild%percent
+ )
+'
+
+test_expect_success 'wildcard files submit back to p4, add' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo git-wild-hash >git-wild#hash &&
+ if test_have_prereq !MINGW,!CYGWIN
+ then
+ echo git-wild-star >git-wild\*star
+ fi &&
+ echo git-wild-at >git-wild@at &&
+ echo git-wild-percent >git-wild%percent &&
+ git add git-wild* &&
+ git commit -m "add some wildcard filenames" &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_file git-wild#hash &&
+ if test_have_prereq !MINGW,!CYGWIN
+ then
+ test_path_is_file git-wild\*star
+ fi &&
+ test_path_is_file git-wild@at &&
+ test_path_is_file git-wild%percent
+ )
+'
+
+test_expect_success 'wildcard files submit back to p4, modify' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo new-line >>git-wild#hash &&
+ if test_have_prereq !MINGW,!CYGWIN
+ then
+ echo new-line >>git-wild\*star
+ fi &&
+ echo new-line >>git-wild@at &&
+ echo new-line >>git-wild%percent &&
+ git add git-wild* &&
+ git commit -m "modify the wildcard files" &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_line_count = 2 git-wild#hash &&
+ if test_have_prereq !MINGW,!CYGWIN
+ then
+ test_line_count = 2 git-wild\*star
+ fi &&
+ test_line_count = 2 git-wild@at &&
+ test_line_count = 2 git-wild%percent
+ )
+'
+
+test_expect_success 'wildcard files submit back to p4, copy' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ cp file2 git-wild-cp#hash &&
+ git add git-wild-cp#hash &&
+ cp git-wild#hash file-wild-3 &&
+ git add file-wild-3 &&
+ git commit -m "wildcard copies" &&
+ git config git-p4.detectCopies true &&
+ git config git-p4.detectCopiesHarder true &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_file git-wild-cp#hash &&
+ test_path_is_file file-wild-3
+ )
+'
+
+test_expect_success 'wildcard files submit back to p4, rename' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git mv git-wild@at file-wild-4 &&
+ git mv file-wild-3 git-wild-cp%percent &&
+ git commit -m "wildcard renames" &&
+ git config git-p4.detectRenames true &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_missing git-wild@at &&
+ test_path_is_file git-wild-cp%percent
+ )
+'
+
+test_expect_success 'wildcard files submit back to p4, delete' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git rm git-wild* &&
+ git commit -m "delete the wildcard files" &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_missing git-wild#hash &&
+ if test_have_prereq !MINGW,!CYGWIN
+ then
+ test_path_is_missing git-wild\*star
+ fi &&
+ test_path_is_missing git-wild@at &&
+ test_path_is_missing git-wild%percent
+ )
+'
+
+test_expect_success 'p4 deleted a wildcard file' '
+ (
+ cd "$cli" &&
+ echo "wild delete test" >wild@delete &&
+ p4 add -f wild@delete &&
+ p4 submit -d "add wild@delete"
+ ) &&
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ test_path_is_file wild@delete
+ ) &&
+ (
+ cd "$cli" &&
+ # must use its encoded name
+ p4 delete wild%40delete &&
+ p4 submit -d "delete wild@delete"
+ ) &&
+ (
+ cd "$git" &&
+ git p4 sync &&
+ git merge --ff-only p4/master &&
+ test_path_is_missing wild@delete
+ )
+'
+
+test_expect_success 'wildcard files requiring keyword scrub' '
+ (
+ cd "$cli" &&
+ cat <<-\EOF >scrub@wild &&
+ $Id$
+ line2
+ EOF
+ p4 add -t text+k -f scrub@wild &&
+ p4 submit -d "scrub at wild"
+ ) &&
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.attemptRCSCleanup true &&
+ sed "s/^line2/line2 edit/" <scrub@wild >scrub@wild.tmp &&
+ mv -f scrub@wild.tmp scrub@wild &&
+ git commit -m "scrub at wild line2 edit" scrub@wild &&
+ git p4 submit
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9813-git-p4-preserve-users.sh b/t/t9813-git-p4-preserve-users.sh
new file mode 100755
index 0000000..0fe2312
--- /dev/null
+++ b/t/t9813-git-p4-preserve-users.sh
@@ -0,0 +1,145 @@
+#!/bin/sh
+
+test_description='git p4 preserve users'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'create files' '
+ (
+ cd "$cli" &&
+ p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i &&
+ echo file1 >file1 &&
+ echo file2 >file2 &&
+ p4 add file1 file2 &&
+ p4 submit -d "add files"
+ )
+'
+
+p4_grant_admin() {
+ name=$1 &&
+ {
+ p4 protect -o &&
+ echo " admin user $name * //depot/..."
+ } | p4 protect -i
+}
+
+p4_check_commit_author() {
+ file=$1 user=$2 &&
+ p4 changes -m 1 //depot/$file | grep -q $user
+}
+
+make_change_by_user() {
+ file=$1 name=$2 email=$3 &&
+ echo "username: a change by $name" >>"$file" &&
+ git add "$file" &&
+ git commit --author "$name <$email>" -m "a change by $name"
+}
+
+# Test username support, submitting as user 'alice'
+test_expect_success 'preserve users' '
+ p4_add_user alice &&
+ p4_add_user bob &&
+ p4_grant_admin alice &&
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ echo "username: a change by alice" >>file1 &&
+ echo "username: a change by bob" >>file2 &&
+ git commit --author "Alice <alice@example.com>" -m "a change by alice" file1 &&
+ git commit --author "Bob <bob@example.com>" -m "a change by bob" file2 &&
+ git config git-p4.skipSubmitEditCheck true &&
+ P4EDITOR="test-chmtime +5" P4USER=alice P4PASSWD=secret &&
+ export P4EDITOR P4USER P4PASSWD &&
+ git p4 commit --preserve-user &&
+ p4_check_commit_author file1 alice &&
+ p4_check_commit_author file2 bob
+ )
+'
+
+# Test username support, submitting as bob, who lacks admin rights. Should
+# not submit change to p4 (git diff should show deltas).
+test_expect_success 'refuse to preserve users without perms' '
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEditCheck true &&
+ echo "username-noperms: a change by alice" >>file1 &&
+ git commit --author "Alice <alice@example.com>" -m "perms: a change by alice" file1 &&
+ P4EDITOR="test-chmtime +5" P4USER=bob P4PASSWD=secret &&
+ export P4EDITOR P4USER P4PASSWD &&
+ test_must_fail git p4 commit --preserve-user &&
+ ! git diff --exit-code HEAD..p4/master
+ )
+'
+
+# What happens with unknown author? Without allowMissingP4Users it should fail.
+test_expect_success 'preserve user where author is unknown to p4' '
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEditCheck true &&
+ echo "username-bob: a change by bob" >>file1 &&
+ git commit --author "Bob <bob@example.com>" -m "preserve: a change by bob" file1 &&
+ echo "username-unknown: a change by charlie" >>file1 &&
+ git commit --author "Charlie <charlie@example.com>" -m "preserve: a change by charlie" file1 &&
+ P4EDITOR="test-chmtime +5" P4USER=alice P4PASSWD=secret &&
+ export P4EDITOR P4USER P4PASSWD &&
+ test_must_fail git p4 commit --preserve-user &&
+ ! git diff --exit-code HEAD..p4/master &&
+
+ echo "$0: repeat with allowMissingP4Users enabled" &&
+ git config git-p4.allowMissingP4Users true &&
+ git config git-p4.preserveUser true &&
+ git p4 commit &&
+ git diff --exit-code HEAD..p4/master &&
+ p4_check_commit_author file1 alice
+ )
+'
+
+# If we're *not* using --preserve-user, git-p4 should warn if we're submitting
+# changes that are not all ours.
+# Test: user in p4 and user unknown to p4.
+# Test: warning disabled and user is the same.
+test_expect_success 'not preserving user with mixed authorship' '
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEditCheck true &&
+ p4_add_user derek &&
+
+ make_change_by_user usernamefile3 Derek derek@example.com &&
+ P4EDITOR=cat P4USER=alice P4PASSWD=secret &&
+ export P4EDITOR P4USER P4PASSWD &&
+ git p4 commit |\
+ grep "git author derek@example.com does not match" &&
+
+ make_change_by_user usernamefile3 Charlie charlie@example.com &&
+ git p4 commit |\
+ grep "git author charlie@example.com does not match" &&
+
+ make_change_by_user usernamefile3 alice alice@example.com &&
+ git p4 commit |\
+ test_must_fail grep "git author.*does not match" &&
+
+ git config git-p4.skipUserNameCheck true &&
+ make_change_by_user usernamefile3 Charlie charlie@example.com &&
+ git p4 commit |\
+ test_must_fail grep "git author.*does not match" &&
+
+ p4_check_commit_author usernamefile3 alice
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh
new file mode 100755
index 0000000..c89992c
--- /dev/null
+++ b/t/t9814-git-p4-rename.sh
@@ -0,0 +1,261 @@
+#!/bin/sh
+
+test_description='git p4 rename'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+# We rely on this behavior to detect for p4 move availability.
+test_expect_success 'p4 help unknown returns 1' '
+ (
+ cd "$cli" &&
+ (
+ p4 help client >errs 2>&1
+ echo $? >retval
+ )
+ echo 0 >expected &&
+ test_cmp expected retval &&
+ rm retval &&
+ (
+ p4 help nosuchcommand >errs 2>&1
+ echo $? >retval
+ )
+ echo 1 >expected &&
+ test_cmp expected retval &&
+ rm retval
+ )
+'
+
+test_expect_success 'create files' '
+ (
+ cd "$cli" &&
+ p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i &&
+ cat >file1 <<-EOF &&
+ A large block of text
+ in file1 that will generate
+ enough context so that rename
+ and copy detection will find
+ something interesting to do.
+ EOF
+ cat >file2 <<-EOF &&
+ /*
+ * This blob looks a bit
+ * different.
+ */
+ int main(int argc, char **argv)
+ {
+ char text[200];
+
+ strcpy(text, "copy/rename this");
+ printf("text is %s\n", text);
+ return 0;
+ }
+ EOF
+ p4 add file1 file2 &&
+ p4 submit -d "add files"
+ )
+'
+
+# Rename a file and confirm that rename is not detected in P4.
+# Rename the new file again with detectRenames option enabled and confirm that
+# this is detected in P4.
+# Rename the new file again adding an extra line, configure a big threshold in
+# detectRenames and confirm that rename is not detected in P4.
+# Repeat, this time with a smaller threshold and confirm that the rename is
+# detected in P4.
+test_expect_success 'detect renames' '
+ git p4 clone --dest="$git" //depot@all &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+
+ git mv file1 file4 &&
+ git commit -a -m "Rename file1 to file4" &&
+ git diff-tree -r -M HEAD &&
+ git p4 submit &&
+ p4 filelog //depot/file4 >filelog &&
+ ! grep " from //depot" filelog &&
+
+ git mv file4 file5 &&
+ git commit -a -m "Rename file4 to file5" &&
+ git diff-tree -r -M HEAD &&
+ git config git-p4.detectRenames true &&
+ git p4 submit &&
+ p4 filelog //depot/file5 >filelog &&
+ grep " from //depot/file4" filelog &&
+
+ git mv file5 file6 &&
+ echo update >>file6 &&
+ git add file6 &&
+ git commit -a -m "Rename file5 to file6 with changes" &&
+ git diff-tree -r -M HEAD &&
+ level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") &&
+ test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 &&
+ git config git-p4.detectRenames $(($level + 2)) &&
+ git p4 submit &&
+ p4 filelog //depot/file6 >filelog &&
+ ! grep " from //depot" filelog &&
+
+ git mv file6 file7 &&
+ echo update >>file7 &&
+ git add file7 &&
+ git commit -a -m "Rename file6 to file7 with changes" &&
+ git diff-tree -r -M HEAD &&
+ level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") &&
+ test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 &&
+ git config git-p4.detectRenames $(($level - 2)) &&
+ git p4 submit &&
+ p4 filelog //depot/file7 >filelog &&
+ grep " from //depot/file6" filelog
+ )
+'
+
+# Copy a file and confirm that copy is not detected in P4.
+# Copy a file with detectCopies option enabled and confirm that copy is not
+# detected in P4.
+# Modify and copy a file with detectCopies option enabled and confirm that copy
+# is detected in P4.
+# Copy a file with detectCopies and detectCopiesHarder options enabled and
+# confirm that copy is detected in P4.
+# Modify and copy a file, configure a bigger threshold in detectCopies and
+# confirm that copy is not detected in P4.
+# Modify and copy a file, configure a smaller threshold in detectCopies and
+# confirm that copy is detected in P4.
+test_expect_success 'detect copies' '
+ git p4 clone --dest="$git" //depot@all &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+
+ echo "file8" >>file2 &&
+ git commit -a -m "Differentiate file2" &&
+ git p4 submit &&
+ cp file2 file8 &&
+ git add file8 &&
+ git commit -a -m "Copy file2 to file8" &&
+ git diff-tree -r -C HEAD &&
+ git p4 submit &&
+ p4 filelog //depot/file8 &&
+ p4 filelog //depot/file8 | test_must_fail grep -q "branch from" &&
+
+ echo "file9" >>file2 &&
+ git commit -a -m "Differentiate file2" &&
+ git p4 submit &&
+
+ cp file2 file9 &&
+ git add file9 &&
+ git commit -a -m "Copy file2 to file9" &&
+ git diff-tree -r -C HEAD &&
+ git config git-p4.detectCopies true &&
+ git p4 submit &&
+ p4 filelog //depot/file9 &&
+ p4 filelog //depot/file9 | test_must_fail grep -q "branch from" &&
+
+ echo "file10" >>file2 &&
+ git commit -a -m "Differentiate file2" &&
+ git p4 submit &&
+
+ echo "file2" >>file2 &&
+ cp file2 file10 &&
+ git add file2 file10 &&
+ git commit -a -m "Modify and copy file2 to file10" &&
+ git diff-tree -r -C HEAD &&
+ src=$(git diff-tree -r -C HEAD | sed 1d | sed 2d | cut -f2) &&
+ test "$src" = file2 &&
+ git p4 submit &&
+ p4 filelog //depot/file10 &&
+ p4 filelog //depot/file10 | grep -q "branch from //depot/file2" &&
+
+ echo "file11" >>file2 &&
+ git commit -a -m "Differentiate file2" &&
+ git p4 submit &&
+
+ cp file2 file11 &&
+ git add file11 &&
+ git commit -a -m "Copy file2 to file11" &&
+ git diff-tree -r -C --find-copies-harder HEAD &&
+ src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
+ test "$src" = file2 &&
+ git config git-p4.detectCopiesHarder true &&
+ git p4 submit &&
+ p4 filelog //depot/file11 &&
+ p4 filelog //depot/file11 | grep -q "branch from //depot/file2" &&
+
+ echo "file12" >>file2 &&
+ git commit -a -m "Differentiate file2" &&
+ git p4 submit &&
+
+ cp file2 file12 &&
+ echo "some text" >>file12 &&
+ git add file12 &&
+ git commit -a -m "Copy file2 to file12 with changes" &&
+ git diff-tree -r -C --find-copies-harder HEAD &&
+ level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") &&
+ test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 &&
+ src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
+ test "$src" = file2 &&
+ git config git-p4.detectCopies $(($level + 2)) &&
+ git p4 submit &&
+ p4 filelog //depot/file12 &&
+ p4 filelog //depot/file12 | test_must_fail grep -q "branch from" &&
+
+ echo "file13" >>file2 &&
+ git commit -a -m "Differentiate file2" &&
+ git p4 submit &&
+
+ cp file2 file13 &&
+ echo "different text" >>file13 &&
+ git add file13 &&
+ git commit -a -m "Copy file2 to file13 with changes" &&
+ git diff-tree -r -C --find-copies-harder HEAD &&
+ level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") &&
+ test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 &&
+ src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
+ test "$src" = file2 &&
+ git config git-p4.detectCopies $(($level - 2)) &&
+ git p4 submit &&
+ p4 filelog //depot/file13 &&
+ p4 filelog //depot/file13 | grep -q "branch from //depot/file2"
+ )
+'
+
+# See if configurables can be set, and in particular if the run.move.allow
+# variable exists, which allows admins to disable the "p4 move" command.
+test_lazy_prereq P4D_HAVE_CONFIGURABLE_RUN_MOVE_ALLOW '
+ p4 configure show run.move.allow >out &&
+ egrep ^run.move.allow: out
+'
+
+# If move can be disabled, turn it off and test p4 move handling
+test_expect_success P4D_HAVE_CONFIGURABLE_RUN_MOVE_ALLOW \
+ 'do not use p4 move when administratively disabled' '
+ test_when_finished "p4 configure set run.move.allow=1" &&
+ p4 configure set run.move.allow=0 &&
+ (
+ cd "$cli" &&
+ echo move-disallow-file >move-disallow-file &&
+ p4 add move-disallow-file &&
+ p4 submit -d "add move-disallow-file"
+ ) &&
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.detectRenames true &&
+ git mv move-disallow-file move-disallow-file-moved &&
+ git commit -m "move move-disallow-file" &&
+ git p4 submit
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh
new file mode 100755
index 0000000..37b42d0
--- /dev/null
+++ b/t/t9815-git-p4-submit-fail.sh
@@ -0,0 +1,429 @@
+#!/bin/sh
+
+test_description='git p4 submit failure handling'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+ p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i &&
+ echo line1 >file1 &&
+ p4 add file1 &&
+ p4 submit -d "line1 in file1"
+ )
+'
+
+test_expect_success 'conflict on one commit' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$cli" &&
+ p4 open file1 &&
+ echo line2 >>file1 &&
+ p4 submit -d "line2 in file1"
+ ) &&
+ (
+ # now this commit should cause a conflict
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ echo line3 >>file1 &&
+ git add file1 &&
+ git commit -m "line3 in file1 will conflict" &&
+ test_expect_code 1 git p4 submit >out &&
+ test_i18ngrep "No commits applied" out
+ )
+'
+
+test_expect_success 'conflict on second of two commits' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$cli" &&
+ p4 open file1 &&
+ echo line3 >>file1 &&
+ p4 submit -d "line3 in file1"
+ ) &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ # this commit is okay
+ test_commit "first_commit_okay" &&
+ # now this submit should cause a conflict
+ echo line4 >>file1 &&
+ git add file1 &&
+ git commit -m "line4 in file1 will conflict" &&
+ test_expect_code 1 git p4 submit >out &&
+ test_i18ngrep "Applied only the commits" out
+ )
+'
+
+test_expect_success 'conflict on first of two commits, skip' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$cli" &&
+ p4 open file1 &&
+ echo line4 >>file1 &&
+ p4 submit -d "line4 in file1"
+ ) &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ # this submit should cause a conflict
+ echo line5 >>file1 &&
+ git add file1 &&
+ git commit -m "line5 in file1 will conflict" &&
+ # but this commit is okay
+ test_commit "okay_commit_after_skip" &&
+ echo s | test_expect_code 1 git p4 submit >out &&
+ test_i18ngrep "Applied only the commits" out
+ )
+'
+
+test_expect_success 'conflict on first of two commits, quit' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$cli" &&
+ p4 open file1 &&
+ echo line7 >>file1 &&
+ p4 submit -d "line7 in file1"
+ ) &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ # this submit should cause a conflict
+ echo line8 >>file1 &&
+ git add file1 &&
+ git commit -m "line8 in file1 will conflict" &&
+ # but this commit is okay
+ test_commit "okay_commit_after_quit" &&
+ echo q | test_expect_code 1 git p4 submit >out &&
+ test_i18ngrep "No commits applied" out
+ )
+'
+
+test_expect_success 'conflict cli and config options' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git p4 submit --conflict=ask &&
+ git p4 submit --conflict=skip &&
+ git p4 submit --conflict=quit &&
+ test_expect_code 2 git p4 submit --conflict=foo &&
+ test_expect_code 2 git p4 submit --conflict &&
+ git config git-p4.conflict foo &&
+ test_expect_code 1 git p4 submit &&
+ git config --unset git-p4.conflict &&
+ git p4 submit
+ )
+'
+
+test_expect_success 'conflict on first of two commits, --conflict=skip' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$cli" &&
+ p4 open file1 &&
+ echo line9 >>file1 &&
+ p4 submit -d "line9 in file1"
+ ) &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ # this submit should cause a conflict
+ echo line10 >>file1 &&
+ git add file1 &&
+ git commit -m "line10 in file1 will conflict" &&
+ # but this commit is okay
+ test_commit "okay_commit_after_auto_skip" &&
+ test_expect_code 1 git p4 submit --conflict=skip >out &&
+ test_i18ngrep "Applied only the commits" out
+ )
+'
+
+test_expect_success 'conflict on first of two commits, --conflict=quit' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$cli" &&
+ p4 open file1 &&
+ echo line11 >>file1 &&
+ p4 submit -d "line11 in file1"
+ ) &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ # this submit should cause a conflict
+ echo line12 >>file1 &&
+ git add file1 &&
+ git commit -m "line12 in file1 will conflict" &&
+ # but this commit is okay
+ test_commit "okay_commit_after_auto_quit" &&
+ test_expect_code 1 git p4 submit --conflict=quit >out &&
+ test_i18ngrep "No commits applied" out
+ )
+'
+
+#
+# Cleanup after submit fail, all cases. Some modifications happen
+# before trying to apply the patch. Make sure these are unwound
+# properly. Put each one in a diff along with something that will
+# obviously conflict. Make sure it is back to normal after.
+#
+
+test_expect_success 'cleanup edit p4 populate' '
+ (
+ cd "$cli" &&
+ echo text file >text &&
+ p4 add text &&
+ echo text+x file >text+x &&
+ chmod 755 text+x &&
+ p4 add text+x &&
+ p4 submit -d "populate p4"
+ )
+'
+
+setup_conflict() {
+ # clone before modifying file1 to force it to conflict
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ # ticks outside subshells
+ test_tick &&
+ (
+ cd "$cli" &&
+ p4 open file1 &&
+ echo $test_tick >>file1 &&
+ p4 submit -d "$test_tick in file1"
+ ) &&
+ test_tick &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ # easy conflict
+ echo $test_tick >>file1 &&
+ git add file1
+ # caller will add more and submit
+ )
+}
+
+test_expect_success 'cleanup edit after submit fail' '
+ setup_conflict &&
+ (
+ cd "$git" &&
+ echo another line >>text &&
+ git add text &&
+ git commit -m "conflict" &&
+ test_expect_code 1 git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ # make sure it is not open
+ ! p4 fstat -T action text
+ )
+'
+
+test_expect_success 'cleanup add after submit fail' '
+ setup_conflict &&
+ (
+ cd "$git" &&
+ echo new file >textnew &&
+ git add textnew &&
+ git commit -m "conflict" &&
+ test_expect_code 1 git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ # make sure it is not there
+ # and that p4 thinks it is not added
+ # P4 returns 0 both for "not there but added" and
+ # "not there", so grep.
+ test_path_is_missing textnew &&
+ p4 fstat -T action textnew 2>&1 | grep "no such file"
+ )
+'
+
+test_expect_success 'cleanup delete after submit fail' '
+ setup_conflict &&
+ (
+ cd "$git" &&
+ git rm text+x &&
+ git commit -m "conflict" &&
+ test_expect_code 1 git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ # make sure it is there
+ test_path_is_file text+x &&
+ ! p4 fstat -T action text+x
+ )
+'
+
+test_expect_success 'cleanup copy after submit fail' '
+ setup_conflict &&
+ (
+ cd "$git" &&
+ cp text text2 &&
+ git add text2 &&
+ git commit -m "conflict" &&
+ git config git-p4.detectCopies true &&
+ git config git-p4.detectCopiesHarder true &&
+ # make sure setup is okay
+ git diff-tree -r -C --find-copies-harder HEAD | grep text2 | grep C100 &&
+ test_expect_code 1 git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_missing text2 &&
+ p4 fstat -T action text2 2>&1 | grep "no such file"
+ )
+'
+
+test_expect_success 'cleanup rename after submit fail' '
+ setup_conflict &&
+ (
+ cd "$git" &&
+ git mv text text2 &&
+ git commit -m "conflict" &&
+ git config git-p4.detectRenames true &&
+ # make sure setup is okay
+ git diff-tree -r -M HEAD | grep text2 | grep R100 &&
+ test_expect_code 1 git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_missing text2 &&
+ p4 fstat -T action text2 2>&1 | grep "no such file"
+ )
+'
+
+#
+# Cleanup after deciding not to submit during editTemplate. This
+# involves unwinding more work, because files have been added, deleted
+# and chmod-ed now. Same approach as above.
+#
+
+test_expect_success 'cleanup edit after submit cancel' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo line >>text &&
+ git add text &&
+ git commit -m text &&
+ echo n | test_expect_code 1 git p4 submit &&
+ git reset --hard HEAD^
+ ) &&
+ (
+ cd "$cli" &&
+ ! p4 fstat -T action text &&
+ test_cmp "$git"/text text
+ )
+'
+
+test_expect_success 'cleanup add after submit cancel' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo line >textnew &&
+ git add textnew &&
+ git commit -m textnew &&
+ echo n | test_expect_code 1 git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_missing textnew &&
+ p4 fstat -T action textnew 2>&1 | grep "no such file"
+ )
+'
+
+test_expect_success 'cleanup delete after submit cancel' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git rm text &&
+ git commit -m "rm text" &&
+ echo n | test_expect_code 1 git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_file text &&
+ ! p4 fstat -T action text
+ )
+'
+
+test_expect_success 'cleanup copy after submit cancel' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ cp text text2 &&
+ git add text2 &&
+ git commit -m text2 &&
+ git config git-p4.detectCopies true &&
+ git config git-p4.detectCopiesHarder true &&
+ git diff-tree -r -C --find-copies-harder HEAD | grep text2 | grep C100 &&
+ echo n | test_expect_code 1 git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_missing text2 &&
+ p4 fstat -T action text2 2>&1 | grep "no such file"
+ )
+'
+
+test_expect_success 'cleanup rename after submit cancel' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git mv text text2 &&
+ git commit -m text2 &&
+ git config git-p4.detectRenames true &&
+ git diff-tree -r -M HEAD | grep text2 | grep R100 &&
+ echo n | test_expect_code 1 git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_missing text2 &&
+ p4 fstat -T action text2 2>&1 | grep "no such file"
+ test_path_is_file text &&
+ ! p4 fstat -T action text
+ )
+'
+
+test_expect_success 'cleanup chmod after submit cancel' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ test_chmod +x text &&
+ test_chmod -x text+x &&
+ git add text text+x &&
+ git commit -m "chmod texts" &&
+ echo n | test_expect_code 1 git p4 submit
+ ) &&
+ (
+ cd "$cli" &&
+ test_path_is_file text &&
+ ! p4 fstat -T action text &&
+ test_path_is_file text+x &&
+ ! p4 fstat -T action text+x &&
+ ls -l text | egrep ^-r-- &&
+ ls -l text+x | egrep ^-r-x
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9816-git-p4-locked.sh b/t/t9816-git-p4-locked.sh
new file mode 100755
index 0000000..d048bd3
--- /dev/null
+++ b/t/t9816-git-p4-locked.sh
@@ -0,0 +1,145 @@
+#!/bin/sh
+
+test_description='git p4 locked file behavior'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+# See
+# http://www.perforce.com/perforce/doc.current/manuals/p4sag/03_superuser.html#1088563
+# for suggestions on how to configure "sitewide pessimistic locking"
+# where only one person can have a file open for edit at a time.
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+ echo "TypeMap: +l //depot/..." | p4 typemap -i &&
+ echo file1 >file1 &&
+ p4 add file1 &&
+ p4 submit -d "add file1"
+ )
+'
+
+test_expect_success 'edit with lock not taken' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo line2 >>file1 &&
+ git add file1 &&
+ git commit -m "line2 in file1" &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit
+ )
+'
+
+test_expect_success 'add with lock not taken' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo line1 >>add-lock-not-taken &&
+ git add add-lock-not-taken &&
+ git commit -m "add add-lock-not-taken" &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit --verbose
+ )
+'
+
+lock_in_another_client() {
+ # build a different client
+ cli2="$TRASH_DIRECTORY/cli2" &&
+ mkdir -p "$cli2" &&
+ test_when_finished "p4 client -f -d client2 && rm -rf \"$cli2\"" &&
+ (
+ cd "$cli2" &&
+ P4CLIENT=client2 &&
+ cli="$cli2" &&
+ client_view "//depot/... //client2/..." &&
+ p4 sync &&
+ p4 open file1
+ )
+}
+
+test_expect_failure 'edit with lock taken' '
+ lock_in_another_client &&
+ test_when_finished cleanup_git &&
+ test_when_finished "cd \"$cli\" && p4 sync -f file1" &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo line3 >>file1 &&
+ git add file1 &&
+ git commit -m "line3 in file1" &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit --verbose
+ )
+'
+
+test_expect_failure 'delete with lock taken' '
+ lock_in_another_client &&
+ test_when_finished cleanup_git &&
+ test_when_finished "cd \"$cli\" && p4 sync -f file1" &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git rm file1 &&
+ git commit -m "delete file1" &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit --verbose
+ )
+'
+
+test_expect_failure 'chmod with lock taken' '
+ lock_in_another_client &&
+ test_when_finished cleanup_git &&
+ test_when_finished "cd \"$cli\" && p4 sync -f file1" &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ chmod +x file1 &&
+ git add file1 &&
+ git commit -m "chmod +x file1" &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit --verbose
+ )
+'
+
+test_expect_success 'copy with lock taken' '
+ lock_in_another_client &&
+ test_when_finished cleanup_git &&
+ test_when_finished "cd \"$cli\" && p4 revert file2 && rm -f file2" &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ cp file1 file2 &&
+ git add file2 &&
+ git commit -m "cp file1 to file2" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.detectCopies true &&
+ git p4 submit --verbose
+ )
+'
+
+test_expect_failure 'move with lock taken' '
+ lock_in_another_client &&
+ test_when_finished cleanup_git &&
+ test_when_finished "cd \"$cli\" && p4 sync file1 && rm -f file2" &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ git mv file1 file3 &&
+ git commit -m "mv file1 to file3" &&
+ git config git-p4.skipSubmitEdit true &&
+ git config git-p4.detectRenames true &&
+ git p4 submit --verbose
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9817-git-p4-exclude.sh b/t/t9817-git-p4-exclude.sh
new file mode 100755
index 0000000..aac568e
--- /dev/null
+++ b/t/t9817-git-p4-exclude.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+test_description='git p4 tests for excluded paths during clone and sync'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+# Create a repo with the structure:
+#
+# //depot/wanted/foo
+# //depot/discard/foo
+#
+# Check that we can exclude a subdirectory with both
+# clone and sync operations.
+
+test_expect_success 'create exclude repo' '
+ (
+ cd "$cli" &&
+ mkdir -p wanted discard &&
+ echo wanted >wanted/foo &&
+ echo discard >discard/foo &&
+ p4 add wanted/foo discard/foo &&
+ p4 submit -d "initial revision"
+ )
+'
+
+test_expect_success 'check the repo was created correctly' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot/...@all &&
+ (
+ cd "$git" &&
+ test_path_is_file wanted/foo &&
+ test_path_is_file discard/foo
+ )
+'
+
+test_expect_success 'clone, excluding part of repo' '
+ test_when_finished cleanup_git &&
+ git p4 clone -//depot/discard/... --dest="$git" //depot/...@all &&
+ (
+ cd "$git" &&
+ test_path_is_file wanted/foo &&
+ test_path_is_missing discard/foo
+ )
+'
+
+test_expect_success 'clone, then sync with exclude' '
+ test_when_finished cleanup_git &&
+ git p4 clone -//depot/discard/... --dest="$git" //depot/...@all &&
+ (
+ cd "$cli" &&
+ p4 edit wanted/foo discard/foo &&
+ date >>wanted/foo &&
+ date >>discard/foo &&
+ p4 submit -d "updating" &&
+
+ cd "$git" &&
+ git p4 sync -//depot/discard/... &&
+ test_path_is_file wanted/foo &&
+ test_path_is_missing discard/foo
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9818-git-p4-block.sh b/t/t9818-git-p4-block.sh
new file mode 100755
index 0000000..8840a18
--- /dev/null
+++ b/t/t9818-git-p4-block.sh
@@ -0,0 +1,145 @@
+#!/bin/sh
+
+test_description='git p4 fetching changes in multiple blocks'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+create_restricted_group() {
+ p4 group -i <<-EOF
+ Group: restricted
+ MaxResults: 7
+ MaxScanRows: 40
+ Users: author
+ EOF
+}
+
+test_expect_success 'Create group with limited maxrows' '
+ create_restricted_group
+'
+
+test_expect_success 'Create a repo with many changes' '
+ (
+ client_view "//depot/included/... //client/included/..." \
+ "//depot/excluded/... //client/excluded/..." &&
+ mkdir -p "$cli/included" "$cli/excluded" &&
+ cd "$cli/included" &&
+ >file.txt &&
+ p4 add file.txt &&
+ p4 submit -d "Add file.txt" &&
+ for i in $(test_seq 0 5)
+ do
+ >outer$i.txt &&
+ p4 add outer$i.txt &&
+ p4 submit -d "Adding outer$i.txt" &&
+ for j in $(test_seq 0 5)
+ do
+ p4 edit file.txt &&
+ echo $i$j >file.txt &&
+ p4 submit -d "Commit $i$j" || exit
+ done || exit
+ done
+ )
+'
+
+test_expect_success 'Default user cannot fetch changes' '
+ ! p4 changes -m 1 //depot/...
+'
+
+test_expect_success 'Clone the repo' '
+ git p4 clone --dest="$git" --changes-block-size=7 --verbose //depot/included@all
+'
+
+test_expect_success 'All files are present' '
+ echo file.txt >expected &&
+ test_write_lines outer0.txt outer1.txt outer2.txt outer3.txt outer4.txt >>expected &&
+ test_write_lines outer5.txt >>expected &&
+ ls "$git" >current &&
+ test_cmp expected current
+'
+
+test_expect_success 'file.txt is correct' '
+ echo 55 >expected &&
+ test_cmp expected "$git/file.txt"
+'
+
+test_expect_success 'Correct number of commits' '
+ (cd "$git" && git log --oneline) >log &&
+ wc -l log &&
+ test_line_count = 43 log
+'
+
+test_expect_success 'Previous version of file.txt is correct' '
+ (cd "$git" && git checkout HEAD^^) &&
+ echo 53 >expected &&
+ test_cmp expected "$git/file.txt"
+'
+
+# Test git-p4 sync, with some files outside the client specification.
+
+p4_add_file() {
+ (cd "$cli" &&
+ >$1 &&
+ p4 add $1 &&
+ p4 submit -d "Added file $1" $1
+ )
+}
+
+test_expect_success 'Add some more files' '
+ for i in $(test_seq 0 10)
+ do
+ p4_add_file "included/x$i" &&
+ p4_add_file "excluded/x$i"
+ done &&
+ for i in $(test_seq 0 10)
+ do
+ p4_add_file "excluded/y$i"
+ done
+'
+
+# This should pick up the 10 new files in "included", but not be confused
+# by the additional files in "excluded"
+test_expect_success 'Syncing files' '
+ (
+ cd "$git" &&
+ git p4 sync --changes-block-size=7 &&
+ git checkout p4/master &&
+ ls -l x* > log &&
+ test_line_count = 11 log
+ )
+'
+
+# Handling of multiple depot paths:
+# git p4 clone //depot/pathA //depot/pathB
+#
+test_expect_success 'Create a repo with multiple depot paths' '
+ client_view "//depot/pathA/... //client/pathA/..." \
+ "//depot/pathB/... //client/pathB/..." &&
+ mkdir -p "$cli/pathA" "$cli/pathB" &&
+ for p in pathA pathB
+ do
+ for i in $(test_seq 1 10)
+ do
+ p4_add_file "$p/file$p$i"
+ done
+ done
+'
+
+test_expect_success 'Clone repo with multiple depot paths' '
+ (
+ cd "$git" &&
+ git p4 clone --changes-block-size=4 //depot/pathA@all //depot/pathB@all \
+ --destination=dest &&
+ ls -1 dest >log &&
+ test_line_count = 20 log
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9819-git-p4-case-folding.sh b/t/t9819-git-p4-case-folding.sh
new file mode 100755
index 0000000..d808c00
--- /dev/null
+++ b/t/t9819-git-p4-case-folding.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='interaction with P4 case-folding'
+
+. ./lib-git-p4.sh
+
+if test_have_prereq CASE_INSENSITIVE_FS
+then
+ skip_all='skipping P4 case-folding tests; case insensitive file system detected'
+ test_done
+fi
+
+test_expect_success 'start p4d with case folding enabled' '
+ start_p4d -C1
+'
+
+test_expect_success 'Create a repo, name is lowercase' '
+ (
+ client_view "//depot/... //client/..." &&
+ cd "$cli" &&
+ mkdir -p lc UC &&
+ >lc/file.txt && >UC/file.txt &&
+ p4 add lc/file.txt UC/file.txt &&
+ p4 submit -d "Add initial lc and UC repos"
+ )
+'
+
+test_expect_success 'Check p4 is in case-folding mode' '
+ (
+ cd "$cli" &&
+ >lc/FILE.TXT &&
+ p4 add lc/FILE.TXT &&
+ test_must_fail p4 submit -d "Cannot add file differing only in case" lc/FILE.TXT
+ )
+'
+
+# Check we created the repo properly
+test_expect_success 'Clone lc repo using lc name' '
+ git p4 clone //depot/lc/... &&
+ test_path_is_file lc/file.txt &&
+ git p4 clone //depot/UC/... &&
+ test_path_is_file UC/file.txt
+'
+
+# The clone should fail, since there is no repo called LC, but because
+# we have case-insensitive p4d enabled, it appears to go ahead and work,
+# but leaves an empty git repo in place.
+test_expect_failure 'Clone lc repo using uc name' '
+ test_must_fail git p4 clone //depot/LC/...
+'
+
+test_expect_failure 'Clone UC repo with lc name' '
+ test_must_fail git p4 clone //depot/uc/...
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9820-git-p4-editor-handling.sh b/t/t9820-git-p4-editor-handling.sh
new file mode 100755
index 0000000..6dc6df0
--- /dev/null
+++ b/t/t9820-git-p4-editor-handling.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='git p4 handling of EDITOR'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+ echo file1 >file1 &&
+ p4 add file1 &&
+ p4 submit -d "file1"
+ )
+'
+
+# Check that the P4EDITOR argument can be given command-line
+# options, which git-p4 will then pass through to the shell.
+test_expect_success 'EDITOR with options' '
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ echo change >file1 &&
+ git commit -m "change" file1 &&
+ P4EDITOR=": >\"$git/touched\" && test-chmtime +5" git p4 submit &&
+ test_path_is_file "$git/touched"
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9821-git-p4-path-variations.sh b/t/t9821-git-p4-path-variations.sh
new file mode 100755
index 0000000..81e46ac
--- /dev/null
+++ b/t/t9821-git-p4-path-variations.sh
@@ -0,0 +1,200 @@
+#!/bin/sh
+
+test_description='Clone repositories with path case variations'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d with case folding enabled' '
+ start_p4d -C1
+'
+
+test_expect_success 'Create a repo with path case variations' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+
+ mkdir -p Path/to &&
+ >Path/to/File2.txt &&
+ p4 add Path/to/File2.txt &&
+ p4 submit -d "Add file2" &&
+ rm -rf Path &&
+
+ mkdir -p path/TO &&
+ >path/TO/file1.txt &&
+ p4 add path/TO/file1.txt &&
+ p4 submit -d "Add file1" &&
+ rm -rf path &&
+
+ mkdir -p path/to &&
+ >path/to/file3.txt &&
+ p4 add path/to/file3.txt &&
+ p4 submit -d "Add file3" &&
+ rm -rf path &&
+
+ mkdir -p x-outside-spec &&
+ >x-outside-spec/file4.txt &&
+ p4 add x-outside-spec/file4.txt &&
+ p4 submit -d "Add file4" &&
+ rm -rf x-outside-spec
+ )
+'
+
+test_expect_success 'Clone root' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config core.ignorecase false &&
+ git p4 clone --use-client-spec --destination="$git" //depot &&
+ # This method is used instead of "test -f" to ensure the case is
+ # checked even if the test is executed on case-insensitive file systems.
+ # All files are there as expected although the path cases look random.
+ cat >expect <<-\EOF &&
+ Path/to/File2.txt
+ path/TO/file1.txt
+ path/to/file3.txt
+ x-outside-spec/file4.txt
+ EOF
+ git ls-files >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'Clone root (ignorecase)' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config core.ignorecase true &&
+ git p4 clone --use-client-spec --destination="$git" //depot &&
+ # This method is used instead of "test -f" to ensure the case is
+ # checked even if the test is executed on case-insensitive file systems.
+ # All files are there as expected although the path cases look random.
+ cat >expect <<-\EOF &&
+ path/TO/File2.txt
+ path/TO/file1.txt
+ path/TO/file3.txt
+ x-outside-spec/file4.txt
+ EOF
+ git ls-files >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'Clone root and ignore one file' '
+ client_view \
+ "//depot/... //client/..." \
+ "-//depot/path/TO/file1.txt //client/path/TO/file1.txt" &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config core.ignorecase false &&
+ git p4 clone --use-client-spec --destination="$git" //depot &&
+ # We ignore one file in the client spec and all path cases change from
+ # "TO" to "to"!
+ cat >expect <<-\EOF &&
+ Path/to/File2.txt
+ path/to/file3.txt
+ x-outside-spec/file4.txt
+ EOF
+ git ls-files >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'Clone root and ignore one file (ignorecase)' '
+ client_view \
+ "//depot/... //client/..." \
+ "-//depot/path/TO/file1.txt //client/path/TO/file1.txt" &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config core.ignorecase true &&
+ git p4 clone --use-client-spec --destination="$git" //depot &&
+ # We ignore one file in the client spec and all path cases change from
+ # "TO" to "to"!
+ cat >expect <<-\EOF &&
+ Path/to/File2.txt
+ Path/to/file3.txt
+ x-outside-spec/file4.txt
+ EOF
+ git ls-files >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'Clone path' '
+ client_view "//depot/Path/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config core.ignorecase false &&
+ git p4 clone --use-client-spec --destination="$git" //depot &&
+ cat >expect <<-\EOF &&
+ to/File2.txt
+ EOF
+ git ls-files >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'Clone path (ignorecase)' '
+ client_view "//depot/Path/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config core.ignorecase true &&
+ git p4 clone --use-client-spec --destination="$git" //depot &&
+ cat >expect <<-\EOF &&
+ TO/File2.txt
+ TO/file1.txt
+ TO/file3.txt
+ EOF
+ git ls-files >actual &&
+ test_cmp expect actual
+ )
+'
+
+# It looks like P4 determines the path case based on the first file in
+# lexicographical order. Please note the lower case "to" directory for all
+# files triggered through the addition of "File0.txt".
+test_expect_success 'Add a new file and clone path with new file (ignorecase)' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+ mkdir -p Path/to &&
+ >Path/to/File0.txt &&
+ p4 add Path/to/File0.txt &&
+ p4 submit -d "Add file" &&
+ rm -rf Path
+ ) &&
+
+ client_view "//depot/Path/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config core.ignorecase true &&
+ git p4 clone --use-client-spec --destination="$git" //depot &&
+ cat >expect <<-\EOF &&
+ to/File0.txt
+ to/File2.txt
+ to/file1.txt
+ to/file3.txt
+ EOF
+ git ls-files >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9822-git-p4-path-encoding.sh b/t/t9822-git-p4-path-encoding.sh
new file mode 100755
index 0000000..7b83e69
--- /dev/null
+++ b/t/t9822-git-p4-path-encoding.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+test_description='Clone repositories with non ASCII paths'
+
+. ./lib-git-p4.sh
+
+UTF8_ESCAPED="a-\303\244_o-\303\266_u-\303\274.txt"
+ISO8859_ESCAPED="a-\344_o-\366_u-\374.txt"
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'Create a repo containing iso8859-1 encoded paths' '
+ (
+ cd "$cli" &&
+ ISO8859="$(printf "$ISO8859_ESCAPED")" &&
+ echo content123 >"$ISO8859" &&
+ p4 add "$ISO8859" &&
+ p4 submit -d "test commit"
+ )
+'
+
+test_expect_failure 'Clone auto-detects depot with iso8859-1 paths' '
+ git p4 clone --destination="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ UTF8="$(printf "$UTF8_ESCAPED")" &&
+ echo "$UTF8" >expect &&
+ git -c core.quotepath=false ls-files >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'Clone repo containing iso8859-1 encoded paths with git-p4.pathEncoding' '
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.pathEncoding iso8859-1 &&
+ git p4 clone --use-client-spec --destination="$git" //depot &&
+ UTF8="$(printf "$UTF8_ESCAPED")" &&
+ echo "$UTF8" >expect &&
+ git -c core.quotepath=false ls-files >actual &&
+ test_cmp expect actual &&
+
+ echo content123 >expect &&
+ cat "$UTF8" >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9823-git-p4-mock-lfs.sh b/t/t9823-git-p4-mock-lfs.sh
new file mode 100755
index 0000000..1f2dc36
--- /dev/null
+++ b/t/t9823-git-p4-mock-lfs.sh
@@ -0,0 +1,192 @@
+#!/bin/sh
+
+test_description='Clone repositories and store files in Mock LFS'
+
+. ./lib-git-p4.sh
+
+test_file_is_not_in_mock_lfs () {
+ FILE="$1" &&
+ CONTENT="$2" &&
+ echo "$CONTENT" >expect_content &&
+ test_path_is_file "$FILE" &&
+ test_cmp expect_content "$FILE"
+}
+
+test_file_is_in_mock_lfs () {
+ FILE="$1" &&
+ CONTENT="$2" &&
+ LOCAL_STORAGE=".git/mock-storage/local/$CONTENT" &&
+ SERVER_STORAGE=".git/mock-storage/remote/$CONTENT" &&
+ echo "pointer-$CONTENT" >expect_pointer &&
+ echo "$CONTENT" >expect_content &&
+ test_path_is_file "$FILE" &&
+ test_path_is_file "$LOCAL_STORAGE" &&
+ test_path_is_file "$SERVER_STORAGE" &&
+ test_cmp expect_pointer "$FILE" &&
+ test_cmp expect_content "$LOCAL_STORAGE" &&
+ test_cmp expect_content "$SERVER_STORAGE"
+}
+
+test_file_is_deleted_in_mock_lfs () {
+ FILE="$1" &&
+ CONTENT="$2" &&
+ LOCAL_STORAGE=".git/mock-storage/local/$CONTENT" &&
+ SERVER_STORAGE=".git/mock-storage/remote/$CONTENT" &&
+ echo "pointer-$CONTENT" >expect_pointer &&
+ echo "$CONTENT" >expect_content &&
+ test_path_is_missing "$FILE" &&
+ test_path_is_file "$LOCAL_STORAGE" &&
+ test_path_is_file "$SERVER_STORAGE" &&
+ test_cmp expect_content "$LOCAL_STORAGE" &&
+ test_cmp expect_content "$SERVER_STORAGE"
+}
+
+test_file_count_in_dir () {
+ DIR="$1" &&
+ EXPECTED_COUNT="$2" &&
+ find "$DIR" -type f >actual &&
+ test_line_count = $EXPECTED_COUNT actual
+}
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'Create repo with binary files' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+
+ echo "content 1 txt 23 bytes" >file1.txt &&
+ p4 add file1.txt &&
+ echo "content 2-3 bin 25 bytes" >file2.dat &&
+ p4 add file2.dat &&
+ p4 submit -d "Add text and binary file" &&
+
+ mkdir "path with spaces" &&
+ echo "content 2-3 bin 25 bytes" >"path with spaces/file3.bin" &&
+ p4 add "path with spaces/file3.bin" &&
+ p4 submit -d "Add another binary file with same content and spaces in path" &&
+
+ echo "content 4 bin 26 bytes XX" >file4.bin &&
+ p4 add file4.bin &&
+ p4 submit -d "Add another binary file with different content"
+ )
+'
+
+test_expect_success 'Store files in Mock LFS based on size (>24 bytes)' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem MockLFS &&
+ git config git-p4.largeFileThreshold 24 &&
+ git config git-p4.largeFilePush True &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_is_not_in_mock_lfs file1.txt "content 1 txt 23 bytes" &&
+ test_file_is_in_mock_lfs file2.dat "content 2-3 bin 25 bytes" &&
+ test_file_is_in_mock_lfs "path with spaces/file3.bin" "content 2-3 bin 25 bytes" &&
+ test_file_is_in_mock_lfs file4.bin "content 4 bin 26 bytes XX" &&
+
+ test_file_count_in_dir ".git/mock-storage/local" 2 &&
+ test_file_count_in_dir ".git/mock-storage/remote" 2
+ )
+'
+
+test_expect_success 'Store files in Mock LFS based on extension (dat)' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem MockLFS &&
+ git config git-p4.largeFileExtensions dat &&
+ git config git-p4.largeFilePush True &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_is_not_in_mock_lfs file1.txt "content 1 txt 23 bytes" &&
+ test_file_is_in_mock_lfs file2.dat "content 2-3 bin 25 bytes" &&
+ test_file_is_not_in_mock_lfs "path with spaces/file3.bin" "content 2-3 bin 25 bytes" &&
+ test_file_is_not_in_mock_lfs file4.bin "content 4 bin 26 bytes XX" &&
+
+ test_file_count_in_dir ".git/mock-storage/local" 1 &&
+ test_file_count_in_dir ".git/mock-storage/remote" 1
+ )
+'
+
+test_expect_success 'Store files in Mock LFS based on extension (dat) and use git p4 sync and no client spec' '
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem MockLFS &&
+ git config git-p4.largeFileExtensions dat &&
+ git config git-p4.largeFilePush True &&
+ git p4 sync //depot &&
+ git checkout p4/master &&
+
+ test_file_is_not_in_mock_lfs file1.txt "content 1 txt 23 bytes" &&
+ test_file_is_in_mock_lfs file2.dat "content 2-3 bin 25 bytes" &&
+ test_file_is_not_in_mock_lfs "path with spaces/file3.bin" "content 2-3 bin 25 bytes" &&
+ test_file_is_not_in_mock_lfs file4.bin "content 4 bin 26 bytes XX" &&
+
+ test_file_count_in_dir ".git/mock-storage/local" 1 &&
+ test_file_count_in_dir ".git/mock-storage/remote" 1
+ )
+'
+
+test_expect_success 'Remove file from repo and store files in Mock LFS based on size (>24 bytes)' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+ p4 delete file4.bin &&
+ p4 submit -d "Remove file"
+ ) &&
+
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem MockLFS &&
+ git config git-p4.largeFileThreshold 24 &&
+ git config git-p4.largeFilePush True &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_is_not_in_mock_lfs file1.txt "content 1 txt 23 bytes" &&
+ test_file_is_in_mock_lfs file2.dat "content 2-3 bin 25 bytes" &&
+ test_file_is_in_mock_lfs "path with spaces/file3.bin" "content 2-3 bin 25 bytes" &&
+ test_file_is_deleted_in_mock_lfs file4.bin "content 4 bin 26 bytes XX" &&
+
+ test_file_count_in_dir ".git/mock-storage/local" 2 &&
+ test_file_count_in_dir ".git/mock-storage/remote" 2
+ )
+'
+
+test_expect_success 'Run git p4 submit in repo configured with large file system' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem MockLFS &&
+ git config git-p4.largeFileThreshold 24 &&
+ git config git-p4.largeFilePush True &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_must_fail git p4 submit
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9824-git-p4-git-lfs.sh b/t/t9824-git-p4-git-lfs.sh
new file mode 100755
index 0000000..110a7e7
--- /dev/null
+++ b/t/t9824-git-p4-git-lfs.sh
@@ -0,0 +1,292 @@
+#!/bin/sh
+
+test_description='Clone repositories and store files in Git LFS'
+
+. ./lib-git-p4.sh
+
+git lfs help >/dev/null 2>&1 || {
+ skip_all='skipping git p4 Git LFS tests; Git LFS not found'
+ test_done
+}
+
+test_file_in_lfs () {
+ FILE="$1" &&
+ SIZE="$2" &&
+ EXPECTED_CONTENT="$3" &&
+ sed -n '1,1 p' "$FILE" | grep "^version " &&
+ sed -n '2,2 p' "$FILE" | grep "^oid " &&
+ sed -n '3,3 p' "$FILE" | grep "^size " &&
+ test_line_count = 3 "$FILE" &&
+ cat "$FILE" | grep "size $SIZE" &&
+ HASH=$(cat "$FILE" | grep "oid sha256:" | sed -e "s/oid sha256://g") &&
+ LFS_FILE=".git/lfs/objects/$(echo "$HASH" | cut -c1-2)/$(echo "$HASH" | cut -c3-4)/$HASH" &&
+ echo $EXPECTED_CONTENT >expect &&
+ test_path_is_file "$FILE" &&
+ test_path_is_file "$LFS_FILE" &&
+ test_cmp expect "$LFS_FILE"
+}
+
+test_file_count_in_dir () {
+ DIR="$1" &&
+ EXPECTED_COUNT="$2" &&
+ find "$DIR" -type f >actual &&
+ test_line_count = $EXPECTED_COUNT actual
+}
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'Create repo with binary files' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+
+ echo "content 1 txt 23 bytes" >file1.txt &&
+ p4 add file1.txt &&
+ echo "content 2-3 bin 25 bytes" >file2.dat &&
+ p4 add file2.dat &&
+ p4 submit -d "Add text and binary file" &&
+
+ mkdir "path with spaces" &&
+ echo "content 2-3 bin 25 bytes" >"path with spaces/file3.bin" &&
+ p4 add "path with spaces/file3.bin" &&
+ p4 submit -d "Add another binary file with same content and spaces in path" &&
+
+ echo "content 4 bin 26 bytes XX" >file4.bin &&
+ p4 add file4.bin &&
+ p4 submit -d "Add another binary file with different content"
+ )
+'
+
+test_expect_success 'Store files in LFS based on size (>24 bytes)' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem GitLFS &&
+ git config git-p4.largeFileThreshold 24 &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_in_lfs file2.dat 25 "content 2-3 bin 25 bytes" &&
+ test_file_in_lfs "path with spaces/file3.bin" 25 "content 2-3 bin 25 bytes" &&
+ test_file_in_lfs file4.bin 26 "content 4 bin 26 bytes XX" &&
+
+ test_file_count_in_dir ".git/lfs/objects" 2 &&
+
+ cat >expect <<-\EOF &&
+
+ #
+ # Git LFS (see https://git-lfs.github.com/)
+ #
+ /file2.dat filter=lfs -text
+ /file4.bin filter=lfs -text
+ /path[[:space:]]with[[:space:]]spaces/file3.bin filter=lfs -text
+ EOF
+ test_path_is_file .gitattributes &&
+ test_cmp expect .gitattributes
+ )
+'
+
+test_expect_success 'Store files in LFS based on size (>25 bytes)' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem GitLFS &&
+ git config git-p4.largeFileThreshold 25 &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_in_lfs file4.bin 26 "content 4 bin 26 bytes XX" &&
+ test_file_count_in_dir ".git/lfs/objects" 1 &&
+
+ cat >expect <<-\EOF &&
+
+ #
+ # Git LFS (see https://git-lfs.github.com/)
+ #
+ /file4.bin filter=lfs -text
+ EOF
+ test_path_is_file .gitattributes &&
+ test_cmp expect .gitattributes
+ )
+'
+
+test_expect_success 'Store files in LFS based on extension (dat)' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem GitLFS &&
+ git config git-p4.largeFileExtensions dat &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_in_lfs file2.dat 25 "content 2-3 bin 25 bytes" &&
+ test_file_count_in_dir ".git/lfs/objects" 1 &&
+
+ cat >expect <<-\EOF &&
+
+ #
+ # Git LFS (see https://git-lfs.github.com/)
+ #
+ *.dat filter=lfs -text
+ EOF
+ test_path_is_file .gitattributes &&
+ test_cmp expect .gitattributes
+ )
+'
+
+test_expect_success 'Store files in LFS based on size (>25 bytes) and extension (dat)' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem GitLFS &&
+ git config git-p4.largeFileExtensions dat &&
+ git config git-p4.largeFileThreshold 25 &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_in_lfs file2.dat 25 "content 2-3 bin 25 bytes" &&
+ test_file_in_lfs file4.bin 26 "content 4 bin 26 bytes XX" &&
+ test_file_count_in_dir ".git/lfs/objects" 2 &&
+
+ cat >expect <<-\EOF &&
+
+ #
+ # Git LFS (see https://git-lfs.github.com/)
+ #
+ *.dat filter=lfs -text
+ /file4.bin filter=lfs -text
+ EOF
+ test_path_is_file .gitattributes &&
+ test_cmp expect .gitattributes
+ )
+'
+
+test_expect_success 'Remove file from repo and store files in LFS based on size (>24 bytes)' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+ p4 delete file4.bin &&
+ p4 submit -d "Remove file"
+ ) &&
+
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem GitLFS &&
+ git config git-p4.largeFileThreshold 24 &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_in_lfs file2.dat 25 "content 2-3 bin 25 bytes" &&
+ test_file_in_lfs "path with spaces/file3.bin" 25 "content 2-3 bin 25 bytes" &&
+ test_path_is_missing file4.bin &&
+ test_file_count_in_dir ".git/lfs/objects" 2 &&
+
+ cat >expect <<-\EOF &&
+
+ #
+ # Git LFS (see https://git-lfs.github.com/)
+ #
+ /file2.dat filter=lfs -text
+ /path[[:space:]]with[[:space:]]spaces/file3.bin filter=lfs -text
+ EOF
+ test_path_is_file .gitattributes &&
+ test_cmp expect .gitattributes
+ )
+'
+
+test_expect_success 'Add .gitattributes and store files in LFS based on size (>24 bytes)' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+ echo "*.txt text" >.gitattributes &&
+ p4 add .gitattributes &&
+ p4 submit -d "Add .gitattributes"
+ ) &&
+
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem GitLFS &&
+ git config git-p4.largeFileThreshold 24 &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_in_lfs file2.dat 25 "content 2-3 bin 25 bytes" &&
+ test_file_in_lfs "path with spaces/file3.bin" 25 "content 2-3 bin 25 bytes" &&
+ test_path_is_missing file4.bin &&
+ test_file_count_in_dir ".git/lfs/objects" 2 &&
+
+ cat >expect <<-\EOF &&
+ *.txt text
+
+ #
+ # Git LFS (see https://git-lfs.github.com/)
+ #
+ /file2.dat filter=lfs -text
+ /path[[:space:]]with[[:space:]]spaces/file3.bin filter=lfs -text
+ EOF
+ test_path_is_file .gitattributes &&
+ test_cmp expect .gitattributes
+ )
+'
+
+test_expect_success 'Add big files to repo and store files in LFS based on compressed size (>28 bytes)' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+ echo "content 5 bin 40 bytes XXXXXXXXXXXXXXXX" >file5.bin &&
+ p4 add file5.bin &&
+ p4 submit -d "Add file with small footprint after compression" &&
+
+ echo "content 6 bin 39 bytes XXXXXYYYYYZZZZZ" >file6.bin &&
+ p4 add file6.bin &&
+ p4 submit -d "Add file with large footprint after compression"
+ ) &&
+
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem GitLFS &&
+ git config git-p4.largeFileCompressedThreshold 28 &&
+ # We only import HEAD here ("@all" is missing!)
+ git p4 clone --destination="$git" //depot &&
+
+ test_file_in_lfs file6.bin 39 "content 6 bin 39 bytes XXXXXYYYYYZZZZZ" &&
+ test_file_count_in_dir ".git/lfs/objects" 1 &&
+
+ cat >expect <<-\EOF &&
+ *.txt text
+
+ #
+ # Git LFS (see https://git-lfs.github.com/)
+ #
+ /file6.bin filter=lfs -text
+ EOF
+ test_path_is_file .gitattributes &&
+ test_cmp expect .gitattributes
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9825-git-p4-handle-utf16-without-bom.sh b/t/t9825-git-p4-handle-utf16-without-bom.sh
new file mode 100755
index 0000000..1551845
--- /dev/null
+++ b/t/t9825-git-p4-handle-utf16-without-bom.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='git p4 handling of UTF-16 files without BOM'
+
+. ./lib-git-p4.sh
+
+UTF16="\227\000\227\000"
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'init depot with UTF-16 encoded file and artificially remove BOM' '
+ (
+ cd "$cli" &&
+ printf "$UTF16" >file1 &&
+ p4 add -t utf16 file1 &&
+ p4 submit -d "file1"
+ ) &&
+
+ (
+ cd db &&
+ p4d -jc &&
+ # P4D automatically adds a BOM. Remove it here to make the file invalid.
+ sed -e "\$d" depot/file1,v >depot/file1,v.new &&
+ mv depot/file1,v.new depot/file1,v &&
+ printf "@$UTF16@" >>depot/file1,v &&
+ p4d -jrF checkpoint.1
+ )
+'
+
+test_expect_success 'clone depot with invalid UTF-16 file in verbose mode' '
+ git p4 clone --dest="$git" --verbose //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ printf "$UTF16" >expect &&
+ test_cmp_bin expect file1
+ )
+'
+
+test_expect_failure 'clone depot with invalid UTF-16 file in non-verbose mode' '
+ git p4 clone --dest="$git" //depot
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9826-git-p4-keep-empty-commits.sh b/t/t9826-git-p4-keep-empty-commits.sh
new file mode 100755
index 0000000..fa8b9da
--- /dev/null
+++ b/t/t9826-git-p4-keep-empty-commits.sh
@@ -0,0 +1,134 @@
+#!/bin/sh
+
+test_description='Clone repositories and keep empty commits'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'Create a repo' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+
+ mkdir -p subdir &&
+
+ >subdir/file1.txt &&
+ p4 add subdir/file1.txt &&
+ p4 submit -d "Add file 1" &&
+
+ >file2.txt &&
+ p4 add file2.txt &&
+ p4 submit -d "Add file 2" &&
+
+ >subdir/file3.txt &&
+ p4 add subdir/file3.txt &&
+ p4 submit -d "Add file 3" &&
+
+ >file4.txt &&
+ p4 add file4.txt &&
+ p4 submit -d "Add file 4" &&
+
+ p4 delete subdir/file3.txt &&
+ p4 submit -d "Remove file 3" &&
+
+ p4 delete file4.txt &&
+ p4 submit -d "Remove file 4"
+ )
+'
+
+test_expect_success 'Clone repo root path with all history' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git p4 clone --use-client-spec --destination="$git" //depot@all &&
+ cat >expect <<-\EOF &&
+ Remove file 4
+ [git-p4: depot-paths = "//depot/": change = 6]
+
+ Remove file 3
+ [git-p4: depot-paths = "//depot/": change = 5]
+
+ Add file 4
+ [git-p4: depot-paths = "//depot/": change = 4]
+
+ Add file 3
+ [git-p4: depot-paths = "//depot/": change = 3]
+
+ Add file 2
+ [git-p4: depot-paths = "//depot/": change = 2]
+
+ Add file 1
+ [git-p4: depot-paths = "//depot/": change = 1]
+
+ EOF
+ git log --format=%B >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'Clone repo subdir with all history but keep empty commits' '
+ client_view "//depot/subdir/... //client/subdir/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.keepEmptyCommits true &&
+ git p4 clone --use-client-spec --destination="$git" //depot@all &&
+ cat >expect <<-\EOF &&
+ Remove file 4
+ [git-p4: depot-paths = "//depot/": change = 6]
+
+ Remove file 3
+ [git-p4: depot-paths = "//depot/": change = 5]
+
+ Add file 4
+ [git-p4: depot-paths = "//depot/": change = 4]
+
+ Add file 3
+ [git-p4: depot-paths = "//depot/": change = 3]
+
+ Add file 2
+ [git-p4: depot-paths = "//depot/": change = 2]
+
+ Add file 1
+ [git-p4: depot-paths = "//depot/": change = 1]
+
+ EOF
+ git log --format=%B >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'Clone repo subdir with all history' '
+ client_view "//depot/subdir/... //client/subdir/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git p4 clone --use-client-spec --destination="$git" --verbose //depot@all &&
+ cat >expect <<-\EOF &&
+ Remove file 3
+ [git-p4: depot-paths = "//depot/": change = 5]
+
+ Add file 3
+ [git-p4: depot-paths = "//depot/": change = 3]
+
+ Add file 1
+ [git-p4: depot-paths = "//depot/": change = 1]
+
+ EOF
+ git log --format=%B >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9827-git-p4-change-filetype.sh b/t/t9827-git-p4-change-filetype.sh
new file mode 100755
index 0000000..7433998
--- /dev/null
+++ b/t/t9827-git-p4-change-filetype.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+test_description='git p4 support for file type change'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'create files' '
+ (
+ cd "$cli" &&
+ p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i &&
+ cat >file1 <<-EOF &&
+ text without any funny substitution business
+ EOF
+ cat >file2 <<-EOF &&
+ second file whose type will change
+ EOF
+ p4 add file1 file2 &&
+ p4 submit -d "add files"
+ )
+'
+
+test_expect_success SYMLINKS 'change file to symbolic link' '
+ git p4 clone --dest="$git" //depot@all &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+
+ rm file2 &&
+ ln -s file1 file2 &&
+ git add file2 &&
+ git commit -m "symlink file1 to file2" &&
+ git p4 submit &&
+ p4 filelog -m 1 //depot/file2 >filelog &&
+ grep "(symlink)" filelog
+ )
+'
+
+test_expect_success SYMLINKS 'change symbolic link to file' '
+ git p4 clone --dest="$git" //depot@all &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+
+ rm file2 &&
+ cat >file2 <<-EOF &&
+ This is another content for the second file.
+ EOF
+ git add file2 &&
+ git commit -m "re-write file2" &&
+ git p4 submit &&
+ p4 filelog -m 1 //depot/file2 >filelog &&
+ grep "(text)" filelog
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9828-git-p4-map-user.sh b/t/t9828-git-p4-map-user.sh
new file mode 100755
index 0000000..e20395c
--- /dev/null
+++ b/t/t9828-git-p4-map-user.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+test_description='Clone repositories and map users'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'Create a repo with different users' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+
+ >author.txt &&
+ p4 add author.txt &&
+ p4 submit -d "Add file author\\n" &&
+
+ P4USER=mmax &&
+ >max.txt &&
+ p4 add max.txt &&
+ p4 submit -d "Add file max" &&
+
+ P4USER=eri &&
+ >moritz.txt &&
+ p4 add moritz.txt &&
+ p4 submit -d "Add file moritz" &&
+
+ P4USER=no &&
+ >nobody.txt &&
+ p4 add nobody.txt &&
+ p4 submit -d "Add file nobody"
+ )
+'
+
+test_expect_success 'Clone repo root path with all history' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config --add git-p4.mapUser "mmax = Max Musterman <max@example.com> " &&
+ git config --add git-p4.mapUser " eri=Erika Musterman <erika@example.com>" &&
+ git p4 clone --use-client-spec --destination="$git" //depot@all &&
+ cat >expect <<-\EOF &&
+ no <no@client>
+ Erika Musterman <erika@example.com>
+ Max Musterman <max@example.com>
+ Dr. author <author@example.com>
+ EOF
+ git log --format="%an <%ae>" >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9829-git-p4-jobs.sh b/t/t9829-git-p4-jobs.sh
new file mode 100755
index 0000000..971aeee
--- /dev/null
+++ b/t/t9829-git-p4-jobs.sh
@@ -0,0 +1,99 @@
+#!/bin/sh
+
+test_description='git p4 retrieve job info'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'add p4 jobs' '
+ (
+ p4_add_job TESTJOB-A &&
+ p4_add_job TESTJOB-B
+ )
+'
+
+test_expect_success 'add p4 files' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+ >file1 &&
+ p4 add file1 &&
+ p4 submit -d "Add file 1"
+ )
+'
+
+test_expect_success 'check log message of changelist with no jobs' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git p4 clone --use-client-spec --destination="$git" //depot@all &&
+ cat >expect <<-\EOF &&
+ Add file 1
+ [git-p4: depot-paths = "//depot/": change = 1]
+
+ EOF
+ git log --format=%B >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'add TESTJOB-A to change 1' '
+ (
+ cd "$cli" &&
+ p4 fix -c 1 TESTJOB-A
+ )
+'
+
+test_expect_success 'check log message of changelist with one job' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git p4 clone --use-client-spec --destination="$git" //depot@all &&
+ cat >expect <<-\EOF &&
+ Add file 1
+ Jobs: TESTJOB-A
+ [git-p4: depot-paths = "//depot/": change = 1]
+
+ EOF
+ git log --format=%B >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'add TESTJOB-B to change 1' '
+ (
+ cd "$cli" &&
+ p4 fix -c 1 TESTJOB-B
+ )
+'
+
+test_expect_success 'check log message of changelist with more jobs' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git p4 clone --use-client-spec --destination="$git" //depot@all &&
+ cat >expect <<-\EOF &&
+ Add file 1
+ Jobs: TESTJOB-A TESTJOB-B
+ [git-p4: depot-paths = "//depot/": change = 1]
+
+ EOF
+ git log --format=%B >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9901-git-web--browse.sh b/t/t9901-git-web--browse.sh
new file mode 100755
index 0000000..de7152f
--- /dev/null
+++ b/t/t9901-git-web--browse.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+#
+
+test_description='git web--browse basic tests
+
+This test checks that git web--browse can handle various valid URLs.'
+
+. ./test-lib.sh
+
+test_web_browse () {
+ # browser=$1 url=$2
+ git web--browse --browser="$1" "$2" >actual &&
+ tr -d '\015' <actual >text &&
+ test_cmp expect text
+}
+
+test_expect_success \
+ 'URL with an ampersand in it' '
+ echo http://example.com/foo\&bar >expect &&
+ git config browser.custom.cmd echo &&
+ test_web_browse custom http://example.com/foo\&bar
+'
+
+test_expect_success \
+ 'URL with a semi-colon in it' '
+ echo http://example.com/foo\;bar >expect &&
+ git config browser.custom.cmd echo &&
+ test_web_browse custom http://example.com/foo\;bar
+'
+
+test_expect_success \
+ 'URL with a hash in it' '
+ echo http://example.com/foo#bar >expect &&
+ git config browser.custom.cmd echo &&
+ test_web_browse custom http://example.com/foo#bar
+'
+
+test_expect_success \
+ 'browser paths are properly quoted' '
+ echo fake: http://example.com/foo >expect &&
+ cat >"fake browser" <<-\EOF &&
+ #!/bin/sh
+ echo fake: "$@"
+ EOF
+ chmod +x "fake browser" &&
+ git config browser.w3m.path "$(pwd)/fake browser" &&
+ test_web_browse w3m http://example.com/foo
+'
+
+test_expect_success \
+ 'browser command allows arbitrary shell code' '
+ echo "arg: http://example.com/foo" >expect &&
+ git config browser.custom.cmd "
+ f() {
+ for i in \"\$@\"; do
+ echo arg: \$i
+ done
+ }
+ f" &&
+ test_web_browse custom http://example.com/foo
+'
+
+test_done
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
new file mode 100755
index 0000000..2ba62fb
--- /dev/null
+++ b/t/t9902-completion.sh
@@ -0,0 +1,651 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Felipe Contreras
+#
+
+test_description='test bash completion'
+
+. ./lib-bash.sh
+
+complete ()
+{
+ # do nothing
+ return 0
+}
+
+# Be careful when updating this list:
+#
+# (1) The build tree may have build artifact from different branch, or
+# the user's $PATH may have a random executable that may begin
+# with "git-check" that are not part of the subcommands this build
+# will ship, e.g. "check-ignore". The tests for completion for
+# subcommand names tests how "check" is expanded; we limit the
+# possible candidates to "checkout" and "check-attr" to make sure
+# "check-attr", which is known by the filter function as a
+# subcommand to be thrown out, while excluding other random files
+# that happen to begin with "check" to avoid letting them get in
+# the way.
+#
+# (2) A test makes sure that common subcommands are included in the
+# completion for "git <TAB>", and a plumbing is excluded. "add",
+# "filter-branch" and "ls-files" are listed for this.
+
+GIT_TESTING_COMMAND_COMPLETION='add checkout check-attr filter-branch ls-files'
+
+. "$GIT_BUILD_DIR/contrib/completion/git-completion.bash"
+
+# We don't need this function to actually join words or do anything special.
+# Also, it's cleaner to avoid touching bash's internal completion variables.
+# So let's override it with a minimal version for testing purposes.
+_get_comp_words_by_ref ()
+{
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ cur)
+ cur=${_words[_cword]}
+ ;;
+ prev)
+ prev=${_words[_cword-1]}
+ ;;
+ words)
+ words=("${_words[@]}")
+ ;;
+ cword)
+ cword=$_cword
+ ;;
+ esac
+ shift
+ done
+}
+
+print_comp ()
+{
+ local IFS=$'\n'
+ echo "${COMPREPLY[*]}" > out
+}
+
+run_completion ()
+{
+ local -a COMPREPLY _words
+ local _cword
+ _words=( $1 )
+ test "${1: -1}" = ' ' && _words[${#_words[@]}+1]=''
+ (( _cword = ${#_words[@]} - 1 ))
+ __git_wrap__git_main && print_comp
+}
+
+# Test high-level completion
+# Arguments are:
+# 1: typed text so far (cur)
+# 2: expected completion
+test_completion ()
+{
+ if test $# -gt 1
+ then
+ printf '%s\n' "$2" >expected
+ else
+ sed -e 's/Z$//' >expected
+ fi &&
+ run_completion "$1" &&
+ test_cmp expected out
+}
+
+# Test __gitcomp.
+# The first argument is the typed text so far (cur); the rest are
+# passed to __gitcomp. Expected output comes is read from the
+# standard input, like test_completion().
+test_gitcomp ()
+{
+ local -a COMPREPLY &&
+ sed -e 's/Z$//' >expected &&
+ cur="$1" &&
+ shift &&
+ __gitcomp "$@" &&
+ print_comp &&
+ test_cmp expected out
+}
+
+# Test __gitcomp_nl
+# Arguments are:
+# 1: current word (cur)
+# -: the rest are passed to __gitcomp_nl
+test_gitcomp_nl ()
+{
+ local -a COMPREPLY &&
+ sed -e 's/Z$//' >expected &&
+ cur="$1" &&
+ shift &&
+ __gitcomp_nl "$@" &&
+ print_comp &&
+ test_cmp expected out
+}
+
+invalid_variable_name='${foo.bar}'
+
+actual="$TRASH_DIRECTORY/actual"
+
+test_expect_success 'setup for __gitdir tests' '
+ mkdir -p subdir/subsubdir &&
+ git init otherrepo
+'
+
+test_expect_success '__gitdir - from command line (through $__git_dir)' '
+ echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+ (
+ __git_dir="$TRASH_DIRECTORY/otherrepo/.git" &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - repo as argument' '
+ echo "otherrepo/.git" >expected &&
+ __gitdir "otherrepo" >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - remote as argument' '
+ echo "remote" >expected &&
+ __gitdir "remote" >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - .git directory in cwd' '
+ echo ".git" >expected &&
+ __gitdir >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - .git directory in parent' '
+ echo "$(pwd -P)/.git" >expected &&
+ (
+ cd subdir/subsubdir &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - cwd is a .git directory' '
+ echo "." >expected &&
+ (
+ cd .git &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - parent is a .git directory' '
+ echo "$(pwd -P)/.git" >expected &&
+ (
+ cd .git/refs/heads &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - $GIT_DIR set while .git directory in cwd' '
+ echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+ (
+ GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
+ export GIT_DIR &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - $GIT_DIR set while .git directory in parent' '
+ echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+ (
+ GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
+ export GIT_DIR &&
+ cd subdir &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - non-existing $GIT_DIR' '
+ (
+ GIT_DIR="$TRASH_DIRECTORY/non-existing" &&
+ export GIT_DIR &&
+ test_must_fail __gitdir
+ )
+'
+
+function pwd_P_W () {
+ if test_have_prereq MINGW
+ then
+ pwd -W
+ else
+ pwd -P
+ fi
+}
+
+test_expect_success '__gitdir - gitfile in cwd' '
+ echo "$(pwd_P_W)/otherrepo/.git" >expected &&
+ echo "gitdir: $(pwd_P_W)/otherrepo/.git" >subdir/.git &&
+ test_when_finished "rm -f subdir/.git" &&
+ (
+ cd subdir &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - gitfile in parent' '
+ echo "$(pwd_P_W)/otherrepo/.git" >expected &&
+ echo "gitdir: $(pwd_P_W)/otherrepo/.git" >subdir/.git &&
+ test_when_finished "rm -f subdir/.git" &&
+ (
+ cd subdir/subsubdir &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success SYMLINKS '__gitdir - resulting path avoids symlinks' '
+ echo "$(pwd -P)/otherrepo/.git" >expected &&
+ mkdir otherrepo/dir &&
+ test_when_finished "rm -rf otherrepo/dir" &&
+ ln -s otherrepo/dir link &&
+ test_when_finished "rm -f link" &&
+ (
+ cd link &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - not a git repository' '
+ (
+ cd subdir/subsubdir &&
+ GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY" &&
+ export GIT_CEILING_DIRECTORIES &&
+ test_must_fail __gitdir
+ )
+'
+
+test_expect_success '__gitcomp - trailing space - options' '
+ test_gitcomp "--re" "--dry-run --reuse-message= --reedit-message=
+ --reset-author" <<-EOF
+ --reuse-message=Z
+ --reedit-message=Z
+ --reset-author Z
+ EOF
+'
+
+test_expect_success '__gitcomp - trailing space - config keys' '
+ test_gitcomp "br" "branch. branch.autosetupmerge
+ branch.autosetuprebase browser." <<-\EOF
+ branch.Z
+ branch.autosetupmerge Z
+ branch.autosetuprebase Z
+ browser.Z
+ EOF
+'
+
+test_expect_success '__gitcomp - option parameter' '
+ test_gitcomp "--strategy=re" "octopus ours recursive resolve subtree" \
+ "" "re" <<-\EOF
+ recursive Z
+ resolve Z
+ EOF
+'
+
+test_expect_success '__gitcomp - prefix' '
+ test_gitcomp "branch.me" "remote merge mergeoptions rebase" \
+ "branch.maint." "me" <<-\EOF
+ branch.maint.merge Z
+ branch.maint.mergeoptions Z
+ EOF
+'
+
+test_expect_success '__gitcomp - suffix' '
+ test_gitcomp "branch.me" "master maint next pu" "branch." \
+ "ma" "." <<-\EOF
+ branch.master.Z
+ branch.maint.Z
+ EOF
+'
+
+test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
+ __gitcomp "$invalid_variable_name"
+'
+
+read -r -d "" refs <<-\EOF
+maint
+master
+next
+pu
+EOF
+
+test_expect_success '__gitcomp_nl - trailing space' '
+ test_gitcomp_nl "m" "$refs" <<-EOF
+ maint Z
+ master Z
+ EOF
+'
+
+test_expect_success '__gitcomp_nl - prefix' '
+ test_gitcomp_nl "--fixup=m" "$refs" "--fixup=" "m" <<-EOF
+ --fixup=maint Z
+ --fixup=master Z
+ EOF
+'
+
+test_expect_success '__gitcomp_nl - suffix' '
+ test_gitcomp_nl "branch.ma" "$refs" "branch." "ma" "." <<-\EOF
+ branch.maint.Z
+ branch.master.Z
+ EOF
+'
+
+test_expect_success '__gitcomp_nl - no suffix' '
+ test_gitcomp_nl "ma" "$refs" "" "ma" "" <<-\EOF
+ maintZ
+ masterZ
+ EOF
+'
+
+test_expect_success '__gitcomp_nl - doesnt fail because of invalid variable name' '
+ __gitcomp_nl "$invalid_variable_name"
+'
+
+test_expect_success '__git_remotes - list remotes from $GIT_DIR/remotes and from config file' '
+ cat >expect <<-EOF &&
+ remote_from_file_1
+ remote_from_file_2
+ remote_in_config_1
+ remote_in_config_2
+ EOF
+ test_when_finished "rm -rf .git/remotes" &&
+ mkdir -p .git/remotes &&
+ >.git/remotes/remote_from_file_1 &&
+ >.git/remotes/remote_from_file_2 &&
+ test_when_finished "git remote remove remote_in_config_1" &&
+ git remote add remote_in_config_1 git://remote_1 &&
+ test_when_finished "git remote remove remote_in_config_2" &&
+ git remote add remote_in_config_2 git://remote_2 &&
+ __git_remotes >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '__git_get_config_variables' '
+ cat >expect <<-EOF &&
+ name-1
+ name-2
+ EOF
+ test_config interesting.name-1 good &&
+ test_config interesting.name-2 good &&
+ test_config subsection.interesting.name-3 bad &&
+ __git_get_config_variables interesting >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '__git_pretty_aliases' '
+ cat >expect <<-EOF &&
+ author
+ hash
+ EOF
+ test_config pretty.author "%an %ae" &&
+ test_config pretty.hash %H &&
+ __git_pretty_aliases >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '__git_aliases' '
+ cat >expect <<-EOF &&
+ ci
+ co
+ EOF
+ test_config alias.ci commit &&
+ test_config alias.co checkout &&
+ __git_aliases >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'basic' '
+ run_completion "git " &&
+ # built-in
+ grep -q "^add \$" out &&
+ # script
+ grep -q "^filter-branch \$" out &&
+ # plumbing
+ ! grep -q "^ls-files \$" out &&
+
+ run_completion "git f" &&
+ ! grep -q -v "^f" out
+'
+
+test_expect_success 'double dash "git" itself' '
+ test_completion "git --" <<-\EOF
+ --paginate Z
+ --no-pager Z
+ --git-dir=
+ --bare Z
+ --version Z
+ --exec-path Z
+ --exec-path=
+ --html-path Z
+ --man-path Z
+ --info-path Z
+ --work-tree=
+ --namespace=
+ --no-replace-objects Z
+ --help Z
+ EOF
+'
+
+test_expect_success 'double dash "git checkout"' '
+ test_completion "git checkout --" <<-\EOF
+ --quiet Z
+ --ours Z
+ --theirs Z
+ --track Z
+ --no-track Z
+ --merge Z
+ --conflict=
+ --orphan Z
+ --patch Z
+ EOF
+'
+
+test_expect_success 'general options' '
+ test_completion "git --ver" "--version " &&
+ test_completion "git --hel" "--help " &&
+ test_completion "git --exe" <<-\EOF &&
+ --exec-path Z
+ --exec-path=
+ EOF
+ test_completion "git --htm" "--html-path " &&
+ test_completion "git --pag" "--paginate " &&
+ test_completion "git --no-p" "--no-pager " &&
+ test_completion "git --git" "--git-dir=" &&
+ test_completion "git --wor" "--work-tree=" &&
+ test_completion "git --nam" "--namespace=" &&
+ test_completion "git --bar" "--bare " &&
+ test_completion "git --inf" "--info-path " &&
+ test_completion "git --no-r" "--no-replace-objects "
+'
+
+test_expect_success 'general options plus command' '
+ test_completion "git --version check" "checkout " &&
+ test_completion "git --paginate check" "checkout " &&
+ test_completion "git --git-dir=foo check" "checkout " &&
+ test_completion "git --bare check" "checkout " &&
+ test_completion "git --exec-path=foo check" "checkout " &&
+ test_completion "git --html-path check" "checkout " &&
+ test_completion "git --no-pager check" "checkout " &&
+ test_completion "git --work-tree=foo check" "checkout " &&
+ test_completion "git --namespace=foo check" "checkout " &&
+ test_completion "git --paginate check" "checkout " &&
+ test_completion "git --info-path check" "checkout " &&
+ test_completion "git --no-replace-objects check" "checkout "
+'
+
+test_expect_success 'git --help completion' '
+ test_completion "git --help ad" "add " &&
+ test_completion "git --help core" "core-tutorial "
+'
+
+test_expect_success 'setup for ref completion' '
+ echo content >file1 &&
+ echo more >file2 &&
+ git add . &&
+ git commit -m one &&
+ git branch mybranch &&
+ git tag mytag
+'
+
+test_expect_success 'checkout completes ref names' '
+ test_completion "git checkout m" <<-\EOF
+ master Z
+ mybranch Z
+ mytag Z
+ EOF
+'
+
+test_expect_success 'show completes all refs' '
+ test_completion "git show m" <<-\EOF
+ master Z
+ mybranch Z
+ mytag Z
+ EOF
+'
+
+test_expect_success '<ref>: completes paths' '
+ test_completion "git show mytag:f" <<-\EOF
+ file1 Z
+ file2 Z
+ EOF
+'
+
+test_expect_success 'complete tree filename with spaces' '
+ echo content >"name with spaces" &&
+ git add . &&
+ git commit -m spaces &&
+ test_completion "git show HEAD:nam" <<-\EOF
+ name with spaces Z
+ EOF
+'
+
+test_expect_success 'complete tree filename with metacharacters' '
+ echo content >"name with \${meta}" &&
+ git add . &&
+ git commit -m meta &&
+ test_completion "git show HEAD:nam" <<-\EOF
+ name with ${meta} Z
+ name with spaces Z
+ EOF
+'
+
+test_expect_success 'send-email' '
+ test_completion "git send-email --cov" "--cover-letter " &&
+ test_completion "git send-email ma" "master "
+'
+
+test_expect_success 'complete files' '
+ git init tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp" &&
+
+ echo "expected" > .gitignore &&
+ echo "out" >> .gitignore &&
+
+ git add .gitignore &&
+ test_completion "git commit " ".gitignore" &&
+
+ git commit -m ignore &&
+
+ touch new &&
+ test_completion "git add " "new" &&
+
+ git add new &&
+ git commit -a -m new &&
+ test_completion "git add " "" &&
+
+ git mv new modified &&
+ echo modify > modified &&
+ test_completion "git add " "modified" &&
+
+ touch untracked &&
+
+ : TODO .gitignore should not be here &&
+ test_completion "git rm " <<-\EOF &&
+ .gitignore
+ modified
+ EOF
+
+ test_completion "git clean " "untracked" &&
+
+ : TODO .gitignore should not be here &&
+ test_completion "git mv " <<-\EOF &&
+ .gitignore
+ modified
+ EOF
+
+ mkdir dir &&
+ touch dir/file-in-dir &&
+ git add dir/file-in-dir &&
+ git commit -m dir &&
+
+ mkdir untracked-dir &&
+
+ : TODO .gitignore should not be here &&
+ test_completion "git mv modified " <<-\EOF &&
+ .gitignore
+ dir
+ modified
+ untracked
+ untracked-dir
+ EOF
+
+ test_completion "git commit " "modified" &&
+
+ : TODO .gitignore should not be here &&
+ test_completion "git ls-files " <<-\EOF &&
+ .gitignore
+ dir
+ modified
+ EOF
+
+ touch momified &&
+ test_completion "git add mom" "momified"
+'
+
+test_expect_success "completion uses <cmd> completion for alias: !sh -c 'git <cmd> ...'" '
+ test_config alias.co "!sh -c '"'"'git checkout ...'"'"'" &&
+ test_completion "git co m" <<-\EOF
+ master Z
+ mybranch Z
+ mytag Z
+ EOF
+'
+
+test_expect_success 'completion uses <cmd> completion for alias: !f () { VAR=val git <cmd> ... }' '
+ test_config alias.co "!f () { VAR=val git checkout ... ; } f" &&
+ test_completion "git co m" <<-\EOF
+ master Z
+ mybranch Z
+ mytag Z
+ EOF
+'
+
+test_expect_success 'completion used <cmd> completion for alias: !f() { : git <cmd> ; ... }' '
+ test_config alias.co "!f() { : git checkout ; if ... } f" &&
+ test_completion "git co m" <<-\EOF
+ master Z
+ mybranch Z
+ mytag Z
+ EOF
+'
+
+test_expect_failure 'complete with tilde expansion' '
+ git init tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp" &&
+
+ touch ~/tmp/file &&
+
+ test_completion "git add ~/tmp/" "~/tmp/file"
+'
+
+test_done
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
new file mode 100755
index 0000000..0db4469
--- /dev/null
+++ b/t/t9903-bash-prompt.sh
@@ -0,0 +1,758 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 SZEDER Gábor
+#
+
+test_description='test git-specific bash prompt functions'
+
+. ./lib-bash.sh
+
+. "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
+
+actual="$TRASH_DIRECTORY/actual"
+c_red='\\[\\e[31m\\]'
+c_green='\\[\\e[32m\\]'
+c_lblue='\\[\\e[1;34m\\]'
+c_clear='\\[\\e[0m\\]'
+
+test_expect_success 'setup for prompt tests' '
+ git init otherrepo &&
+ echo 1 >file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ git tag -a -m msg1 t1 &&
+ git checkout -b b1 &&
+ echo 2 >file &&
+ git commit -m "second b1" file &&
+ echo 3 >file &&
+ git commit -m "third b1" file &&
+ git tag -a -m msg2 t2 &&
+ git checkout -b b2 master &&
+ echo 0 >file &&
+ git commit -m "second b2" file &&
+ echo 00 >file &&
+ git commit -m "another b2" file &&
+ echo 000 >file &&
+ git commit -m "yet another b2" file &&
+ mkdir ignored_dir &&
+ echo "ignored_dir/" >>.gitignore &&
+ git checkout master
+'
+
+test_expect_success 'prompt - branch name' '
+ printf " (master)" >expected &&
+ __git_ps1 >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success SYMLINKS 'prompt - branch name - symlink symref' '
+ printf " (master)" >expected &&
+ test_when_finished "git checkout master" &&
+ test_config core.preferSymlinkRefs true &&
+ git checkout master &&
+ __git_ps1 >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - unborn branch' '
+ printf " (unborn)" >expected &&
+ git checkout --orphan unborn &&
+ test_when_finished "git checkout master" &&
+ __git_ps1 >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+repo_with_newline='repo
+with
+newline'
+
+if test_have_prereq !MINGW && mkdir "$repo_with_newline" 2>/dev/null
+then
+ test_set_prereq FUNNYNAMES
+else
+ say 'Your filesystem does not allow newlines in filenames.'
+fi
+
+test_expect_success FUNNYNAMES 'prompt - with newline in path' '
+ printf " (master)" >expected &&
+ git init "$repo_with_newline" &&
+ test_when_finished "rm -rf \"$repo_with_newline\"" &&
+ mkdir "$repo_with_newline"/subdir &&
+ (
+ cd "$repo_with_newline/subdir" &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - detached head' '
+ printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
+ test_config core.abbrev 13 &&
+ git checkout b1^ &&
+ test_when_finished "git checkout master" &&
+ __git_ps1 >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - describe detached head - contains' '
+ printf " ((t2~1))" >expected &&
+ git checkout b1^ &&
+ test_when_finished "git checkout master" &&
+ (
+ GIT_PS1_DESCRIBE_STYLE=contains &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - describe detached head - branch' '
+ printf " ((tags/t2~1))" >expected &&
+ git checkout b1^ &&
+ test_when_finished "git checkout master" &&
+ (
+ GIT_PS1_DESCRIBE_STYLE=branch &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - describe detached head - describe' '
+ printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected &&
+ git checkout b1^ &&
+ test_when_finished "git checkout master" &&
+ (
+ GIT_PS1_DESCRIBE_STYLE=describe &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - describe detached head - default' '
+ printf " ((t2))" >expected &&
+ git checkout --detach b1 &&
+ test_when_finished "git checkout master" &&
+ __git_ps1 >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - inside .git directory' '
+ printf " (GIT_DIR!)" >expected &&
+ (
+ cd .git &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - deep inside .git directory' '
+ printf " (GIT_DIR!)" >expected &&
+ (
+ cd .git/refs/heads &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - inside bare repository' '
+ printf " (BARE:master)" >expected &&
+ git init --bare bare.git &&
+ test_when_finished "rm -rf bare.git" &&
+ (
+ cd bare.git &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - interactive rebase' '
+ printf " (b1|REBASE-i 2/3)" >expected &&
+ write_script fake_editor.sh <<-\EOF &&
+ echo "exec echo" >"$1"
+ echo "edit $(git log -1 --format="%h")" >>"$1"
+ echo "exec echo" >>"$1"
+ EOF
+ test_when_finished "rm -f fake_editor.sh" &&
+ test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" &&
+ git checkout b1 &&
+ test_when_finished "git checkout master" &&
+ git rebase -i HEAD^ &&
+ test_when_finished "git rebase --abort"
+ __git_ps1 >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - rebase merge' '
+ printf " (b2|REBASE-m 1/3)" >expected &&
+ git checkout b2 &&
+ test_when_finished "git checkout master" &&
+ test_must_fail git rebase --merge b1 b2 &&
+ test_when_finished "git rebase --abort" &&
+ __git_ps1 >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - rebase' '
+ printf " (b2|REBASE 1/3)" >expected &&
+ git checkout b2 &&
+ test_when_finished "git checkout master" &&
+ test_must_fail git rebase b1 b2 &&
+ test_when_finished "git rebase --abort" &&
+ __git_ps1 >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - merge' '
+ printf " (b1|MERGING)" >expected &&
+ git checkout b1 &&
+ test_when_finished "git checkout master" &&
+ test_must_fail git merge b2 &&
+ test_when_finished "git reset --hard" &&
+ __git_ps1 >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - cherry-pick' '
+ printf " (master|CHERRY-PICKING)" >expected &&
+ test_must_fail git cherry-pick b1 &&
+ test_when_finished "git reset --hard" &&
+ __git_ps1 >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bisect' '
+ printf " (master|BISECTING)" >expected &&
+ git bisect start &&
+ test_when_finished "git bisect reset" &&
+ __git_ps1 >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - clean' '
+ printf " (master)" >expected &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - dirty worktree' '
+ printf " (master *)" >expected &&
+ echo "dirty" >file &&
+ test_when_finished "git reset --hard" &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - dirty index' '
+ printf " (master +)" >expected &&
+ echo "dirty" >file &&
+ test_when_finished "git reset --hard" &&
+ git add -u &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - dirty index and worktree' '
+ printf " (master *+)" >expected &&
+ echo "dirty index" >file &&
+ test_when_finished "git reset --hard" &&
+ git add -u &&
+ echo "dirty worktree" >file &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - orphan branch - clean' '
+ printf " (orphan #)" >expected &&
+ test_when_finished "git checkout master" &&
+ git checkout --orphan orphan &&
+ git reset --hard &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - orphan branch - dirty index' '
+ printf " (orphan +)" >expected &&
+ test_when_finished "git checkout master" &&
+ git checkout --orphan orphan &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - orphan branch - dirty index and worktree' '
+ printf " (orphan *+)" >expected &&
+ test_when_finished "git checkout master" &&
+ git checkout --orphan orphan &&
+ >file &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - shell variable unset with config disabled' '
+ printf " (master)" >expected &&
+ echo "dirty" >file &&
+ test_when_finished "git reset --hard" &&
+ test_config bash.showDirtyState false &&
+ (
+ sane_unset GIT_PS1_SHOWDIRTYSTATE &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - shell variable unset with config enabled' '
+ printf " (master)" >expected &&
+ echo "dirty" >file &&
+ test_when_finished "git reset --hard" &&
+ test_config bash.showDirtyState true &&
+ (
+ sane_unset GIT_PS1_SHOWDIRTYSTATE &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - shell variable set with config disabled' '
+ printf " (master)" >expected &&
+ echo "dirty" >file &&
+ test_when_finished "git reset --hard" &&
+ test_config bash.showDirtyState false &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - shell variable set with config enabled' '
+ printf " (master *)" >expected &&
+ echo "dirty" >file &&
+ test_when_finished "git reset --hard" &&
+ test_config bash.showDirtyState true &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - not shown inside .git directory' '
+ printf " (GIT_DIR!)" >expected &&
+ echo "dirty" >file &&
+ test_when_finished "git reset --hard" &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ cd .git &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - stash status indicator - no stash' '
+ printf " (master)" >expected &&
+ (
+ GIT_PS1_SHOWSTASHSTATE=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - stash status indicator - stash' '
+ printf " (master $)" >expected &&
+ echo 2 >file &&
+ git stash &&
+ test_when_finished "git stash drop" &&
+ git pack-refs --all &&
+ (
+ GIT_PS1_SHOWSTASHSTATE=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - stash status indicator - not shown inside .git directory' '
+ printf " (GIT_DIR!)" >expected &&
+ echo 2 >file &&
+ git stash &&
+ test_when_finished "git stash drop" &&
+ (
+ GIT_PS1_SHOWSTASHSTATE=y &&
+ cd .git &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - untracked files status indicator - no untracked files' '
+ printf " (master)" >expected &&
+ (
+ GIT_PS1_SHOWUNTRACKEDFILES=y &&
+ cd otherrepo &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - untracked files status indicator - untracked files' '
+ printf " (master %%)" >expected &&
+ (
+ GIT_PS1_SHOWUNTRACKEDFILES=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - untracked files status indicator - empty untracked dir' '
+ printf " (master)" >expected &&
+ mkdir otherrepo/untracked-dir &&
+ test_when_finished "rm -rf otherrepo/untracked-dir" &&
+ (
+ GIT_PS1_SHOWUNTRACKEDFILES=y &&
+ cd otherrepo &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - untracked files status indicator - non-empty untracked dir' '
+ printf " (master %%)" >expected &&
+ mkdir otherrepo/untracked-dir &&
+ test_when_finished "rm -rf otherrepo/untracked-dir" &&
+ >otherrepo/untracked-dir/untracked-file &&
+ (
+ GIT_PS1_SHOWUNTRACKEDFILES=y &&
+ cd otherrepo &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - untracked files status indicator - untracked files outside cwd' '
+ printf " (master %%)" >expected &&
+ (
+ mkdir -p ignored_dir &&
+ cd ignored_dir &&
+ GIT_PS1_SHOWUNTRACKEDFILES=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - untracked files status indicator - shell variable unset with config disabled' '
+ printf " (master)" >expected &&
+ test_config bash.showUntrackedFiles false &&
+ (
+ sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - untracked files status indicator - shell variable unset with config enabled' '
+ printf " (master)" >expected &&
+ test_config bash.showUntrackedFiles true &&
+ (
+ sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - untracked files status indicator - shell variable set with config disabled' '
+ printf " (master)" >expected &&
+ test_config bash.showUntrackedFiles false &&
+ (
+ GIT_PS1_SHOWUNTRACKEDFILES=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - untracked files status indicator - shell variable set with config enabled' '
+ printf " (master %%)" >expected &&
+ test_config bash.showUntrackedFiles true &&
+ (
+ GIT_PS1_SHOWUNTRACKEDFILES=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - untracked files status indicator - not shown inside .git directory' '
+ printf " (GIT_DIR!)" >expected &&
+ (
+ GIT_PS1_SHOWUNTRACKEDFILES=y &&
+ cd .git &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - format string starting with dash' '
+ printf -- "-master" >expected &&
+ __git_ps1 "-%s" >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - pc mode' '
+ printf "BEFORE: (\${__git_ps1_branch_name}):AFTER\\nmaster" >expected &&
+ printf "" >expected_output &&
+ (
+ __git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
+ test_cmp expected_output "$actual" &&
+ printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - branch name' '
+ printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nmaster" >expected &&
+ (
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ __git_ps1 "BEFORE:" ":AFTER" >"$actual"
+ printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - detached head' '
+ printf "BEFORE: (${c_red}\${__git_ps1_branch_name}${c_clear}):AFTER\\n(%s...)" $(git log -1 --format="%h" b1^) >expected &&
+ git checkout b1^ &&
+ test_when_finished "git checkout master" &&
+ (
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty worktree' '
+ printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_clear}):AFTER\\nmaster" >expected &&
+ echo "dirty" >file &&
+ test_when_finished "git reset --hard" &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index' '
+ printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
+ echo "dirty" >file &&
+ test_when_finished "git reset --hard" &&
+ git add -u &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index and worktree' '
+ printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
+ echo "dirty index" >file &&
+ test_when_finished "git reset --hard" &&
+ git add -u &&
+ echo "dirty worktree" >file &&
+ (
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - before root commit' '
+ printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}#${c_clear}):AFTER\\nmaster" >expected &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ cd otherrepo &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - inside .git directory' '
+ printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nGIT_DIR!" >expected &&
+ echo "dirty" >file &&
+ test_when_finished "git reset --hard" &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ cd .git &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - stash status indicator' '
+ printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_lblue}\$${c_clear}):AFTER\\nmaster" >expected &&
+ echo 2 >file &&
+ git stash &&
+ test_when_finished "git stash drop" &&
+ (
+ GIT_PS1_SHOWSTASHSTATE=y &&
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - untracked files status indicator' '
+ printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}%%${c_clear}):AFTER\\nmaster" >expected &&
+ (
+ GIT_PS1_SHOWUNTRACKEDFILES=y &&
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - zsh color pc mode' '
+ printf "BEFORE: (%%F{green}master%%f):AFTER" >expected &&
+ (
+ ZSH_VERSION=5.0.0 &&
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s" "$PS1" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - hide if pwd ignored - env var unset, config disabled' '
+ printf " (master)" >expected &&
+ test_config bash.hideIfPwdIgnored false &&
+ (
+ cd ignored_dir &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - hide if pwd ignored - env var unset, config disabled, pc mode' '
+ printf "BEFORE: (\${__git_ps1_branch_name}):AFTER" >expected &&
+ test_config bash.hideIfPwdIgnored false &&
+ (
+ cd ignored_dir &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s" "$PS1" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - hide if pwd ignored - env var unset, config unset' '
+ printf " (master)" >expected &&
+ (
+ cd ignored_dir &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - hide if pwd ignored - env var unset, config unset, pc mode' '
+ printf "BEFORE: (\${__git_ps1_branch_name}):AFTER" >expected &&
+ (
+ cd ignored_dir &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s" "$PS1" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - hide if pwd ignored - env var set, config disabled' '
+ printf " (master)" >expected &&
+ test_config bash.hideIfPwdIgnored false &&
+ (
+ cd ignored_dir &&
+ GIT_PS1_HIDE_IF_PWD_IGNORED=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - hide if pwd ignored - env var set, config disabled, pc mode' '
+ printf "BEFORE: (\${__git_ps1_branch_name}):AFTER" >expected &&
+ test_config bash.hideIfPwdIgnored false &&
+ (
+ cd ignored_dir &&
+ GIT_PS1_HIDE_IF_PWD_IGNORED=y &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s" "$PS1" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - hide if pwd ignored - env var set, config unset' '
+ printf "" >expected &&
+ (
+ cd ignored_dir &&
+ GIT_PS1_HIDE_IF_PWD_IGNORED=y &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - hide if pwd ignored - env var set, config unset, pc mode' '
+ printf "BEFORE::AFTER" >expected &&
+ (
+ cd ignored_dir &&
+ GIT_PS1_HIDE_IF_PWD_IGNORED=y &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s" "$PS1" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - hide if pwd ignored - inside gitdir (stdout)' '
+ printf " (GIT_DIR!)" >expected &&
+ (
+ GIT_PS1_HIDE_IF_PWD_IGNORED=y &&
+ cd .git &&
+ __git_ps1 >"$actual" 2>/dev/null
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - hide if pwd ignored - inside gitdir (stderr)' '
+ printf "" >expected &&
+ (
+ GIT_PS1_HIDE_IF_PWD_IGNORED=y &&
+ cd .git &&
+ __git_ps1 >/dev/null 2>"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_done
diff --git a/t/test9200a.png b/t/test-binary-1.png
index 7b181d1..7b181d1 100644
--- a/t/test9200a.png
+++ b/t/test-binary-1.png
Binary files differ
diff --git a/t/test9200b.png b/t/test-binary-2.png
index ac22ccb..ac22ccb 100644
--- a/t/test9200b.png
+++ b/t/test-binary-2.png
Binary files differ
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
new file mode 100644
index 0000000..48884d5
--- /dev/null
+++ b/t/test-lib-functions.sh
@@ -0,0 +1,963 @@
+# Library of functions shared by all tests scripts, included by
+# test-lib.sh.
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see http://www.gnu.org/licenses/ .
+
+# The semantics of the editor variables are that of invoking
+# sh -c "$EDITOR \"$@\"" files ...
+#
+# If our trash directory contains shell metacharacters, they will be
+# interpreted if we just set $EDITOR directly, so do a little dance with
+# environment variables to work around this.
+#
+# In particular, quoting isn't enough, as the path may contain the same quote
+# that we're using.
+test_set_editor () {
+ FAKE_EDITOR="$1"
+ export FAKE_EDITOR
+ EDITOR='"$FAKE_EDITOR"'
+ export EDITOR
+}
+
+test_set_index_version () {
+ GIT_INDEX_VERSION="$1"
+ export GIT_INDEX_VERSION
+}
+
+test_decode_color () {
+ awk '
+ function name(n) {
+ if (n == 0) return "RESET";
+ if (n == 1) return "BOLD";
+ if (n == 30) return "BLACK";
+ if (n == 31) return "RED";
+ if (n == 32) return "GREEN";
+ if (n == 33) return "YELLOW";
+ if (n == 34) return "BLUE";
+ if (n == 35) return "MAGENTA";
+ if (n == 36) return "CYAN";
+ if (n == 37) return "WHITE";
+ if (n == 40) return "BLACK";
+ if (n == 41) return "BRED";
+ if (n == 42) return "BGREEN";
+ if (n == 43) return "BYELLOW";
+ if (n == 44) return "BBLUE";
+ if (n == 45) return "BMAGENTA";
+ if (n == 46) return "BCYAN";
+ if (n == 47) return "BWHITE";
+ }
+ {
+ while (match($0, /\033\[[0-9;]*m/) != 0) {
+ printf "%s<", substr($0, 1, RSTART-1);
+ codes = substr($0, RSTART+2, RLENGTH-3);
+ if (length(codes) == 0)
+ printf "%s", name(0)
+ else {
+ n = split(codes, ary, ";");
+ sep = "";
+ for (i = 1; i <= n; i++) {
+ printf "%s%s", sep, name(ary[i]);
+ sep = ";"
+ }
+ }
+ printf ">";
+ $0 = substr($0, RSTART + RLENGTH, length($0) - RSTART - RLENGTH + 1);
+ }
+ print
+ }
+ '
+}
+
+nul_to_q () {
+ perl -pe 'y/\000/Q/'
+}
+
+q_to_nul () {
+ perl -pe 'y/Q/\000/'
+}
+
+q_to_cr () {
+ tr Q '\015'
+}
+
+q_to_tab () {
+ tr Q '\011'
+}
+
+qz_to_tab_space () {
+ tr QZ '\011\040'
+}
+
+append_cr () {
+ sed -e 's/$/Q/' | tr Q '\015'
+}
+
+remove_cr () {
+ tr '\015' Q | sed -e 's/Q$//'
+}
+
+# In some bourne shell implementations, the "unset" builtin returns
+# nonzero status when a variable to be unset was not set in the first
+# place.
+#
+# Use sane_unset when that should not be considered an error.
+
+sane_unset () {
+ unset "$@"
+ return 0
+}
+
+test_tick () {
+ if test -z "${test_tick+set}"
+ then
+ test_tick=1112911993
+ else
+ test_tick=$(($test_tick + 60))
+ fi
+ GIT_COMMITTER_DATE="$test_tick -0700"
+ GIT_AUTHOR_DATE="$test_tick -0700"
+ export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+}
+
+# Stop execution and start a shell. This is useful for debugging tests and
+# only makes sense together with "-v".
+#
+# Be sure to remove all invocations of this command before submitting.
+
+test_pause () {
+ if test "$verbose" = t; then
+ "$SHELL_PATH" <&6 >&3 2>&4
+ else
+ error >&5 "test_pause requires --verbose"
+ fi
+}
+
+# Wrap git in gdb. Adding this to a command can make it easier to
+# understand what is going on in a failing test.
+#
+# Example: "debug git checkout master".
+debug () {
+ GIT_TEST_GDB=1 "$@"
+}
+
+# Call test_commit with the arguments "<message> [<file> [<contents> [<tag>]]]"
+#
+# This will commit a file with the given contents and the given commit
+# message, and tag the resulting commit with the given tag name.
+#
+# <file>, <contents>, and <tag> all default to <message>.
+
+test_commit () {
+ notick= &&
+ signoff= &&
+ while test $# != 0
+ do
+ case "$1" in
+ --notick)
+ notick=yes
+ ;;
+ --signoff)
+ signoff="$1"
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+ done &&
+ file=${2:-"$1.t"} &&
+ echo "${3-$1}" > "$file" &&
+ git add "$file" &&
+ if test -z "$notick"
+ then
+ test_tick
+ fi &&
+ git commit $signoff -m "$1" &&
+ git tag "${4:-$1}"
+}
+
+# Call test_merge with the arguments "<message> <commit>", where <commit>
+# can be a tag pointing to the commit-to-merge.
+
+test_merge () {
+ test_tick &&
+ git merge -m "$1" "$2" &&
+ git tag "$1"
+}
+
+# This function helps systems where core.filemode=false is set.
+# Use it instead of plain 'chmod +x' to set or unset the executable bit
+# of a file in the working directory and add it to the index.
+
+test_chmod () {
+ chmod "$@" &&
+ git update-index --add "--chmod=$@"
+}
+
+# Unset a configuration variable, but don't fail if it doesn't exist.
+test_unconfig () {
+ config_dir=
+ if test "$1" = -C
+ then
+ shift
+ config_dir=$1
+ shift
+ fi
+ git ${config_dir:+-C "$config_dir"} config --unset-all "$@"
+ config_status=$?
+ case "$config_status" in
+ 5) # ok, nothing to unset
+ config_status=0
+ ;;
+ esac
+ return $config_status
+}
+
+# Set git config, automatically unsetting it after the test is over.
+test_config () {
+ config_dir=
+ if test "$1" = -C
+ then
+ shift
+ config_dir=$1
+ shift
+ fi
+ test_when_finished "test_unconfig ${config_dir:+-C '$config_dir'} '$1'" &&
+ git ${config_dir:+-C "$config_dir"} config "$@"
+}
+
+test_config_global () {
+ test_when_finished "test_unconfig --global '$1'" &&
+ git config --global "$@"
+}
+
+write_script () {
+ {
+ echo "#!${2-"$SHELL_PATH"}" &&
+ cat
+ } >"$1" &&
+ chmod +x "$1"
+}
+
+# Use test_set_prereq to tell that a particular prerequisite is available.
+# The prerequisite can later be checked for in two ways:
+#
+# - Explicitly using test_have_prereq.
+#
+# - Implicitly by specifying the prerequisite tag in the calls to
+# test_expect_{success,failure,code}.
+#
+# The single parameter is the prerequisite tag (a simple word, in all
+# capital letters by convention).
+
+test_set_prereq () {
+ satisfied_prereq="$satisfied_prereq$1 "
+}
+satisfied_prereq=" "
+lazily_testable_prereq= lazily_tested_prereq=
+
+# Usage: test_lazy_prereq PREREQ 'script'
+test_lazy_prereq () {
+ lazily_testable_prereq="$lazily_testable_prereq$1 "
+ eval test_prereq_lazily_$1=\$2
+}
+
+test_run_lazy_prereq_ () {
+ script='
+mkdir -p "$TRASH_DIRECTORY/prereq-test-dir" &&
+(
+ cd "$TRASH_DIRECTORY/prereq-test-dir" &&'"$2"'
+)'
+ say >&3 "checking prerequisite: $1"
+ say >&3 "$script"
+ test_eval_ "$script"
+ eval_ret=$?
+ rm -rf "$TRASH_DIRECTORY/prereq-test-dir"
+ if test "$eval_ret" = 0; then
+ say >&3 "prerequisite $1 ok"
+ else
+ say >&3 "prerequisite $1 not satisfied"
+ fi
+ return $eval_ret
+}
+
+test_have_prereq () {
+ # prerequisites can be concatenated with ','
+ save_IFS=$IFS
+ IFS=,
+ set -- $*
+ IFS=$save_IFS
+
+ total_prereq=0
+ ok_prereq=0
+ missing_prereq=
+
+ for prerequisite
+ do
+ case "$prerequisite" in
+ !*)
+ negative_prereq=t
+ prerequisite=${prerequisite#!}
+ ;;
+ *)
+ negative_prereq=
+ esac
+
+ case " $lazily_tested_prereq " in
+ *" $prerequisite "*)
+ ;;
+ *)
+ case " $lazily_testable_prereq " in
+ *" $prerequisite "*)
+ eval "script=\$test_prereq_lazily_$prerequisite" &&
+ if test_run_lazy_prereq_ "$prerequisite" "$script"
+ then
+ test_set_prereq $prerequisite
+ fi
+ lazily_tested_prereq="$lazily_tested_prereq$prerequisite "
+ esac
+ ;;
+ esac
+
+ total_prereq=$(($total_prereq + 1))
+ case "$satisfied_prereq" in
+ *" $prerequisite "*)
+ satisfied_this_prereq=t
+ ;;
+ *)
+ satisfied_this_prereq=
+ esac
+
+ case "$satisfied_this_prereq,$negative_prereq" in
+ t,|,t)
+ ok_prereq=$(($ok_prereq + 1))
+ ;;
+ *)
+ # Keep a list of missing prerequisites; restore
+ # the negative marker if necessary.
+ prerequisite=${negative_prereq:+!}$prerequisite
+ if test -z "$missing_prereq"
+ then
+ missing_prereq=$prerequisite
+ else
+ missing_prereq="$prerequisite,$missing_prereq"
+ fi
+ esac
+ done
+
+ test $total_prereq = $ok_prereq
+}
+
+test_declared_prereq () {
+ case ",$test_prereq," in
+ *,$1,*)
+ return 0
+ ;;
+ esac
+ return 1
+}
+
+test_verify_prereq () {
+ test -z "$test_prereq" ||
+ expr >/dev/null "$test_prereq" : '[A-Z0-9_,!]*$' ||
+ error "bug in the test script: '$test_prereq' does not look like a prereq"
+}
+
+test_expect_failure () {
+ test_start_
+ test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
+ test "$#" = 2 ||
+ error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
+ test_verify_prereq
+ export test_prereq
+ if ! test_skip "$@"
+ then
+ say >&3 "checking known breakage: $2"
+ if test_run_ "$2" expecting_failure
+ then
+ test_known_broken_ok_ "$1"
+ else
+ test_known_broken_failure_ "$1"
+ fi
+ fi
+ test_finish_
+}
+
+test_expect_success () {
+ test_start_
+ test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
+ test "$#" = 2 ||
+ error "bug in the test script: not 2 or 3 parameters to test-expect-success"
+ test_verify_prereq
+ export test_prereq
+ if ! test_skip "$@"
+ then
+ say >&3 "expecting success: $2"
+ if test_run_ "$2"
+ then
+ test_ok_ "$1"
+ else
+ test_failure_ "$@"
+ fi
+ fi
+ test_finish_
+}
+
+# test_external runs external test scripts that provide continuous
+# test output about their progress, and succeeds/fails on
+# zero/non-zero exit code. It outputs the test output on stdout even
+# in non-verbose mode, and announces the external script with "# run
+# <n>: ..." before running it. When providing relative paths, keep in
+# mind that all scripts run in "trash directory".
+# Usage: test_external description command arguments...
+# Example: test_external 'Perl API' perl ../path/to/test.pl
+test_external () {
+ test "$#" = 4 && { test_prereq=$1; shift; } || test_prereq=
+ test "$#" = 3 ||
+ error >&5 "bug in the test script: not 3 or 4 parameters to test_external"
+ descr="$1"
+ shift
+ test_verify_prereq
+ export test_prereq
+ if ! test_skip "$descr" "$@"
+ then
+ # Announce the script to reduce confusion about the
+ # test output that follows.
+ say_color "" "# run $test_count: $descr ($*)"
+ # Export TEST_DIRECTORY, TRASH_DIRECTORY and GIT_TEST_LONG
+ # to be able to use them in script
+ export TEST_DIRECTORY TRASH_DIRECTORY GIT_TEST_LONG
+ # Run command; redirect its stderr to &4 as in
+ # test_run_, but keep its stdout on our stdout even in
+ # non-verbose mode.
+ "$@" 2>&4
+ if test "$?" = 0
+ then
+ if test $test_external_has_tap -eq 0; then
+ test_ok_ "$descr"
+ else
+ say_color "" "# test_external test $descr was ok"
+ test_success=$(($test_success + 1))
+ fi
+ else
+ if test $test_external_has_tap -eq 0; then
+ test_failure_ "$descr" "$@"
+ else
+ say_color error "# test_external test $descr failed: $@"
+ test_failure=$(($test_failure + 1))
+ fi
+ fi
+ fi
+}
+
+# Like test_external, but in addition tests that the command generated
+# no output on stderr.
+test_external_without_stderr () {
+ # The temporary file has no (and must have no) security
+ # implications.
+ tmp=${TMPDIR:-/tmp}
+ stderr="$tmp/git-external-stderr.$$.tmp"
+ test_external "$@" 4> "$stderr"
+ test -f "$stderr" || error "Internal error: $stderr disappeared."
+ descr="no stderr: $1"
+ shift
+ say >&3 "# expecting no stderr from previous command"
+ if test ! -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 test "$verbose" = t
+ then
+ output=$(echo; echo "# Stderr is:"; cat "$stderr")
+ else
+ output=
+ fi
+ # rm first in case test_failure exits.
+ rm "$stderr"
+ if test $test_external_has_tap -eq 0; then
+ test_failure_ "$descr" "$@" "$output"
+ else
+ say_color error "# test_external_without_stderr test $descr failed: $@: $output"
+ test_failure=$(($test_failure + 1))
+ fi
+ fi
+}
+
+# debugging-friendly alternatives to "test [-f|-d|-e]"
+# The commands test the existence or non-existence of $1. $2 can be
+# given to provide a more precise diagnosis.
+test_path_is_file () {
+ if ! test -f "$1"
+ then
+ echo "File $1 doesn't exist. $2"
+ false
+ fi
+}
+
+test_path_is_dir () {
+ if ! test -d "$1"
+ then
+ echo "Directory $1 doesn't exist. $2"
+ false
+ fi
+}
+
+# Check if the directory exists and is empty as expected, barf otherwise.
+test_dir_is_empty () {
+ test_path_is_dir "$1" &&
+ if test -n "$(ls -a1 "$1" | egrep -v '^\.\.?$')"
+ then
+ echo "Directory '$1' is not empty, it contains:"
+ ls -la "$1"
+ return 1
+ fi
+}
+
+test_path_is_missing () {
+ if test -e "$1"
+ then
+ echo "Path exists:"
+ ls -ld "$1"
+ if test $# -ge 1
+ then
+ echo "$*"
+ fi
+ false
+ fi
+}
+
+# test_line_count checks that a file has the number of lines it
+# ought to. For example:
+#
+# test_expect_success 'produce exactly one line of output' '
+# do something >output &&
+# test_line_count = 1 output
+# '
+#
+# is like "test $(wc -l <output) = 1" except that it passes the
+# output through when the number of lines is wrong.
+
+test_line_count () {
+ if test $# != 3
+ then
+ error "bug in the test script: not 3 parameters to test_line_count"
+ elif ! test $(wc -l <"$3") "$1" "$2"
+ then
+ echo "test_line_count: line count for $3 !$1 $2"
+ cat "$3"
+ return 1
+ fi
+}
+
+# Returns success if a comma separated string of keywords ($1) contains a
+# given keyword ($2).
+# Examples:
+# `list_contains "foo,bar" bar` returns 0
+# `list_contains "foo" bar` returns 1
+
+list_contains () {
+ case ",$1," in
+ *,$2,*)
+ return 0
+ ;;
+ esac
+ return 1
+}
+
+# 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 () {
+ case "$1" in
+ ok=*)
+ _test_ok=${1#ok=}
+ shift
+ ;;
+ *)
+ _test_ok=
+ ;;
+ esac
+ "$@"
+ exit_code=$?
+ if test $exit_code -eq 0 && ! list_contains "$_test_ok" success
+ then
+ echo >&2 "test_must_fail: command succeeded: $*"
+ return 1
+ elif test $exit_code -eq 141 && list_contains "$_test_ok" sigpipe
+ then
+ return 0
+ elif test $exit_code -gt 129 && test $exit_code -le 192
+ then
+ echo >&2 "test_must_fail: died by signal $(($exit_code - 128)): $*"
+ return 1
+ elif test $exit_code -eq 127
+ then
+ echo >&2 "test_must_fail: command not found: $*"
+ return 1
+ elif test $exit_code -eq 126
+ then
+ echo >&2 "test_must_fail: valgrind error: $*"
+ return 1
+ fi
+ return 0
+}
+
+# Similar to test_must_fail, but tolerates success, too. This is
+# meant to be used in contexts like:
+#
+# test_expect_success 'some command works without configuration' '
+# test_might_fail git config --unset all.configuration &&
+# do something
+# '
+#
+# Writing "git config --unset all.configuration || :" would be wrong,
+# because we want to notice if it fails due to segv.
+
+test_might_fail () {
+ test_must_fail ok=success "$@"
+}
+
+# Similar to test_must_fail and test_might_fail, but check that a
+# given command exited with a given exit code. Meant to be used as:
+#
+# test_expect_success 'Merge with d/f conflicts' '
+# test_expect_code 1 git merge "merge msg" B master
+# '
+
+test_expect_code () {
+ want_code=$1
+ shift
+ "$@"
+ exit_code=$?
+ if test $exit_code = $want_code
+ then
+ return 0
+ fi
+
+ echo >&2 "test_expect_code: command exited with $exit_code, we wanted $want_code $*"
+ return 1
+}
+
+# test_cmp is a helper function to compare actual and expected output.
+# You can use it like:
+#
+# test_expect_success 'foo works' '
+# echo expected >expected &&
+# foo >actual &&
+# test_cmp expected actual
+# '
+#
+# This could be written as either "cmp" or "diff -u", but:
+# - cmp's output is not nearly as easy to read as diff -u
+# - not all diff versions understand "-u"
+
+test_cmp() {
+ $GIT_TEST_CMP "$@"
+}
+
+# test_cmp_bin - helper to compare binary files
+
+test_cmp_bin() {
+ cmp "$@"
+}
+
+# Call any command "$@" but be more verbose about its
+# failure. This is handy for commands like "test" which do
+# not output anything when they fail.
+verbose () {
+ "$@" && return 0
+ echo >&2 "command failed: $(git rev-parse --sq-quote "$@")"
+ return 1
+}
+
+# Check if the file expected to be empty is indeed empty, and barfs
+# otherwise.
+
+test_must_be_empty () {
+ if test -s "$1"
+ then
+ echo "'$1' is not empty, it contains:"
+ cat "$1"
+ return 1
+ fi
+}
+
+# Tests that its two parameters refer to the same revision
+test_cmp_rev () {
+ git rev-parse --verify "$1" >expect.rev &&
+ git rev-parse --verify "$2" >actual.rev &&
+ test_cmp expect.rev actual.rev
+}
+
+# Print a sequence of integers in increasing order, either with
+# two arguments (start and end):
+#
+# test_seq 1 5 -- outputs 1 2 3 4 5 one line at a time
+#
+# or with one argument (end), in which case it starts counting
+# from 1.
+
+test_seq () {
+ case $# in
+ 1) set 1 "$@" ;;
+ 2) ;;
+ *) error "bug in the test script: not 1 or 2 parameters to test_seq" ;;
+ esac
+ test_seq_counter__=$1
+ while test "$test_seq_counter__" -le "$2"
+ do
+ echo "$test_seq_counter__"
+ test_seq_counter__=$(( $test_seq_counter__ + 1 ))
+ done
+}
+
+# This function can be used to schedule some commands to be run
+# unconditionally at the end of the test to restore sanity:
+#
+# test_expect_success 'test core.capslock' '
+# git config core.capslock true &&
+# test_when_finished "git config --unset core.capslock" &&
+# hello world
+# '
+#
+# That would be roughly equivalent to
+#
+# test_expect_success 'test core.capslock' '
+# git config core.capslock true &&
+# hello world
+# git config --unset core.capslock
+# '
+#
+# except that the greeting and config --unset must both succeed for
+# the test to pass.
+#
+# Note that under --immediate mode, no clean-up is done to help diagnose
+# what went wrong.
+
+test_when_finished () {
+ # We cannot detect when we are in a subshell in general, but by
+ # doing so on Bash is better than nothing (the test will
+ # silently pass on other shells).
+ test "${BASH_SUBSHELL-0}" = 0 ||
+ error "bug in test script: test_when_finished does nothing in a subshell"
+ test_cleanup="{ $*
+ } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
+}
+
+# Most tests can use the created repository, but some may need to create more.
+# Usage: test_create_repo <directory>
+test_create_repo () {
+ test "$#" = 1 ||
+ error "bug in the test script: not 1 parameter to test-create-repo"
+ repo="$1"
+ mkdir -p "$repo"
+ (
+ cd "$repo" || error "Cannot setup test environment"
+ "$GIT_EXEC_PATH/git-init" "--template=$GIT_BUILD_DIR/templates/blt/" >&3 2>&4 ||
+ error "cannot run git init -- have you built things yet?"
+ mv .git/hooks .git/hooks-disabled
+ ) || exit
+}
+
+# This function helps on symlink challenged file systems when it is not
+# important that the file system entry is a symbolic link.
+# Use test_ln_s_add instead of "ln -s x y && git add y" to add a
+# symbolic link entry y to the index.
+
+test_ln_s_add () {
+ if test_have_prereq SYMLINKS
+ then
+ ln -s "$1" "$2" &&
+ git update-index --add "$2"
+ else
+ printf '%s' "$1" >"$2" &&
+ ln_s_obj=$(git hash-object -w "$2") &&
+ git update-index --add --cacheinfo 120000 $ln_s_obj "$2" &&
+ # pick up stat info from the file
+ git update-index "$2"
+ fi
+}
+
+# This function writes out its parameters, one per line
+test_write_lines () {
+ printf "%s\n" "$@"
+}
+
+perl () {
+ command "$PERL_PATH" "$@"
+}
+
+# Is the value one of the various ways to spell a boolean true/false?
+test_normalize_bool () {
+ git -c magic.variable="$1" config --bool magic.variable 2>/dev/null
+}
+
+# Given a variable $1, normalize the value of it to one of "true",
+# "false", or "auto" and store the result to it.
+#
+# test_tristate GIT_TEST_HTTPD
+#
+# A variable set to an empty string is set to 'false'.
+# A variable set to 'false' or 'auto' keeps its value.
+# Anything else is set to 'true'.
+# An unset variable defaults to 'auto'.
+#
+# The last rule is to allow people to set the variable to an empty
+# string and export it to decline testing the particular feature
+# for versions both before and after this change. We used to treat
+# both unset and empty variable as a signal for "do not test" and
+# took any non-empty string as "please test".
+
+test_tristate () {
+ if eval "test x\"\${$1+isset}\" = xisset"
+ then
+ # explicitly set
+ eval "
+ case \"\$$1\" in
+ '') $1=false ;;
+ auto) ;;
+ *) $1=\$(test_normalize_bool \$$1 || echo true) ;;
+ esac
+ "
+ else
+ eval "$1=auto"
+ fi
+}
+
+# Exit the test suite, either by skipping all remaining tests or by
+# exiting with an error. If "$1" is "auto", we then we assume we were
+# opportunistically trying to set up some tests and we skip. If it is
+# "true", then we report a failure.
+#
+# The error/skip message should be given by $2.
+#
+test_skip_or_die () {
+ case "$1" in
+ auto)
+ skip_all=$2
+ test_done
+ ;;
+ true)
+ error "$2"
+ ;;
+ *)
+ error "BUG: test tristate is '$1' (real error: $2)"
+ esac
+}
+
+# The following mingw_* functions obey POSIX shell syntax, but are actually
+# bash scripts, and are meant to be used only with bash on Windows.
+
+# A test_cmp function that treats LF and CRLF equal and avoids to fork
+# diff when possible.
+mingw_test_cmp () {
+ # Read text into shell variables and compare them. If the results
+ # are different, use regular diff to report the difference.
+ local test_cmp_a= test_cmp_b=
+
+ # When text came from stdin (one argument is '-') we must feed it
+ # to diff.
+ local stdin_for_diff=
+
+ # Since it is difficult to detect the difference between an
+ # empty input file and a failure to read the files, we go straight
+ # to diff if one of the inputs is empty.
+ if test -s "$1" && test -s "$2"
+ then
+ # regular case: both files non-empty
+ mingw_read_file_strip_cr_ test_cmp_a <"$1"
+ mingw_read_file_strip_cr_ test_cmp_b <"$2"
+ elif test -s "$1" && test "$2" = -
+ then
+ # read 2nd file from stdin
+ mingw_read_file_strip_cr_ test_cmp_a <"$1"
+ mingw_read_file_strip_cr_ test_cmp_b
+ stdin_for_diff='<<<"$test_cmp_b"'
+ elif test "$1" = - && test -s "$2"
+ then
+ # read 1st file from stdin
+ mingw_read_file_strip_cr_ test_cmp_a
+ mingw_read_file_strip_cr_ test_cmp_b <"$2"
+ stdin_for_diff='<<<"$test_cmp_a"'
+ fi
+ test -n "$test_cmp_a" &&
+ test -n "$test_cmp_b" &&
+ test "$test_cmp_a" = "$test_cmp_b" ||
+ eval "diff -u \"\$@\" $stdin_for_diff"
+}
+
+# $1 is the name of the shell variable to fill in
+mingw_read_file_strip_cr_ () {
+ # Read line-wise using LF as the line separator
+ # and use IFS to strip CR.
+ local line
+ while :
+ do
+ if IFS=$'\r' read -r -d $'\n' line
+ then
+ # good
+ line=$line$'\n'
+ else
+ # we get here at EOF, but also if the last line
+ # was not terminated by LF; in the latter case,
+ # some text was read
+ if test -z "$line"
+ then
+ # EOF, really
+ break
+ fi
+ fi
+ eval "$1=\$$1\$line"
+ done
+}
+
+# Like "env FOO=BAR some-program", but run inside a subshell, which means
+# it also works for shell functions (though those functions cannot impact
+# the environment outside of the test_env invocation).
+test_env () {
+ (
+ while test $# -gt 0
+ do
+ case "$1" in
+ *=*)
+ eval "${1%%=*}=\${1#*=}"
+ eval "export ${1%%=*}"
+ shift
+ ;;
+ *)
+ "$@"
+ exit
+ ;;
+ esac
+ done
+ )
+}
diff --git a/t/test-lib.sh b/t/test-lib.sh
index e5523dd..11201e9 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+# Test framework for git. See t/README for usage.
#
# Copyright (c) 2005 Junio C Hamano
#
@@ -15,6 +15,39 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see http://www.gnu.org/licenses/ .
+# Test the binaries we have just built. The tests are kept in
+# t/ subdirectory and are run in 'trash directory' subdirectory.
+if test -z "$TEST_DIRECTORY"
+then
+ # We allow tests to override this, in case they want to run tests
+ # outside of t/, e.g. for running tests on the test library
+ # itself.
+ TEST_DIRECTORY=$(pwd)
+else
+ # ensure that TEST_DIRECTORY is an absolute path so that it
+ # is valid even if the current working directory is changed
+ TEST_DIRECTORY=$(cd "$TEST_DIRECTORY" && pwd) || exit 1
+fi
+if test -z "$TEST_OUTPUT_DIRECTORY"
+then
+ # Similarly, override this to store the test-results subdir
+ # elsewhere
+ TEST_OUTPUT_DIRECTORY=$TEST_DIRECTORY
+fi
+GIT_BUILD_DIR="$TEST_DIRECTORY"/..
+
+################################################################
+# It appears that people try to run tests without building...
+"$GIT_BUILD_DIR/git" >/dev/null
+if test $? != 1
+then
+ echo >&2 'error: you do not seem to have built git yet.'
+ exit 1
+fi
+
+. "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
+export PERL_PATH SHELL_PATH
+
# if --tee was passed, write the output not only to the terminal, but
# additionally to the file test-results/$BASENAME.out, too.
case "$GIT_TEST_TEE_STARTED, $* " in
@@ -22,73 +55,102 @@ done,*)
# do not redirect again
;;
*' --tee '*|*' --va'*)
- mkdir -p test-results
- BASE=test-results/$(basename "$0" .sh)
- (GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1;
+ mkdir -p "$TEST_OUTPUT_DIRECTORY/test-results"
+ BASE="$TEST_OUTPUT_DIRECTORY/test-results/$(basename "$0" .sh)"
+ (GIT_TEST_TEE_STARTED=done ${SHELL_PATH} "$0" "$@" 2>&1;
echo $? > $BASE.exit) | tee $BASE.out
test "$(cat $BASE.exit)" = 0
exit
;;
esac
-# Keep the original TERM for say_color
-ORIGINAL_TERM=$TERM
-
# For repeatability, reset the environment to known value.
+# TERM is sanitized below, after saving color control sequences.
LANG=C
LC_ALL=C
PAGER=cat
TZ=UTC
-TERM=dumb
-export LANG LC_ALL PAGER TERM TZ
+export LANG LC_ALL PAGER 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
+# A call to "unset" with no arguments causes at least Solaris 10
+# /usr/xpg4/bin/sh and /bin/ksh to bail out. So keep the unsets
+# deriving from the command substitution clustered with the other
+# ones.
+unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e '
+ my @env = keys %ENV;
+ my $ok = join("|", qw(
+ TRACE
+ DEBUG
+ USE_LOOKUP
+ TEST
+ .*_TEST
+ PROVE
+ VALGRIND
+ UNZIP
+ PERF_
+ CURL_VERBOSE
+ ));
+ my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env);
+ print join("\n", @vars);
+')
+unset XDG_CONFIG_HOME
+unset GITPERLLIB
GIT_AUTHOR_EMAIL=author@example.com
GIT_AUTHOR_NAME='A U Thor'
-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
+GIT_MERGE_AUTOEDIT=no
+export GIT_MERGE_VERBOSITY GIT_MERGE_AUTOEDIT
export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
export EDITOR
+# Tests using GIT_TRACE typically don't want <timestamp> <file>:<line> output
+GIT_TRACE_BARE=1
+export GIT_TRACE_BARE
+
+if test -n "${TEST_GIT_INDEX_VERSION:+isset}"
+then
+ GIT_INDEX_VERSION="$TEST_GIT_INDEX_VERSION"
+ export GIT_INDEX_VERSION
+fi
+
+# Add libc MALLOC and MALLOC_PERTURB test
+# only if we are not executing the test with valgrind
+if expr " $GIT_TEST_OPTS " : ".* --valgrind " >/dev/null ||
+ test -n "$TEST_NO_MALLOC_CHECK"
+then
+ setup_malloc_check () {
+ : nothing
+ }
+ teardown_malloc_check () {
+ : nothing
+ }
+else
+ setup_malloc_check () {
+ MALLOC_CHECK_=3 MALLOC_PERTURB_=165
+ export MALLOC_CHECK_ MALLOC_PERTURB_
+ }
+ teardown_malloc_check () {
+ unset MALLOC_CHECK_ MALLOC_PERTURB_
+ }
+fi
+
+: ${ASAN_OPTIONS=detect_leaks=0}
+export ASAN_OPTIONS
+
# Protect ourselves from common misconfiguration to export
# CDPATH into the environment
unset CDPATH
unset GREP_OPTIONS
+unset UNZIP
case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in
- 1|2|true)
- echo "* warning: Some tests will not work if GIT_TRACE" \
- "is set as to trace on STDERR ! *"
- echo "* warning: Please set GIT_TRACE to something" \
- "other than 1, 2 or true ! *"
- ;;
+1|2|true)
+ GIT_TRACE=4
+ ;;
esac
# Convenience
@@ -97,16 +159,27 @@ esac
_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"
+# Zero SHA-1
+_z40=0000000000000000000000000000000000000000
+
+# Line feed
+LF='
+'
+
+# UTF-8 ZERO WIDTH NON-JOINER, which HFS+ ignores
+# when case-folding filenames
+u200c=$(printf '\342\200\214')
+
+export _x05 _x40 _z40 LF u200c
+
# 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 ] &&
+test "x$TERM" != "xdumb" && (
+ test -t 1 &&
tput bold >/dev/null 2>&1 &&
tput setaf 1 >/dev/null 2>&1 &&
tput sgr0 >/dev/null 2>&1
@@ -122,57 +195,105 @@ do
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 ;;
+ -r)
+ shift; test "$#" -ne 0 || {
+ echo 'error: -r requires an argument' >&2;
+ exit 1;
+ }
+ run_list=$1; shift ;;
+ --run=*)
+ run_list=${1#--*=}; shift ;;
-h|--h|--he|--hel|--help)
help=t; shift ;;
-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
verbose=t; shift ;;
+ --verbose-only=*)
+ verbose_only=${1#--*=}
+ shift ;;
-q|--q|--qu|--qui|--quie|--quiet)
- quiet=t; shift ;;
+ # Ignore --quiet under a TAP::Harness. Saying how many tests
+ # passed without the ok/not ok details is always an error.
+ test -z "$HARNESS_ACTIVE" && 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 ;;
+ valgrind=memcheck
+ shift ;;
+ --valgrind=*)
+ valgrind=${1#--*=}
+ shift ;;
+ --valgrind-only=*)
+ valgrind_only=${1#--*=}
+ shift ;;
--tee)
shift ;; # was handled already
--root=*)
- root=$(expr "z$1" : 'z[^=]*=\(.*\)')
+ root=${1#--*=}
+ shift ;;
+ --chain-lint)
+ GIT_TEST_CHAIN_LINT=1
+ shift ;;
+ --no-chain-lint)
+ GIT_TEST_CHAIN_LINT=0
+ shift ;;
+ -x)
+ trace=t
+ verbose=t
shift ;;
*)
echo "error: unknown test option '$1'" >&2; exit 1 ;;
esac
done
-if test -n "$color"; then
+if test -n "$valgrind_only"
+then
+ test -z "$valgrind" && valgrind=memcheck
+ test -z "$verbose" && verbose_only="$valgrind_only"
+elif test -n "$valgrind"
+then
+ verbose=t
+fi
+
+if test -n "$color"
+then
+ # Save the color control sequences now rather than run tput
+ # each time say_color() is called. This is done for two
+ # reasons:
+ # * TERM will be changed to dumb
+ # * HOME will be changed to a temporary directory and tput
+ # might need to read ~/.terminfo from the original HOME
+ # directory to get the control sequences
+ # Note: This approach assumes the control sequences don't end
+ # in a newline for any terminal of interest (command
+ # substitutions strip trailing newlines). Given that most
+ # (all?) terminals in common use are related to ECMA-48, this
+ # shouldn't be a problem.
+ say_color_error=$(tput bold; tput setaf 1) # bold red
+ say_color_skip=$(tput setaf 4) # blue
+ say_color_warn=$(tput setaf 3) # brown/yellow
+ say_color_pass=$(tput setaf 2) # green
+ say_color_info=$(tput setaf 6) # cyan
+ say_color_reset=$(tput sgr0)
+ say_color_="" # no formatting for normal text
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
+ test -z "$1" && test -n "$quiet" && return
+ eval "say_color_color=\$say_color_$1"
shift
- printf "%s" "$*"
- tput sgr0
- echo
- )
+ printf "%s\\n" "$say_color_color$*$say_color_reset"
}
else
say_color() {
test -z "$1" && test -n "$quiet" && return
shift
- echo "$*"
+ printf "%s\n" "$*"
}
fi
+TERM=dumb
+export TERM
+
error () {
say_color error "error: $*"
GIT_EXIT_OK=t
@@ -188,11 +309,12 @@ error "Test script did not set test_description."
if test "$help" = "t"
then
- echo "$test_description"
+ printf '%s\n' "$test_description"
exit 0
fi
exec 5>&1
+exec 6<&0
if test "$verbose" = "t"
then
exec 4>&2 3>&1
@@ -200,6 +322,19 @@ else
exec 4>/dev/null 3>/dev/null
fi
+# Send any "-x" output directly to stderr to avoid polluting tests
+# which capture stderr. We can do this unconditionally since it
+# has no effect if tracing isn't turned on.
+#
+# Note that this sets up the trace fd as soon as we assign the variable, so it
+# must come after the creation of descriptor 4 above. Likewise, we must never
+# unset this, as it has the side effect of closing descriptor 4, which we
+# use to show verbose tests to the user.
+#
+# Note also that we don't need or want to export it. The tracing is local to
+# this shell, and we would not want to influence any shells we exec.
+BASH_XTRACEFD=4
+
test_failure=0
test_count=0
test_fixed=0
@@ -221,123 +356,14 @@ die () {
GIT_EXIT_OK=
trap 'die' EXIT
+trap 'exit $?' INT
-# 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
-}
+# The user-facing functions are loaded from a separate file so that
+# test_perf subshells can have them too
+. "$TEST_DIRECTORY/test-lib-functions.sh"
# You are not expected to call test_ok_ and test_failure_ directly, use
-# the text_expect_* functions instead.
+# the test_expect_* functions instead.
test_ok_ () {
test_success=$(($test_success + 1))
@@ -346,323 +372,361 @@ test_ok_ () {
test_failure_ () {
test_failure=$(($test_failure + 1))
- say_color error "not ok - $test_count $1"
+ say_color error "not ok $test_count - $1"
shift
- echo "$@" | sed -e 's/^/# /'
+ printf '%s\n' "$*" | 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"
+ say_color error "ok $test_count - $@ # TODO known breakage vanished"
}
test_known_broken_failure_ () {
test_broken=$(($test_broken+1))
- say_color skip "not ok $test_count - $@ # TODO known breakage"
+ say_color warn "not ok $test_count - $@ # TODO known breakage"
}
test_debug () {
test "$debug" = "" || eval "$1"
}
-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
+match_pattern_list () {
+ arg="$1"
+ shift
+ test -z "$*" && return 1
+ for pattern_
+ do
+ case "$arg" in
+ $pattern_)
+ return 0
+ esac
+ done
+ return 1
}
-test_skip () {
- test_count=$(($test_count+1))
- to_skip=
- for skp in $GIT_SKIP_TESTS
+match_test_selector_list () {
+ title="$1"
+ shift
+ arg="$1"
+ shift
+ test -z "$1" && return 0
+
+ # Both commas and whitespace are accepted as separators.
+ OLDIFS=$IFS
+ IFS=' ,'
+ set -- $1
+ IFS=$OLDIFS
+
+ # If the first selector is negative we include by default.
+ include=
+ case "$1" in
+ !*) include=t ;;
+ esac
+
+ for selector
do
- case $this_test.$test_count in
- $skp)
- to_skip=t
- break
+ orig_selector=$selector
+
+ positive=t
+ case "$selector" in
+ !*)
+ positive=
+ selector=${selector##?}
+ ;;
+ esac
+
+ test -z "$selector" && continue
+
+ case "$selector" in
+ *-*)
+ if expr "z${selector%%-*}" : "z[0-9]*[^0-9]" >/dev/null
+ then
+ echo "error: $title: invalid non-numeric in range" \
+ "start: '$orig_selector'" >&2
+ exit 1
+ fi
+ if expr "z${selector#*-}" : "z[0-9]*[^0-9]" >/dev/null
+ then
+ echo "error: $title: invalid non-numeric in range" \
+ "end: '$orig_selector'" >&2
+ exit 1
+ fi
+ ;;
+ *)
+ if expr "z$selector" : "z[0-9]*[^0-9]" >/dev/null
+ then
+ echo "error: $title: invalid non-numeric in test" \
+ "selector: '$orig_selector'" >&2
+ exit 1
+ fi
+ esac
+
+ # Short cut for "obvious" cases
+ test -z "$include" && test -z "$positive" && continue
+ test -n "$include" && test -n "$positive" && continue
+
+ case "$selector" in
+ -*)
+ if test $arg -le ${selector#-}
+ then
+ include=$positive
+ fi
+ ;;
+ *-)
+ if test $arg -ge ${selector%-}
+ then
+ include=$positive
+ fi
+ ;;
+ *-*)
+ if test ${selector%%-*} -le $arg \
+ && test $arg -le ${selector#*-}
+ then
+ include=$positive
+ fi
+ ;;
+ *)
+ if test $arg -eq $selector
+ then
+ include=$positive
+ fi
+ ;;
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 -n "$include"
}
-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 ""
+maybe_teardown_verbose () {
+ test -z "$verbose_only" && return
+ exec 4>/dev/null 3>/dev/null
+ verbose=
}
-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 "$@"
+last_verbose=t
+maybe_setup_verbose () {
+ test -z "$verbose_only" && return
+ if match_pattern_list $test_count $verbose_only
then
- say >&3 "expecting success: $2"
- test_run_ "$2"
- if [ "$?" = 0 -a "$eval_ret" = 0 ]
- then
- test_ok_ "$1"
- else
- test_failure_ "$@"
- fi
+ exec 4>&2 3>&1
+ # Emit a delimiting blank line when going from
+ # non-verbose to verbose. Within verbose mode the
+ # delimiter is printed by test_expect_*. The choice
+ # of the initial $last_verbose is such that before
+ # test 1, we do not print it.
+ test -z "$last_verbose" && echo >&3 ""
+ verbose=t
+ else
+ exec 4>/dev/null 3>/dev/null
+ verbose=
fi
- echo >&3 ""
+ last_verbose=$verbose
+}
+
+maybe_teardown_valgrind () {
+ test -z "$GIT_VALGRIND" && return
+ GIT_VALGRIND_ENABLED=
}
-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 "$@"
+maybe_setup_valgrind () {
+ test -z "$GIT_VALGRIND" && return
+ if test -z "$valgrind_only"
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
+ GIT_VALGRIND_ENABLED=t
+ return
+ fi
+ GIT_VALGRIND_ENABLED=
+ if match_pattern_list $test_count $valgrind_only
+ then
+ GIT_VALGRIND_ENABLED=t
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 ]
+want_trace () {
+ test "$trace" = t && test "$verbose" = t
+}
+
+# This is a separate function because some tests use
+# "return" to end a test_expect_success block early
+# (and we want to make sure we run any cleanup like
+# "set +x").
+test_eval_inner_ () {
+ # Do not add anything extra (including LF) after '$*'
+ eval "
+ want_trace && set -x
+ $*"
+}
+
+test_eval_ () {
+ # We run this block with stderr redirected to avoid extra cruft
+ # during a "-x" trace. Once in "set -x" mode, we cannot prevent
+ # the shell from printing the "set +x" to turn it off (nor the saving
+ # of $? before that). But we can make sure that the output goes to
+ # /dev/null.
+ #
+ # The test itself is run with stderr put back to &4 (so either to
+ # /dev/null, or to the original stderr if --verbose was used).
+ {
+ test_eval_inner_ "$@" </dev/null >&3 2>&4
+ test_eval_ret_=$?
+ if want_trace
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))
+ set +x
+ if test "$test_eval_ret_" != 0
+ then
+ say_color error >&4 "error: last command exited with \$?=$test_eval_ret_"
fi
fi
- fi
+ } 2>/dev/null
+ return $test_eval_ret_
}
-# 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))
+test_run_ () {
+ test_cleanup=:
+ expecting_failure=$2
+
+ if test "${GIT_TEST_CHAIN_LINT:-1}" != 0; then
+ # turn off tracing for this test-eval, as it simply creates
+ # confusing noise in the "-x" output
+ trace_tmp=$trace
+ trace=
+ # 117 is magic because it is unlikely to match the exit
+ # code of other programs
+ test_eval_ "(exit 117) && $1"
+ if test "$?" != 117; then
+ error "bug in the test script: broken &&-chain: $1"
fi
+ trace=$trace_tmp
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.
+ setup_malloc_check
+ test_eval_ "$1"
+ eval_ret=$?
+ teardown_malloc_check
-test_must_fail () {
- "$@"
- test $? -gt 0 -a $? -le 129 -o $? -gt 192
+ if test -z "$immediate" || test $eval_ret = 0 ||
+ test -n "$expecting_failure" && test "$test_cleanup" != ":"
+ then
+ setup_malloc_check
+ test_eval_ "$test_cleanup"
+ teardown_malloc_check
+ fi
+ if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"
+ then
+ echo ""
+ fi
+ return "$eval_ret"
}
-# 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_start_ () {
+ test_count=$(($test_count+1))
+ maybe_setup_verbose
+ maybe_setup_valgrind
}
-# 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 "$@"
+test_finish_ () {
+ echo >&3 ""
+ maybe_teardown_valgrind
+ maybe_teardown_verbose
}
-# 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_skip () {
+ to_skip=
+ skipped_reason=
+ if match_pattern_list $this_test.$test_count $GIT_SKIP_TESTS
+ then
+ to_skip=t
+ skipped_reason="GIT_SKIP_TESTS"
+ fi
+ if test -z "$to_skip" && test -n "$test_prereq" &&
+ ! test_have_prereq "$test_prereq"
+ then
+ to_skip=t
-test_when_finished () {
- test_cleanup="{ $*
- } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
+ of_prereq=
+ if test "$missing_prereq" != "$test_prereq"
+ then
+ of_prereq=" of $test_prereq"
+ fi
+ skipped_reason="missing $missing_prereq${of_prereq}"
+ fi
+ if test -z "$to_skip" && test -n "$run_list" &&
+ ! match_test_selector_list '--run' $test_count "$run_list"
+ then
+ to_skip=t
+ skipped_reason="--run"
+ fi
+
+ case "$to_skip" in
+ t)
+ say_color skip >&3 "skipping test: $@"
+ say_color skip "ok $test_count # skip $1 ($skipped_reason)"
+ : true
+ ;;
+ *)
+ false
+ ;;
+ esac
}
-# 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"
+# stub; perf-lib overrides it
+test_at_end_hook_ () {
+ :
}
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 -z "$HARNESS_ACTIVE"
+ then
+ test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results"
+ mkdir -p "$test_results_dir"
+ base=${0##*/}
+ test_results_path="$test_results_dir/${base%.sh}-$$.counts"
+
+ cat >>"$test_results_path" <<-EOF
+ total $test_count
+ success $test_success
+ fixed $test_fixed
+ broken $test_broken
+ failed $test_failure
+
+ EOF
+ fi
if test "$test_fixed" != 0
then
- say_color pass "# fixed $test_fixed known breakage(s)"
+ say_color error "# $test_fixed known breakage(s) vanished; please update test(s)"
fi
if test "$test_broken" != 0
then
- say_color error "# still have $test_broken known breakage(s)"
- msg="remaining $(($test_count-$test_broken)) test(s)"
+ say_color warn "# still have $test_broken known breakage(s)"
+ fi
+ if test "$test_broken" != 0 || test "$test_fixed" != 0
+ then
+ test_remaining=$(( $test_count - $test_broken - $test_fixed ))
+ msg="remaining $test_remaining test(s)"
else
+ test_remaining=$test_count
msg="$test_count test(s)"
fi
case "$test_failure" in
0)
# Maybe print SKIP message
- [ -z "$skip_all" ] || skip_all=" # SKIP $skip_all"
+ if test -n "$skip_all" && test $test_count -gt 0
+ then
+ error "Can't use skip_all after running some tests"
+ fi
+ test -z "$skip_all" || skip_all=" # SKIP $skip_all"
- if test $test_external_has_tap -eq 0; then
- say_color pass "# passed all $msg"
+ if test $test_external_has_tap -eq 0
+ then
+ if test $test_remaining -gt 0
+ then
+ say_color pass "# passed all $msg"
+ fi
say "1..$test_count$skip_all"
fi
@@ -670,10 +734,13 @@ test_done () {
cd "$(dirname "$remove_trash")" &&
rm -rf "$(basename "$remove_trash")"
+ test_at_end_hook_
+
exit 0 ;;
*)
- if test $test_external_has_tap -eq 0; then
+ if test $test_external_has_tap -eq 0
+ then
say_color error "# failed $test_failure among $msg"
say "1..$test_count"
fi
@@ -683,9 +750,6 @@ test_done () {
esac
}
-# Test the binaries we have just built. The tests are kept in
-# t/ subdirectory and are run in 'trash directory' subdirectory.
-TEST_DIRECTORY=$(pwd)
if test -n "$valgrind"
then
make_symlink () {
@@ -708,11 +772,14 @@ then
}
make_valgrind_symlink () {
- # handle only executables
- test -x "$1" || return
+ # handle only executables, unless they are shell libraries that
+ # need to be in the exec-path.
+ test -x "$1" ||
+ test "# " = "$(head -c 2 <"$1")" ||
+ return;
base=$(basename "$1")
- symlink_target=$TEST_DIRECTORY/../$base
+ symlink_target=$GIT_BUILD_DIR/$base
# do not override scripts
if test -x "$symlink_target" &&
test ! -d "$symlink_target" &&
@@ -731,10 +798,12 @@ then
# 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-*
+ for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-*
do
make_valgrind_symlink $file
done
+ # special-case the mergetools loadables
+ make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools"
OLDIFS=$IFS
IFS=:
for path in $PATH
@@ -749,32 +818,38 @@ then
PATH=$GIT_VALGRIND/bin:$PATH
GIT_EXEC_PATH=$GIT_VALGRIND/bin
export GIT_VALGRIND
-elif test -n "$GIT_TEST_INSTALLED" ; then
+ GIT_VALGRIND_MODE="$valgrind"
+ export GIT_VALGRIND_MODE
+ GIT_VALGRIND_ENABLED=t
+ test -n "$valgrind_only" && GIT_VALGRIND_ENABLED=
+ export GIT_VALGRIND_ENABLED
+elif test -n "$GIT_TEST_INSTALLED"
+then
GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path) ||
error "Cannot run git from $GIT_TEST_INSTALLED."
- PATH=$GIT_TEST_INSTALLED:$TEST_DIRECTORY/..:$PATH
+ PATH=$GIT_TEST_INSTALLED:$GIT_BUILD_DIR:$PATH
GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH}
else # normal case, use ../bin-wrappers only unless $with_dashes:
- git_bin_dir="$TEST_DIRECTORY/../bin-wrappers"
- if ! test -x "$git_bin_dir/git" ; then
- if test -z "$with_dashes" ; then
+ git_bin_dir="$GIT_BUILD_DIR/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"
+ GIT_EXEC_PATH=$GIT_BUILD_DIR
+ if test -n "$with_dashes"
+ then
+ PATH="$GIT_BUILD_DIR:$PATH"
fi
fi
-GIT_TEMPLATE_DIR=$(pwd)/../templates/blt
-unset GIT_CONFIG
+GIT_TEMPLATE_DIR="$GIT_BUILD_DIR"/templates/blt
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
+GIT_ATTR_NOSYSTEM=1
+export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_ATTR_NOSYSTEM
if test -z "$GIT_TEST_CMP"
then
@@ -786,57 +861,55 @@ then
fi
fi
-GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git
+GITPERLLIB="$GIT_BUILD_DIR"/perl/blib/lib:"$GIT_BUILD_DIR"/perl/blib/arch/auto/Git
export GITPERLLIB
-test -d ../templates/blt || {
+test -d "$GIT_BUILD_DIR"/templates/blt || {
error "You haven't built things yet, have you?"
}
-if test -z "$GIT_TEST_INSTALLED" && test -z "$NO_PYTHON"
+if ! test -x "$GIT_BUILD_DIR"/t/helper/test-chmtime
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'
+ echo >&2 'Run "make t/helper/test-chmtime" in the source (toplevel) directory'
exit 1
fi
# Test repository
-test="trash directory.$(basename "$0" .sh)"
-test -n "$root" && test="$root/$test"
-case "$test" in
-/*) TRASH_DIRECTORY="$test" ;;
- *) TRASH_DIRECTORY="$TEST_DIRECTORY/$test" ;;
+TRASH_DIRECTORY="trash directory.$(basename "$0" .sh)"
+test -n "$root" && TRASH_DIRECTORY="$root/$TRASH_DIRECTORY"
+case "$TRASH_DIRECTORY" in
+/*) ;; # absolute path is good
+ *) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$TRASH_DIRECTORY" ;;
esac
test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY
-rm -fr "$test" || {
+rm -fr "$TRASH_DIRECTORY" || {
GIT_EXIT_OK=t
echo >&5 "FATAL: Cannot prepare test area"
exit 1
}
-test_create_repo "$test"
+HOME="$TRASH_DIRECTORY"
+GNUPGHOME="$HOME/gnupg-home-not-used"
+export HOME GNUPGHOME
+
+if test -z "$TEST_NO_CREATE_REPO"
+then
+ test_create_repo "$TRASH_DIRECTORY"
+else
+ mkdir -p "$TRASH_DIRECTORY"
+fi
# Use -P to resolve symlinks in our working directory so that the cwd
# in subprocesses like git equals our $PWD (for pathname comparisons).
-cd -P "$test" || exit 1
+cd -P "$TRASH_DIRECTORY" || exit 1
this_test=${0##*/}
this_test=${this_test%%-*}
-for skp in $GIT_SKIP_TESTS
-do
- case "$this_test" in
- $skp)
- say_color skip >&3 "skipping test $this_test altogether"
- skip_all="skip all tests in $this_test"
- test_done
- esac
-done
+if match_pattern_list "$this_test" $GIT_SKIP_TESTS
+then
+ say_color info >&3 "skipping test $this_test altogether"
+ skip_all="skip all tests in $this_test"
+ test_done
+fi
# Provide an implementation of the 'yes' utility
yes () {
@@ -847,9 +920,11 @@ yes () {
y="$*"
fi
- while echo "$y"
+ i=0
+ while test $i -lt 99
do
- :
+ echo "$y"
+ i=$(($i+1))
done
}
@@ -873,6 +948,18 @@ case $(uname -s) in
# no POSIX permissions
# backslashes in pathspec are converted to '/'
# exec does not inherit the PID
+ test_set_prereq MINGW
+ test_set_prereq NATIVE_CRLF
+ test_set_prereq SED_STRIPS_CR
+ test_set_prereq GREP_STRIPS_CR
+ GIT_TEST_CMP=mingw_test_cmp
+ ;;
+*CYGWIN*)
+ test_set_prereq POSIXPERM
+ test_set_prereq EXECKEEPSPID
+ test_set_prereq CYGWIN
+ test_set_prereq SED_STRIPS_CR
+ test_set_prereq GREP_STRIPS_CR
;;
*)
test_set_prereq POSIXPERM
@@ -881,9 +968,155 @@ case $(uname -s) in
;;
esac
+( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1
test -z "$NO_PERL" && test_set_prereq PERL
test -z "$NO_PYTHON" && test_set_prereq PYTHON
+test -n "$USE_LIBPCRE" && test_set_prereq LIBPCRE
+test -z "$NO_GETTEXT" && test_set_prereq GETTEXT
+
+# Can we rely on git's output in the C locale?
+if test -n "$GETTEXT_POISON"
+then
+ GIT_GETTEXT_POISON=YesPlease
+ export GIT_GETTEXT_POISON
+ test_set_prereq GETTEXT_POISON
+else
+ test_set_prereq C_LOCALE_OUTPUT
+fi
+
+# Use this instead of test_cmp to compare files that contain expected and
+# actual output from git commands that can be translated. When running
+# under GETTEXT_POISON this pretends that the command produced expected
+# results.
+test_i18ncmp () {
+ test -n "$GETTEXT_POISON" || test_cmp "$@"
+}
+
+# Use this instead of "grep expected-string actual" to see if the
+# output from a git command that can be translated either contains an
+# expected string, or does not contain an unwanted one. When running
+# under GETTEXT_POISON this pretends that the command produced expected
+# results.
+test_i18ngrep () {
+ if test -n "$GETTEXT_POISON"
+ then
+ : # pretend success
+ elif test "x!" = "x$1"
+ then
+ shift
+ ! grep "$@"
+ else
+ grep "$@"
+ fi
+}
+
+test_lazy_prereq PIPE '
+ # test whether the filesystem supports FIFOs
+ case $(uname -s) in
+ CYGWIN*|MINGW*)
+ false
+ ;;
+ *)
+ rm -f testfifo && mkfifo testfifo
+ ;;
+ esac
+'
+
+test_lazy_prereq SYMLINKS '
+ # test whether the filesystem supports symbolic links
+ ln -s x y && test -h y
+'
+
+test_lazy_prereq FILEMODE '
+ test "$(git config --bool core.filemode)" = true
+'
+
+test_lazy_prereq CASE_INSENSITIVE_FS '
+ echo good >CamelCase &&
+ echo bad >camelcase &&
+ test "$(cat CamelCase)" != good
+'
+
+test_lazy_prereq UTF8_NFD_TO_NFC '
+ # check whether FS converts nfd unicode to nfc
+ auml=$(printf "\303\244")
+ aumlcdiar=$(printf "\141\314\210")
+ >"$auml" &&
+ case "$(echo *)" in
+ "$aumlcdiar")
+ true ;;
+ *)
+ false ;;
+ esac
+'
+
+test_lazy_prereq AUTOIDENT '
+ sane_unset GIT_AUTHOR_NAME &&
+ sane_unset GIT_AUTHOR_EMAIL &&
+ git var GIT_AUTHOR_IDENT
+'
+
+test_lazy_prereq EXPENSIVE '
+ test -n "$GIT_TEST_LONG"
+'
+
+test_lazy_prereq USR_BIN_TIME '
+ test -x /usr/bin/time
+'
+
+test_lazy_prereq NOT_ROOT '
+ uid=$(id -u) &&
+ test "$uid" != 0
+'
+
+# SANITY is about "can you correctly predict what the filesystem would
+# do by only looking at the permission bits of the files and
+# directories?" A typical example of !SANITY is running the test
+# suite as root, where a test may expect "chmod -r file && cat file"
+# to fail because file is supposed to be unreadable after a successful
+# chmod. In an environment (i.e. combination of what filesystem is
+# being used and who is running the tests) that lacks SANITY, you may
+# be able to delete or create a file when the containing directory
+# doesn't have write permissions, or access a file even if the
+# containing directory doesn't have read or execute permissions.
+
+test_lazy_prereq SANITY '
+ mkdir SANETESTD.1 SANETESTD.2 &&
+
+ chmod +w SANETESTD.1 SANETESTD.2 &&
+ >SANETESTD.1/x 2>SANETESTD.2/x &&
+ chmod -w SANETESTD.1 &&
+ chmod -r SANETESTD.1/x &&
+ chmod -rx SANETESTD.2 ||
+ error "bug in test sript: cannot prepare SANETESTD"
+
+ ! test -r SANETESTD.1/x &&
+ ! rm SANETESTD.1/x && ! test -f SANETESTD.2/x
+ status=$?
+
+ chmod +rwx SANETESTD.1 SANETESTD.2 &&
+ rm -rf SANETESTD.1 SANETESTD.2 ||
+ error "bug in test sript: cannot clean SANETESTD"
+ return $status
+'
+
+GIT_UNZIP=${GIT_UNZIP:-unzip}
+test_lazy_prereq UNZIP '
+ "$GIT_UNZIP" -v
+ test $? -ne 127
+'
+
+run_with_limited_cmdline () {
+ (ulimit -s 128 && "$@")
+}
+
+test_lazy_prereq CMDLINE_LIMIT 'run_with_limited_cmdline true'
+
+build_option () {
+ git version --build-options |
+ sed -ne "s/^$1: //p"
+}
-# test whether the filesystem supports symbolic links
-ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS
-rm -f y
+test_lazy_prereq LONG_IS_64BIT '
+ test 8 -le "$(build_option sizeof-long)"
+'
diff --git a/t/test-terminal.perl b/t/test-terminal.perl
new file mode 100755
index 0000000..96b6a03
--- /dev/null
+++ b/t/test-terminal.perl
@@ -0,0 +1,104 @@
+#!/usr/bin/perl
+use 5.008;
+use strict;
+use warnings;
+use IO::Pty;
+use File::Copy;
+
+# Run @$argv in the background with stdio redirected to $in, $out and $err.
+sub start_child {
+ my ($argv, $in, $out, $err) = @_;
+ my $pid = fork;
+ if (not defined $pid) {
+ die "fork failed: $!"
+ } elsif ($pid == 0) {
+ open STDIN, "<&", $in;
+ open STDOUT, ">&", $out;
+ open STDERR, ">&", $err;
+ close $in;
+ 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: $!";
+}
+
+sub copy_stdin {
+ my ($in) = @_;
+ my $pid = fork;
+ if (!$pid) {
+ xsendfile($in, \*STDIN);
+ exit 0;
+ }
+ close($in);
+ return $pid;
+}
+
+sub copy_stdio {
+ my ($out, $err) = @_;
+ my $pid = fork;
+ defined $pid or die "fork failed: $!";
+ if (!$pid) {
+ close($out);
+ xsendfile(\*STDERR, $err);
+ exit 0;
+ }
+ close($err);
+ xsendfile(\*STDOUT, $out);
+ finish_child($pid) == 0
+ or exit 1;
+}
+
+if ($#ARGV < 1) {
+ die "usage: test-terminal program args";
+}
+my $master_in = new IO::Pty;
+my $master_out = new IO::Pty;
+my $master_err = new IO::Pty;
+$master_in->set_raw();
+$master_out->set_raw();
+$master_err->set_raw();
+$master_in->slave->set_raw();
+$master_out->slave->set_raw();
+$master_err->slave->set_raw();
+my $pid = start_child(\@ARGV, $master_in->slave, $master_out->slave, $master_err->slave);
+close $master_in->slave;
+close $master_out->slave;
+close $master_err->slave;
+my $in_pid = copy_stdin($master_in);
+copy_stdio($master_out, $master_err);
+my $ret = finish_child($pid);
+# If the child process terminates before our copy_stdin() process is able to
+# write all of its data to $master_in, the copy_stdin() process could stall.
+# Send SIGTERM to it to ensure it terminates.
+kill 'TERM', $in_pid;
+finish_child($in_pid);
+exit($ret);
diff --git a/t/test4012.png b/t/test4012.png
deleted file mode 100644
index 7b181d1..0000000
--- a/t/test4012.png
+++ /dev/null
Binary files differ
diff --git a/t/valgrind/analyze.sh b/t/valgrind/analyze.sh
index d8105d9..2ffc80f 100755
--- a/t/valgrind/analyze.sh
+++ b/t/valgrind/analyze.sh
@@ -1,6 +1,10 @@
#!/bin/sh
-out_prefix=$(dirname "$0")/../test-results/valgrind.out
+# Get TEST_OUTPUT_DIRECTORY from GIT-BUILD-OPTIONS if it's there...
+. "$(dirname "$0")/../../GIT-BUILD-OPTIONS"
+# ... otherwise set it to the default value.
+: ${TEST_OUTPUT_DIRECTORY=$(dirname "$0")/..}
+
output=
count=0
total_count=0
@@ -115,7 +119,7 @@ handle_one () {
finish_output
}
-for test_script in "$(dirname "$0")"/../test-results/*.out
+for test_script in "$TEST_OUTPUT_DIRECTORY"/test-results/*.out
do
handle_one $test_script
done
diff --git a/t/valgrind/default.supp b/t/valgrind/default.supp
index 9e013fa..0a6724f 100644
--- a/t/valgrind/default.supp
+++ b/t/valgrind/default.supp
@@ -43,3 +43,9 @@
fun:write_buffer
fun:write_loose_object
}
+
+{
+ ignore-sse-strlen-invalid-read-size
+ Memcheck:Addr4
+ fun:copy_ref
+}
diff --git a/t/valgrind/valgrind.sh b/t/valgrind/valgrind.sh
index 582b4dc..4215303 100755
--- a/t/valgrind/valgrind.sh
+++ b/t/valgrind/valgrind.sh
@@ -2,20 +2,30 @@
base=$(basename "$0")
-TRACK_ORIGINS=
+TOOL_OPTIONS='--leak-check=no'
-VALGRIND_VERSION=$(valgrind --version)
-VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)')
-VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)')
-test 3 -gt "$VALGRIND_MAJOR" ||
-test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" ||
-TRACK_ORIGINS=--track-origins=yes
+test -z "$GIT_VALGRIND_ENABLED" &&
+exec "$GIT_VALGRIND"/../../"$base" "$@"
+
+case "$GIT_VALGRIND_MODE" in
+memcheck-fast)
+ ;;
+memcheck)
+ VALGRIND_VERSION=$(valgrind --version)
+ VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)')
+ VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)')
+ test 3 -gt "$VALGRIND_MAJOR" ||
+ test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" ||
+ TOOL_OPTIONS="$TOOL_OPTIONS --track-origins=yes"
+ ;;
+*)
+ TOOL_OPTIONS="--tool=$GIT_VALGRIND_MODE"
+esac
exec valgrind -q --error-exitcode=126 \
- --leak-check=no \
- --suppressions="$GIT_VALGRIND/default.supp" \
--gen-suppressions=all \
- $TRACK_ORIGINS \
+ --suppressions="$GIT_VALGRIND/default.supp" \
+ $TOOL_OPTIONS \
--log-fd=4 \
--input-fd=4 \
$GIT_VALGRIND_OPTIONS \