summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore33
-rw-r--r--.mailmap1
-rw-r--r--.travis.yml33
-rw-r--r--Documentation/Makefile4
-rw-r--r--Documentation/RelNotes/2.8.3.txt31
-rw-r--r--Documentation/RelNotes/2.8.4.txt27
-rw-r--r--Documentation/RelNotes/2.9.0.txt499
-rw-r--r--Documentation/SubmittingPatches80
-rw-r--r--Documentation/config.txt143
-rw-r--r--Documentation/diff-config.txt10
-rw-r--r--Documentation/diff-options.txt2
-rw-r--r--Documentation/everyday.txto2
-rw-r--r--Documentation/git-check-ignore.txt2
-rw-r--r--Documentation/git-clone.txt15
-rw-r--r--Documentation/git-commit-tree.txt4
-rw-r--r--Documentation/git-commit.txt3
-rw-r--r--Documentation/git-config.txt6
-rw-r--r--Documentation/git-filter-branch.txt2
-rw-r--r--Documentation/git-for-each-ref.txt2
-rw-r--r--Documentation/git-format-patch.txt60
-rw-r--r--Documentation/git-init.txt7
-rw-r--r--Documentation/git-log.txt8
-rw-r--r--Documentation/git-mailinfo.txt2
-rw-r--r--Documentation/git-merge.txt1
-rw-r--r--Documentation/git-notes.txt2
-rw-r--r--Documentation/git-p4.txt11
-rw-r--r--Documentation/git-pack-objects.txt3
-rw-r--r--Documentation/git-pull.txt9
-rw-r--r--Documentation/git-rebase.txt6
-rw-r--r--Documentation/git-repack.txt8
-rw-r--r--Documentation/git-send-email.txt13
-rw-r--r--Documentation/git-submodule.txt26
-rw-r--r--Documentation/git-worktree.txt8
-rw-r--r--Documentation/git.txt7
-rw-r--r--Documentation/gitcredentials.txt5
-rw-r--r--Documentation/githooks.txt73
-rw-r--r--Documentation/glossary-content.txt2
-rwxr-xr-xDocumentation/lint-gitlink.perl71
-rw-r--r--Documentation/merge-options.txt15
-rw-r--r--Documentation/pretty-formats.txt4
-rw-r--r--Documentation/pretty-options.txt14
-rw-r--r--Documentation/technical/api-credentials.txt4
-rw-r--r--Documentation/technical/api-parse-options.txt8
-rw-r--r--Documentation/technical/pack-protocol.txt2
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile50
-rw-r--r--README.md7
l---------RelNotes2
-rw-r--r--archive-tar.c2
-rw-r--r--bisect.c7
-rw-r--r--branch.c18
-rw-r--r--branch.h2
-rw-r--r--builtin/am.c11
-rw-r--r--builtin/apply.c2
-rw-r--r--builtin/branch.c48
-rw-r--r--builtin/checkout.c5
-rw-r--r--builtin/clone.c26
-rw-r--r--builtin/commit-tree.c4
-rw-r--r--builtin/commit.c11
-rw-r--r--builtin/diff-files.c1
-rw-r--r--builtin/diff-index.c1
-rw-r--r--builtin/diff-tree.c2
-rw-r--r--builtin/diff.c2
-rw-r--r--builtin/fetch.c6
-rw-r--r--builtin/fsck.c3
-rw-r--r--builtin/grep.c6
-rw-r--r--builtin/help.c8
-rw-r--r--builtin/index-pack.c4
-rw-r--r--builtin/log.c185
-rw-r--r--builtin/mailsplit.c10
-rw-r--r--builtin/merge-file.c10
-rw-r--r--builtin/merge-tree.c18
-rw-r--r--builtin/merge.c25
-rw-r--r--builtin/mv.c21
-rw-r--r--builtin/name-rev.c19
-rw-r--r--builtin/notes.c13
-rw-r--r--builtin/pack-objects.c19
-rw-r--r--builtin/pull.c50
-rw-r--r--builtin/receive-pack.c55
-rw-r--r--builtin/reflog.c4
-rw-r--r--builtin/remote-ext.c2
-rw-r--r--builtin/remote.c2
-rw-r--r--builtin/rm.c4
-rw-r--r--builtin/submodule--helper.c607
-rw-r--r--builtin/tag.c28
-rw-r--r--builtin/update-index.c2
-rw-r--r--builtin/upload-archive.c3
-rw-r--r--builtin/verify-tag.c61
-rw-r--r--builtin/worktree.c35
-rw-r--r--bundle.c23
-rw-r--r--cache-tree.c4
-rw-r--r--cache.h20
-rw-r--r--check-racy.c2
-rwxr-xr-xci/test-documentation.sh14
-rw-r--r--combine-diff.c3
-rw-r--r--commit.h1
-rw-r--r--compat/mingw.c74
-rw-r--r--compat/mingw.h4
-rw-r--r--compat/precompose_utf8.c2
-rw-r--r--compat/win32/pthread.h7
-rw-r--r--compat/win32/syslog.c6
-rw-r--r--compat/win32mmap.c31
-rw-r--r--compat/winansi.c58
-rw-r--r--config.c44
-rw-r--r--config.mak.uname3
-rw-r--r--connected.c9
-rw-r--r--contrib/completion/git-completion.bash7
-rw-r--r--contrib/hooks/multimail/CHANGES8
-rw-r--r--contrib/hooks/multimail/README2
-rw-r--r--contrib/hooks/multimail/README.Git4
-rwxr-xr-xcontrib/hooks/multimail/git_multimail.py10
-rw-r--r--convert.c19
-rw-r--r--copy.c6
-rw-r--r--credential-cache--daemon.c4
-rw-r--r--credential-cache.c1
-rw-r--r--credential.c9
-rw-r--r--diff-no-index.c3
-rw-r--r--diff.c16
-rw-r--r--diff.h1
-rw-r--r--dir.c20
-rw-r--r--dir.h5
-rw-r--r--editor.c3
-rw-r--r--entry.c10
-rw-r--r--environment.c2
-rw-r--r--fast-import.c16
-rw-r--r--fetch-pack.c4
-rw-r--r--fsck.c26
-rwxr-xr-xgit-add--interactive.perl12
-rw-r--r--git-compat-util.h2
-rwxr-xr-xgit-cvsserver.perl6
-rwxr-xr-xgit-difftool--helper.sh4
-rwxr-xr-xgit-difftool.perl9
-rwxr-xr-xgit-merge-octopus.sh6
-rw-r--r--git-mergetool--lib.sh27
-rwxr-xr-xgit-mergetool.sh2
-rwxr-xr-xgit-p4.py34
-rw-r--r--git-parse-remote.sh46
-rw-r--r--git-rebase--interactive.sh8
-rw-r--r--git-rebase--merge.sh4
-rwxr-xr-xgit-rebase.sh12
-rwxr-xr-xgit-send-email.perl5
-rwxr-xr-xgit-stash.sh4
-rwxr-xr-xgit-submodule.sh239
-rw-r--r--git.spec.in330
-rwxr-xr-xgitweb/gitweb.perl3
-rw-r--r--gpg-interface.c8
-rw-r--r--grep.c4
-rw-r--r--http-backend.c4
-rw-r--r--http-push.c14
-rw-r--r--http.c47
-rw-r--r--http.h1
-rw-r--r--ident.c24
-rw-r--r--list-objects.c6
-rw-r--r--ll-merge.c12
-rw-r--r--log-tree.c1
-rw-r--r--mailmap.c3
-rw-r--r--match-trees.c60
-rw-r--r--merge-recursive.c12
-rw-r--r--mergetools/examdiff18
-rw-r--r--mergetools/winmerge21
-rw-r--r--notes.c4
-rw-r--r--parse-options.c2
-rw-r--r--patch-ids.c2
-rw-r--r--patch-ids.h2
-rw-r--r--path.c55
-rw-r--r--perl/Git.pm2
-rw-r--r--perl/Git/SVN.pm3
-rw-r--r--pretty.c90
-rw-r--r--quote.c13
-rw-r--r--quote.h3
-rw-r--r--reachable.c3
-rw-r--r--refs.c149
-rw-r--r--refs/files-backend.c406
-rw-r--r--refs/refs-internal.h15
-rw-r--r--remote-curl.c4
-rw-r--r--remote.c4
-rw-r--r--rerere.c659
-rw-r--r--rerere.h4
-rw-r--r--revision.c18
-rw-r--r--revision.h2
-rw-r--r--run-command.c42
-rw-r--r--run-command.h11
-rw-r--r--send-pack.c7
-rw-r--r--sequencer.c3
-rw-r--r--server-info.c2
-rw-r--r--sha1_file.c34
-rw-r--r--sha1_name.c9
-rw-r--r--split-index.c2
-rw-r--r--strbuf.c6
-rw-r--r--strbuf.h6
-rw-r--r--string-list.c8
-rw-r--r--submodule-config.c31
-rw-r--r--submodule-config.h2
-rw-r--r--submodule.c83
-rw-r--r--submodule.h26
-rw-r--r--t/README6
-rw-r--r--t/helper/.gitignore33
-rw-r--r--t/helper/test-chmtime.c (renamed from test-chmtime.c)0
-rw-r--r--t/helper/test-config.c (renamed from test-config.c)0
-rw-r--r--t/helper/test-ctype.c (renamed from test-ctype.c)0
-rw-r--r--t/helper/test-date.c (renamed from test-date.c)0
-rw-r--r--t/helper/test-delta.c (renamed from test-delta.c)0
-rw-r--r--t/helper/test-dump-cache-tree.c (renamed from test-dump-cache-tree.c)0
-rw-r--r--t/helper/test-dump-split-index.c (renamed from test-dump-split-index.c)0
-rw-r--r--t/helper/test-dump-untracked-cache.c (renamed from test-dump-untracked-cache.c)0
-rw-r--r--t/helper/test-fake-ssh.c (renamed from test-fake-ssh.c)0
-rw-r--r--t/helper/test-genrandom.c (renamed from test-genrandom.c)0
-rw-r--r--t/helper/test-hashmap.c (renamed from test-hashmap.c)0
-rw-r--r--t/helper/test-index-version.c (renamed from test-index-version.c)0
-rw-r--r--t/helper/test-line-buffer.c (renamed from test-line-buffer.c)0
-rw-r--r--t/helper/test-match-trees.c (renamed from test-match-trees.c)14
-rw-r--r--t/helper/test-mergesort.c (renamed from test-mergesort.c)0
-rw-r--r--t/helper/test-mktemp.c (renamed from test-mktemp.c)0
-rw-r--r--t/helper/test-parse-options.c (renamed from test-parse-options.c)107
-rw-r--r--t/helper/test-path-utils.c (renamed from test-path-utils.c)0
-rw-r--r--t/helper/test-prio-queue.c (renamed from test-prio-queue.c)0
-rw-r--r--t/helper/test-read-cache.c (renamed from test-read-cache.c)0
-rw-r--r--t/helper/test-regex.c (renamed from test-regex.c)0
-rw-r--r--t/helper/test-revision-walking.c (renamed from test-revision-walking.c)0
-rw-r--r--t/helper/test-run-command.c (renamed from test-run-command.c)0
-rw-r--r--t/helper/test-scrap-cache-tree.c (renamed from test-scrap-cache-tree.c)0
-rw-r--r--t/helper/test-sha1-array.c (renamed from test-sha1-array.c)0
-rw-r--r--t/helper/test-sha1.c (renamed from test-sha1.c)0
-rwxr-xr-xt/helper/test-sha1.sh (renamed from test-sha1.sh)4
-rw-r--r--t/helper/test-sigchain.c (renamed from test-sigchain.c)0
-rw-r--r--t/helper/test-string-list.c (renamed from test-string-list.c)0
-rw-r--r--t/helper/test-submodule-config.c (renamed from test-submodule-config.c)0
-rw-r--r--t/helper/test-subprocess.c (renamed from test-subprocess.c)0
-rw-r--r--t/helper/test-svn-fe.c (renamed from test-svn-fe.c)0
-rw-r--r--t/helper/test-urlmatch-normalization.c (renamed from test-urlmatch-normalization.c)0
-rw-r--r--t/helper/test-wildmatch.c (renamed from test-wildmatch.c)0
-rw-r--r--t/lib-git-p4.sh40
-rw-r--r--t/lib-git-svn.sh3
-rwxr-xr-xt/lib-gpg.sh5
-rw-r--r--t/lib-httpd/apache.conf16
-rwxr-xr-xt/perf/p3404-rebase-interactive.sh36
-rw-r--r--t/perf/perf-lib.sh19
-rwxr-xr-xt/t0000-basic.sh2
-rwxr-xr-xt/t0001-init.sh30
-rwxr-xr-xt/t0008-ignores.sh4
-rwxr-xr-xt/t0027-auto-crlf.sh301
-rwxr-xr-xt/t0040-parse-options.sh279
-rwxr-xr-xt/t0060-path-utils.sh46
-rwxr-xr-xt/t0300-credentials.sh11
-rwxr-xr-xt/t1300-repo-config.sh14
-rwxr-xr-xt/t1350-config-hooks-path.sh37
-rwxr-xr-xt/t1410-reflog.sh10
-rwxr-xr-xt/t1430-bad-ref-name.sh132
-rwxr-xr-xt/t1450-fsck.sh18
-rwxr-xr-xt/t2025-worktree-add.sh71
-rwxr-xr-xt/t3033-merge-toplevel.sh16
-rwxr-xr-xt/t3402-rebase-merge.sh9
-rwxr-xr-xt/t3404-rebase-interactive.sh21
-rwxr-xr-xt/t3412-rebase-root.sh2
-rwxr-xr-xt/t3419-rebase-patch-id.sh12
-rwxr-xr-xt/t3421-rebase-topology-linear.sh4
-rwxr-xr-xt/t3513-revert-submodule.sh4
-rwxr-xr-xt/t3910-mac-os-precompose.sh42
-rwxr-xr-xt/t4001-diff-rename.sh125
-rwxr-xr-xt/t4013-diff-various.sh2
-rwxr-xr-xt/t4014-format-patch.sh109
-rwxr-xr-xt/t4047-diff-dirstat.sh3
-rwxr-xr-xt/t4151-am-abort.sh2
-rwxr-xr-xt/t4200-rerere.sh170
-rwxr-xr-xt/t4201-shortlog.sh2
-rwxr-xr-xt/t4202-log.sh8
-rwxr-xr-xt/t4204-patch-id.sh6
-rwxr-xr-xt/t4213-log-tabexpand.sh105
-rwxr-xr-xt/t5500-fetch-pack.sh6
-rwxr-xr-xt/t5504-fetch-receive-strict.sh10
-rwxr-xr-xt/t5510-fetch.sh1
-rwxr-xr-xt/t5520-pull.sh74
-rwxr-xr-xt/t5521-pull-options.sh21
-rwxr-xr-xt/t5526-fetch-submodules.sh14
-rwxr-xr-xt/t5550-http-fetch-dumb.sh49
-rwxr-xr-xt/t5551-http-fetch-smart.sh17
-rwxr-xr-xt/t5601-clone.sh4
-rwxr-xr-xt/t5611-clone-config.sh20
-rwxr-xr-xt/t5614-clone-submodules.sh85
-rwxr-xr-xt/t6009-rev-list-parent.sh4
-rwxr-xr-xt/t6010-merge-base.sh6
-rwxr-xr-xt/t6012-rev-list-simplify.sh2
-rwxr-xr-xt/t6024-recursive-merge.sh2
-rwxr-xr-xt/t6026-merge-attr.sh3
-rwxr-xr-xt/t6029-merge-subtree.sh2
-rwxr-xr-xt/t6036-recursive-corner-cases.sh86
-rwxr-xr-xt/t6041-bisect-submodule.sh4
-rwxr-xr-xt/t6044-merge-unrelated-index-changes.sh153
-rwxr-xr-xt/t6101-rev-parse-parents.sh2
-rwxr-xr-xt/t6302-for-each-ref-filter.sh72
-rwxr-xr-xt/t7001-mv.sh16
-rwxr-xr-xt/t7004-tag.sh41
-rwxr-xr-xt/t7030-verify-tag.sh13
-rwxr-xr-xt/t7300-clean.sh2
-rwxr-xr-xt/t7400-submodule-basic.sh46
-rwxr-xr-xt/t7406-submodule-update.sh51
-rwxr-xr-xt/t7501-commit.sh20
-rwxr-xr-xt/t7507-commit-verbose.sh72
-rwxr-xr-xt/t7510-signed-commit.sh13
-rwxr-xr-xt/t7605-merge-resolve.sh6
-rwxr-xr-xt/t7609-merge-co-error-msgs.sh18
-rwxr-xr-xt/t7800-difftool.sh25
-rwxr-xr-xt/t9100-git-svn-basic.sh66
-rwxr-xr-xt/t9101-git-svn-props.sh12
-rwxr-xr-xt/t9102-git-svn-deep-rmdir.sh2
-rwxr-xr-xt/t9103-git-svn-tracked-directory-removed.sh22
-rwxr-xr-xt/t9106-git-svn-commit-diff-clobber.sh6
-rwxr-xr-xt/t9107-git-svn-migrate.sh61
-rwxr-xr-xt/t9110-git-svn-use-svm-props.sh18
-rwxr-xr-xt/t9111-git-svn-use-svnsync-props.sh18
-rwxr-xr-xt/t9120-git-svn-clone-with-percent-escapes.sh6
-rwxr-xr-xt/t9123-git-svn-rebuild-with-rewriteroot.sh2
-rwxr-xr-xt/t9153-git-svn-rewrite-uuid.sh4
-rwxr-xr-xt/t9400-git-cvsserver-server.sh3
-rwxr-xr-xt/t9802-git-p4-filetype.sh6
-rwxr-xr-xt/t9824-git-p4-git-lfs.sh6
-rwxr-xr-xt/t9826-git-p4-keep-empty-commits.sh60
-rwxr-xr-xt/t9828-git-p4-map-user.sh61
-rwxr-xr-xt/t9829-git-p4-jobs.sh99
-rwxr-xr-xt/t9903-bash-prompt.sh2
-rw-r--r--t/test-lib-functions.sh28
-rw-r--r--t/test-lib.sh27
-rw-r--r--tag.c53
-rw-r--r--tag.h2
-rw-r--r--transport-helper.c8
-rw-r--r--tree-diff.c8
-rw-r--r--tree-walk.c16
-rw-r--r--tree-walk.h8
-rw-r--r--tree.c10
-rw-r--r--unpack-trees.c84
-rw-r--r--upload-pack.c3
-rw-r--r--usage.c48
-rw-r--r--utf8.h2
-rw-r--r--vcs-svn/line_buffer.c4
-rw-r--r--vcs-svn/sliding_window.c2
-rw-r--r--vcs-svn/svndiff.c4
-rw-r--r--vcs-svn/svndump.c4
-rw-r--r--walker.c4
-rw-r--r--wildmatch.c2
-rw-r--r--worktree.c124
-rw-r--r--worktree.h28
-rw-r--r--wrap-for-bin.sh1
-rw-r--r--wrapper.c21
-rw-r--r--wt-status.c68
-rw-r--r--wt-status.h6
-rw-r--r--xdiff/xdiff.h2
-rw-r--r--xdiff/xdiffi.c40
347 files changed, 7046 insertions, 2707 deletions
diff --git a/.gitignore b/.gitignore
index 5087ce1eb7..05cb58a3d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -179,39 +179,6 @@
/gitweb/gitweb.cgi
/gitweb/static/gitweb.js
/gitweb/static/gitweb.min.*
-/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
/common-cmds.h
*.tar.gz
*.dsc
diff --git a/.mailmap b/.mailmap
index e5b4126bec..a9162c0095 100644
--- a/.mailmap
+++ b/.mailmap
@@ -51,6 +51,7 @@ Dirk SΓΌsserott <newsletter@dirk.my1.cc>
Eric Blake <eblake@redhat.com> <ebb9@byu.net>
Eric Hanchrow <eric.hanchrow@gmail.com> <offby1@blarg.net>
Eric S. Raymond <esr@thyrsus.com>
+Eric Wong <e@80x24.org> <normalperson@yhbt.net>
Erik Faye-Lund <kusmabite@gmail.com> <kusmabite@googlemail.com>
Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com> <eyvind-git@orakel.ntnu.no>
Florian Achleitner <florian.achleitner.2.6.31@gmail.com> <florian.achleitner2.6.31@gmail.com>
diff --git a/.travis.yml b/.travis.yml
index 78e433ba71..c20ec540c8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,12 +18,16 @@ addons:
apt:
packages:
- language-pack-is
+ - git-svn
env:
global:
- DEVELOPER=1
- - P4_VERSION="15.2"
- - GIT_LFS_VERSION="1.1.0"
+ # The Linux build installs the defined dependency versions below.
+ # The OS X build installs the latest available versions. Keep that
+ # in mind when you encounter a broken OS X build!
+ - LINUX_P4_VERSION="16.1"
+ - LINUX_GIT_LFS_VERSION="1.2.0"
- DEFAULT_TEST_TARGET=prove
- GIT_PROVE_OPTS="--timer --jobs 3 --state=failed,slow,save"
- GIT_TEST_OPTS="--verbose --tee"
@@ -32,23 +36,38 @@ env:
# t9816 occasionally fails with "TAP out of sequence errors" on Travis CI OS X
- GIT_SKIP_TESTS="t9810 t9816"
+matrix:
+ include:
+ - env: Documentation
+ os: linux
+ compiler: clang
+ addons:
+ apt:
+ packages:
+ - asciidoc
+ - xmlto
+ before_install:
+ before_script:
+ script: ci/test-documentation.sh
+ after_failure:
+
before_install:
- >
case "${TRAVIS_OS_NAME:-linux}" in
linux)
mkdir --parents custom/p4
pushd custom/p4
- wget --quiet http://filehost.perforce.com/perforce/r$P4_VERSION/bin.linux26x86_64/p4d
- wget --quiet http://filehost.perforce.com/perforce/r$P4_VERSION/bin.linux26x86_64/p4
+ wget --quiet http://filehost.perforce.com/perforce/r$LINUX_P4_VERSION/bin.linux26x86_64/p4d
+ wget --quiet http://filehost.perforce.com/perforce/r$LINUX_P4_VERSION/bin.linux26x86_64/p4
chmod u+x p4d
chmod u+x p4
export PATH="$(pwd):$PATH"
popd
mkdir --parents custom/git-lfs
pushd custom/git-lfs
- wget --quiet https://github.com/github/git-lfs/releases/download/v$GIT_LFS_VERSION/git-lfs-linux-amd64-$GIT_LFS_VERSION.tar.gz
- tar --extract --gunzip --file "git-lfs-linux-amd64-$GIT_LFS_VERSION.tar.gz"
- cp git-lfs-$GIT_LFS_VERSION/git-lfs .
+ wget --quiet https://github.com/github/git-lfs/releases/download/v$LINUX_GIT_LFS_VERSION/git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz
+ tar --extract --gunzip --file "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
+ cp git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs .
export PATH="$(pwd):$PATH"
popd
;;
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 3e39e2815b..f6e288bc63 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -204,6 +204,7 @@ ifndef V
QUIET_DBLATEX = @echo ' ' DBLATEX $@;
QUIET_XSLTPROC = @echo ' ' XSLTPROC $@;
QUIET_GEN = @echo ' ' GEN $@;
+ QUIET_LINT = @echo ' ' LINT $@;
QUIET_STDERR = 2> /dev/null
QUIET_SUBDIR0 = +@subdir=
QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
@@ -427,4 +428,7 @@ quick-install-html: require-htmlrepo
print-man1:
@for i in $(MAN1_TXT); do echo $$i; done
+lint-docs::
+ $(QUIET_LINT)$(PERL_PATH) lint-gitlink.perl
+
.PHONY: FORCE
diff --git a/Documentation/RelNotes/2.8.3.txt b/Documentation/RelNotes/2.8.3.txt
index af184783bc..fedd9968e5 100644
--- a/Documentation/RelNotes/2.8.3.txt
+++ b/Documentation/RelNotes/2.8.3.txt
@@ -67,4 +67,35 @@ Fixes since v2.8.2
recurses into, but this was incorrect when the command was not run
from the root level of the superproject.
+ * The test scripts for "git p4" (but not "git p4" implementation
+ itself) has been updated so that they would work even on a system
+ where the installed version of Python is python 3.
+
+ * The "user.useConfigOnly" configuration variable makes it an error
+ if users do not explicitly set user.name and user.email. However,
+ its check was not done early enough and allowed another error to
+ trigger, reporting that the default value we guessed from the
+ system setting was unusable. This was a suboptimal end-user
+ experience as we want the users to set user.name/user.email without
+ relying on the auto-detection at all.
+
+ * "git mv old new" did not adjust the path for a submodule that lives
+ as a subdirectory inside old/ directory correctly.
+
+ * "git push" from a corrupt repository that attempts to push a large
+ number of refs deadlocked; the thread to relay rejection notices
+ for these ref updates blocked on writing them to the main thread,
+ after the main thread at the receiving end notices that the push
+ failed and decides not to read these notices and return a failure.
+
+ * A question by "git send-email" to ask the identity of the sender
+ has been updated.
+
+ * Recent update to Git LFS broke "git p4" by changing the output from
+ its "lfs pointer" subcommand.
+
+ * Some multi-byte encoding can have a backslash byte as a later part
+ of one letter, which would confuse "highlight" filter used in
+ gitweb.
+
Also contains minor documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.8.4.txt b/Documentation/RelNotes/2.8.4.txt
new file mode 100644
index 0000000000..6005548fd6
--- /dev/null
+++ b/Documentation/RelNotes/2.8.4.txt
@@ -0,0 +1,27 @@
+Git v2.8.4 Release Notes
+========================
+
+Fixes since v2.8.3
+------------------
+
+ * Documentation for "git merge --verify-signatures" has been updated
+ to clarify that the signature of only the commit at the tip is
+ verified. Also the phrasing used for signature and key validity is
+ adjusted to align with that used by OpenPGP.
+
+ * On Windows, .git and optionally any files whose name starts with a
+ dot are now marked as hidden, with a core.hideDotFiles knob to
+ customize this behaviour.
+
+ * Portability enhancement for "rebase -i" to help platforms whose
+ shell does not like "for i in <empty>" (which is not POSIX-kosher).
+
+ * "git fsck" learned to catch NUL byte in a commit object as
+ potential error and warn.
+
+ * CI test was taught to build documentation pages.
+
+ * Many 'linkgit:<git documentation page>' references were broken,
+ which are all fixed with this.
+
+Also contains other minor documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.9.0.txt b/Documentation/RelNotes/2.9.0.txt
new file mode 100644
index 0000000000..1bfcce06c5
--- /dev/null
+++ b/Documentation/RelNotes/2.9.0.txt
@@ -0,0 +1,499 @@
+Git 2.9 Release Notes
+=====================
+
+Backward compatibility note
+---------------------------
+
+The end-user facing Porcelain level commands in the "git diff" and
+"git log" by default enables the rename detection; you can still use
+"diff.renames" configuration variable to disable this.
+
+Merging two branches that have no common ancestor with "git merge" is
+by default forbidden now to prevent creating such an unusual merge by
+mistake.
+
+The output formats of "git log" that indents the commit log message by
+4 spaces now expands HT in the log message by default. You can use
+the "--no-expand-tabs" option to disable this.
+
+"git commit-tree" plumbing command required the user to always sign
+its result when the user sets the commit.gpgsign configuration
+variable, which was an ancient mistake, which this release corrects.
+A script that drives commit-tree, if it relies on this mistake, now
+needs to read commit.gpgsign and pass the -S option as necessary.
+
+
+Updates since v2.8
+------------------
+
+UI, Workflows & Features
+
+ * Comes with git-multimail 1.3.1 (in contrib/).
+
+ * The end-user facing Porcelain level commands like "diff" and "log"
+ now enables the rename detection by default.
+
+ * The credential.helper configuration variable is cumulative and
+ there is no good way to override it from the command line. As
+ a special case, giving an empty string as its value now serves
+ as the signal to clear the values specified in various files.
+
+ * A new "interactive.diffFilter" configuration can be used to
+ customize the diff shown in "git add -i" session.
+
+ * "git p4" now allows P4 author names to be mapped to Git author
+ names.
+
+ * "git rebase -x" can be used without passing "-i" option.
+
+ * "git -c credential.<var>=<value> submodule" can now be used to
+ propagate configuration variables related to credential helper
+ down to the submodules.
+
+ * "git tag" can create an annotated tag without explicitly given an
+ "-a" (or "-s") option (i.e. when a tag message is given). A new
+ configuration variable, tag.forceSignAnnotated, can be used to tell
+ the command to create signed tag in such a situation.
+
+ * "git merge" used to allow merging two branches that have no common
+ base by default, which led to a brand new history of an existing
+ project created and then get pulled by an unsuspecting maintainer,
+ which allowed an unnecessary parallel history merged into the
+ existing project. The command has been taught not to allow this by
+ default, with an escape hatch "--allow-unrelated-histories" option
+ to be used in a rare event that merges histories of two projects
+ that started their lives independently.
+
+ * "git pull" has been taught to pass --allow-unrelated-histories
+ option to underlying "git merge".
+
+ * "git apply -v" learned to report paths in the patch that were
+ skipped via --include/--exclude mechanism or being outside the
+ current working directory.
+
+ * Shell completion (in contrib/) updates.
+
+ * The commit object name reported when "rebase -i" stops has been
+ shortened.
+
+ * "git worktree add" can be given "--no-checkout" option to only
+ create an empty worktree without checking out the files.
+
+ * "git mergetools" learned to drive ExamDiff.
+
+ * "git pull --rebase" learned "--[no-]autostash" option, so that
+ the rebase.autostash configuration variable set to true can be
+ overridden from the command line.
+
+ * When "git log" shows the log message indented by 4-spaces, the
+ remainder of a line after a HT does not align in the way the author
+ originally intended. The command now expands tabs by default in
+ such a case, and allows the users to override it with a new option,
+ "--no-expand-tabs".
+
+ * "git send-email" now uses a more readable timestamps when
+ formulating a message ID.
+
+ * "git rerere" can encounter two or more files with the same conflict
+ signature that have to be resolved in different ways, but there was
+ no way to record these separate resolutions.
+ (merge d9d501b068 jc/rerere-multi later to maint).
+
+ * "git p4" learned to record P4 jobs in Git commit that imports from
+ the history in Perforce.
+
+ * "git describe --contains" often made a hard-to-justify choice of
+ tag to give name to a given commit, because it tried to come up
+ with a name with smallest number of hops from a tag, causing an old
+ commit whose close descendant that is recently tagged were not
+ described with respect to an old tag but with a newer tag. It did
+ not help that its computation of "hop" count was further tweaked to
+ penalize being on a side branch of a merge. The logic has been
+ updated to favor using the tag with the oldest tagger date, which
+ is a lot easier to explain to the end users: "We describe a commit
+ in terms of the (chronologically) oldest tag that contains the
+ commit."
+ (merge 7550424 js/name-rev-use-oldest-ref later to maint).
+
+ * "git clone" learned "--shallow-submodules" option.
+
+ * HTTP transport clients learned to throw extra HTTP headers at the
+ server, specified via http.extraHeader configuration variable.
+
+ * Patch output from "git diff" and friends has been tweaked to be
+ more readable by using a blank line as a strong hint that the
+ contents before and after it belong to a logically separate unit.
+
+ * A new configuration variable core.hooksPath allows customizing
+ where the hook directory is.
+
+ * An earlier addition of "sanitize_submodule_env" with 14111fc4 (git:
+ submodule honor -c credential.* from command line, 2016-02-29)
+ turned out to be a convoluted no-op; implement what it wanted to do
+ correctly, and stop filtering settings given via "git -c var=val".
+
+ * "git commit --dry-run" reported "No, no, you cannot commit." in one
+ case where "git commit" would have allowed you to commit, and this
+ improves it a little bit ("git commit --dry-run --short" still does
+ not give you the correct answer, for example). This is a stop-gap
+ measure in that "commit --short --dry-run" still gives an incorrect
+ result.
+
+ * The experimental "multiple worktree" feature gains more safety to
+ forbid operations on a branch that is checked out or being actively
+ worked on elsewhere, by noticing that e.g. it is being rebased.
+
+ * "git format-patch" learned a new "--base" option to record what
+ (public, well-known) commit the original series was built on in
+ its output.
+
+ * "git commit" learned to pay attention to "commit.verbose"
+ configuration variable and act as if "--verbose" option was
+ given from the command line.
+
+ * Give hints to GMail users with two-factor auth enabled that
+ they need app-specific-password when using send-email.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * The embedded args argv-array in the child process is used to build
+ the command line to run pack-objects instead of using a separate
+ array of strings.
+
+ * A test for tags has been restructured so that more parts of it can
+ easily be run on a platform without a working GnuPG.
+
+ * The startup_info data, which records if we are working inside a
+ repository (among other things), are now uniformly available to Git
+ subcommand implementations, and Git avoids attempting to touch
+ references when we are not in a repository.
+
+ * The command line argument parser for "receive-pack" has been
+ rewritten to use parse-options.
+
+ * A major part of "git submodule update" has been ported to C to take
+ advantage of the recently added framework to run download tasks in
+ parallel.
+
+ * Rename bunch of tests on "git clone" for better organization.
+
+ * The tests that involve running httpd leaked the system-wide
+ configuration in /etc/gitconfig to the tested environment.
+
+ * Build updates for MSVC.
+
+ * The repository set-up sequence has been streamlined (the biggest
+ change is that there is no longer git_config_early()), so that we
+ do not attempt to look into refs/* when we know we do not have a
+ Git repository.
+
+ * Code restructuring around the "refs" area to prepare for pluggable
+ refs backends.
+
+ * Sources to many test helper binaries (and the generated helpers)
+ have been moved to t/helper/ subdirectory to reduce clutter at the
+ top level of the tree.
+
+ * Unify internal logic between "git tag -v" and "git verify-tag"
+ commands by making one directly call into the other.
+ (merge bef234b st/verify-tag later to maint).
+
+ * "merge-recursive" strategy incorrectly checked if a path that is
+ involved in its internal merge exists in the working tree.
+
+ * The test scripts for "git p4" (but not "git p4" implementation
+ itself) has been updated so that they would work even on a system
+ where the installed version of Python is python 3.
+
+ * As nobody maintains our in-tree git.spec.in and distros use their
+ own spec file, we stopped pretending that we support "make rpm".
+
+ * Move from unsigned char[20] to struct object_id continues.
+
+ * Update of "git submodule" to move pieces of logic to C continues.
+
+ * The code for warning_errno/die_errno has been refactored and a new
+ error_errno() reporting helper is introduced.
+ (merge 1da045f nd/error-errno later to maint).
+
+ * Running tests with '-x' option to trace the individual command
+ executions is a useful way to debug test scripts, but some tests
+ that capture the standard error stream and check what the command
+ said can be broken with the trace output mixed in. When running
+ our tests under "bash", however, we can redirect the trace output
+ to another file descriptor to keep the standard error of programs
+ being tested intact.
+ (merge d88785e jk/test-send-sh-x-trace-elsewhere later to maint).
+
+ * t0040 had too many unnecessary repetitions in its test data. Teach
+ test-parse-options program so that a caller can tell what it
+ expects in its output, so that these repetitions can be cleaned up.
+
+ * Add perf test for "rebase -i"
+
+ * Common mistakes when writing gitlink: in our documentation are
+ found by "make check-docs".
+
+ * t9xxx series has been updated primarily for readability, while
+ fixing small bugs in it. A few scripted Porcelains have also been
+ updated to fix possible bugs around their use of "test -z" and
+ "test -n".
+
+ * CI test was taught to run git-svn tests.
+
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v2.8
+----------------
+
+Unless otherwise noted, all the fixes since v2.8 in the maintenance
+track are contained in this release (see the maintenance releases'
+notes for details).
+
+ * "git config --get-urlmatch", unlike other variants of the "git
+ config --get" family, did not signal error with its exit status
+ when there was no matching configuration.
+
+ * The "--local-env-vars" and "--resolve-git-dir" options of "git
+ rev-parse" failed to work outside a repository when the command's
+ option parsing was rewritten in 1.8.5 era.
+
+ * "git index-pack --keep[=<msg>] pack-$name.pack" simply did not work.
+
+ * Fetching of history by naming a commit object name directly didn't
+ work across remote-curl transport.
+
+ * A small memory leak in an error codepath has been plugged in xdiff
+ code.
+
+ * strbuf_getwholeline() did not NUL-terminate the buffer on certain
+ corner cases in its error codepath.
+
+ * "git mergetool" did not work well with conflicts that both sides
+ deleted.
+
+ * "git send-email" had trouble parsing alias file in mailrc format
+ when lines in it had trailing whitespaces on them.
+
+ * When "git merge --squash" stopped due to conflict, the concluding
+ "git commit" failed to read in the SQUASH_MSG that shows the log
+ messages from all the squashed commits.
+
+ * "git merge FETCH_HEAD" dereferenced NULL pointer when merging
+ nothing into an unborn history (which is arguably unusual usage,
+ which perhaps was the reason why nobody noticed it).
+
+ * When "git worktree" feature is in use, "git branch -d" allowed
+ deletion of a branch that is checked out in another worktree,
+ which was wrong.
+
+ * When "git worktree" feature is in use, "git branch -m" renamed a
+ branch that is checked out in another worktree without adjusting
+ the HEAD symbolic ref for the worktree.
+
+ * "git diff -M" used to work better when two originally identical
+ files A and B got renamed to X/A and X/B by pairing A to X/A and B
+ to X/B, but this was broken in the 2.0 timeframe.
+
+ * "git send-pack --all <there>" was broken when its command line
+ option parsing was written in the 2.6 timeframe.
+
+ * "git format-patch --help" showed `-s` and `--no-patch` as if these
+ are valid options to the command. We already hide `--patch` option
+ from the documentation, because format-patch is about showing the
+ diff, and the documentation now hides these options as well.
+
+ * When running "git blame $path" with unnormalized data in the index
+ for the path, the data in the working tree was blamed, even though
+ "git add" would not have changed what is already in the index, due
+ to "safe crlf" that disables the line-end conversion. It has been
+ corrected.
+
+ * A change back in version 2.7 to "git branch" broke display of a
+ symbolic ref in a non-standard place in the refs/ hierarchy (we
+ expect symbolic refs to appear in refs/remotes/*/HEAD to point at
+ the primary branch the remote has, and as .git/HEAD to point at the
+ branch we locally checked out).
+
+ * A partial rewrite of "git submodule" in the 2.7 timeframe changed
+ the way the gitdir: pointer in the submodules point at the real
+ repository location to use absolute paths by accident. This has
+ been corrected.
+
+ * "git commit" misbehaved in a few minor ways when an empty message
+ is given via -m '', all of which has been corrected.
+
+ * Support for CRAM-MD5 authentication method in "git imap-send" did
+ not work well.
+
+ * Upcoming OpenSSL 1.1.0 will break compilation b updating a few APIs
+ we use in imap-send, which has been adjusted for the change.
+
+ * The socks5:// proxy support added back in 2.6.4 days was not aware
+ that socks5h:// proxies behave differently.
+
+ * "git config" had a codepath that tried to pass a NULL to
+ printf("%s"), which nobody seems to have noticed.
+
+ * On Cygwin, object creation uses the "create a temporary and then
+ rename it to the final name" pattern, not "create a temporary,
+ hardlink it to the final name and then unlink the temporary"
+ pattern.
+
+ This is necessary to use Git on Windows shared directories, and is
+ already enabled for the MinGW and plain Windows builds. It also
+ has been used in Cygwin packaged versions of Git for quite a while.
+ See http://thread.gmane.org/gmane.comp.version-control.git/291853
+
+ * "merge-octopus" strategy did not ensure that the index is clean
+ when merge begins.
+
+ * When "git merge" notices that the merge can be resolved purely at
+ the tree level (without having to merge blobs) and the resulting
+ tree happens to already exist in the object store, it forgot to
+ update the index, which lead to an inconsistent state for later
+ operations.
+
+ * "git submodule" reports the paths of submodules the command
+ recurses into, but this was incorrect when the command was not run
+ from the root level of the superproject.
+
+ * The "user.useConfigOnly" configuration variable makes it an error
+ if users do not explicitly set user.name and user.email. However,
+ its check was not done early enough and allowed another error to
+ trigger, reporting that the default value we guessed from the
+ system setting was unusable. This was a suboptimal end-user
+ experience as we want the users to set user.name/user.email without
+ relying on the auto-detection at all.
+
+ * "git mv old new" did not adjust the path for a submodule that lives
+ as a subdirectory inside old/ directory correctly.
+
+ * "git replace -e" did not honour "core.editor" configuration.
+
+ * "git push" from a corrupt repository that attempts to push a large
+ number of refs deadlocked; the thread to relay rejection notices
+ for these ref updates blocked on writing them to the main thread,
+ after the main thread at the receiving end notices that the push
+ failed and decides not to read these notices and return a failure.
+
+ * mmap emulation on Windows has been optimized and work better without
+ consuming paging store when not needed.
+
+ * A question by "git send-email" to ask the identity of the sender
+ has been updated.
+
+ * UI consistency improvements for "git mergetool".
+
+ * "git rebase -m" could be asked to rebase an entire branch starting
+ from the root, but failed by assuming that there always is a parent
+ commit to the first commit on the branch.
+ (merge 79f4344 bw/rebase-merge-entire-branch later to maint).
+
+ * Fix a broken "p4 lfs" test.
+
+ * Recent update to Git LFS broke "git p4" by changing the output from
+ its "lfs pointer" subcommand.
+
+ * "git fetch" test t5510 was flaky while running a (forced) automagic
+ garbage collection.
+
+ * Documentation updates to help contributors setting up Travis CI
+ test for their patches.
+
+ * Some multi-byte encoding can have a backslash byte as a later part
+ of one letter, which would confuse "highlight" filter used in
+ gitweb.
+
+ * "git commit-tree" plumbing command required the user to always sign
+ its result when the user sets the commit.gpgsign configuration
+ variable, which was an ancient mistake. Rework "git rebase" that
+ relied on this mistake so that it reads commit.gpgsign and pass (or
+ not pass) the -S option to "git commit-tree" to keep the end-user
+ expectation the same, while teaching "git commit-tree" to ignore
+ the configuration variable. This will stop requiring the users to
+ sign commit objects used internally as an implementation detail of
+ "git stash".
+ (merge 6694856 jc/commit-tree-ignore-commit-gpgsign later to maint).
+
+ * "http.cookieFile" configuration variable clearly wants a pathname,
+ but we forgot to treat it as such by e.g. applying tilde expansion.
+ (merge e5a39ad bn/http-cookiefile-config later to maint).
+
+ * Consolidate description of tilde-expansion that is done to
+ configuration variables that take pathname to a single place.
+ (merge dca83ab jc/config-pathname-type later to maint).
+
+ * Correct faulty recommendation to use "git submodule deinit ." when
+ de-initialising all submodules, which would result in a strange
+ error message in a pathological corner case.
+ (merge f6a5279 sb/submodule-deinit-all later to maint).
+
+ * Many 'linkgit:<git documentation page>' references were broken,
+ which are all fixed with this.
+ (merge 1cca17d jc/linkgit-fix later to maint).
+
+ * "git rerere" can get confused by conflict markers deliberately left
+ by the inner merge step, because they are indistinguishable from
+ the real conflict markers left by the outermost merge which are
+ what the end user and "rerere" need to look at. This was fixed by
+ making the conflict markers left by the inner merges a bit longer.
+ (merge 0f9fd5c jc/ll-merge-internal later to maint).
+
+ * CI test was taught to build documentation pages.
+ (merge b98712b ls/travis-build-doc later to maint).
+
+ * "git fsck" learned to catch NUL byte in a commit object as
+ potential error and warn.
+ (merge 6d2d780 jc/fsck-nul-in-commit later to maint).
+
+ * Portability enhancement for "rebase -i" to help platforms whose
+ shell does not like "for i in <empty>" (which is not POSIX-kosher).
+ (merge 8e98b35 jk/rebase-interative-eval-fix later to maint).
+
+ * On Windows, .git and optionally any files whose name starts with a
+ dot are now marked as hidden, with a core.hideDotFiles knob to
+ customize this behaviour.
+ (merge ebf31e7 js/windows-dotgit later to maint).
+
+ * Documentation for "git merge --verify-signatures" has been updated
+ to clarify that the signature of only the commit at the tip is
+ verified. Also the phrasing used for signature and key validity is
+ adjusted to align with that used by OpenPGP.
+ (merge 05a5869 kf/gpg-sig-verification-doc later to maint).
+
+ * A couple of bugs around core.autocrlf have been fixed.
+ (merge caa47ad tb/core-eol-fix later to maint).
+
+ * Many commands normalize command line arguments from NFD to NFC
+ variant of UTF-8 on OSX, but commands in the "diff" family did
+ not, causing "git diff $path" to complain that no such path is
+ known to Git. They have been taught to do the normalization.
+ (merge 90a78b8 ar/diff-args-osx-precompose later to maint).
+
+ * "git difftool" learned to handle unmerged paths correctly in
+ dir-diff mode.
+ (merge 366f9ce da/difftool later to maint).
+
+ * The "are we talking with TTY, doing an interactive session?"
+ detection has been updated to work better for "Git for Windows".
+ (merge f7f90e0 kb/msys2-tty later to maint).
+
+ * We forgot to add "git log --decorate=auto" to documentation when we
+ added the feature back in v2.1.0 timeframe.
+ (merge 462cbb4 rj/log-decorate-auto later to maint).
+
+ * Other minor clean-ups and documentation updates
+ (merge 832c0e5 lp/typofixes later to maint).
+ (merge f5ee54a sb/z-is-gnutar-ism later to maint).
+ (merge 2e3926b va/i18n-misc-updates later to maint).
+ (merge f212dcc bn/config-doc-tt-varnames later to maint).
+ (merge f54bea4 nd/remote-plural-ours-plus-theirs later to maint).
+ (merge 2bb0518 ak/t4151-ls-files-could-be-empty later to maint).
+ (merge 4df4313 jc/test-seq later to maint).
+ (merge a75a308 tb/t5601-sed-fix later to maint).
+ (merge 6c1fbe1 va/i18n-remote-comment-to-align later to maint).
+ (merge dee2303 va/mailinfo-doc-typofix later to maint).
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 98fc4cc1d0..e8ad978824 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -61,23 +61,28 @@ Make sure that you have tests for the bug you are fixing. See
t/README for guidance.
When adding a new feature, make sure that you have new tests to show
-the feature triggers the new behaviour when it should, and to show the
-feature does not trigger when it shouldn't. Also make sure that the
-test suite passes after your commit. Do not forget to update the
-documentation to describe the updated behaviour.
-
-Speaking of the documentation, it is currently a liberal mixture of US
-and UK English norms for spelling and grammar, which is somewhat
-unfortunate. A huge patch that touches the files all over the place
-only to correct the inconsistency is not welcome, though. Potential
-clashes with other changes that can result from such a patch are not
-worth it. We prefer to gradually reconcile the inconsistencies in
-favor of US English, with small and easily digestible patches, as a
-side effect of doing some other real work in the vicinity (e.g.
-rewriting a paragraph for clarity, while turning en_UK spelling to
-en_US). Obvious typographical fixes are much more welcomed ("teh ->
-"the"), preferably submitted as independent patches separate from
-other documentation changes.
+the feature triggers the new behavior when it should, and to show the
+feature does not trigger when it shouldn't. After any code change, make
+sure that the entire test suite passes.
+
+If you have an account at GitHub (and you can get one for free to work
+on open source projects), you can use their Travis CI integration to
+test your changes on Linux, Mac (and hopefully soon Windows). See
+GitHub-Travis CI hints section for details.
+
+Do not forget to update the documentation to describe the updated
+behavior and make sure that the resulting documentation set formats
+well. It is currently a liberal mixture of US and UK English norms for
+spelling and grammar, which is somewhat unfortunate. A huge patch that
+touches the files all over the place only to correct the inconsistency
+is not welcome, though. Potential clashes with other changes that can
+result from such a patch are not worth it. We prefer to gradually
+reconcile the inconsistencies in favor of US English, with small and
+easily digestible patches, as a side effect of doing some other real
+work in the vicinity (e.g. rewriting a paragraph for clarity, while
+turning en_UK spelling to en_US). Obvious typographical fixes are much
+more welcomed ("teh -> "the"), preferably submitted as independent
+patches separate from other documentation changes.
Oh, another thing. We are picky about whitespaces. Make sure your
changes do not trigger errors with the sample pre-commit hook shipped
@@ -370,6 +375,47 @@ Know the status of your patch after submission
entitled "What's cooking in git.git" and "What's in git.git" giving
the status of various proposed changes.
+--------------------------------------------------
+GitHub-Travis CI hints
+
+With an account at GitHub (you can get one for free to work on open
+source projects), you can use Travis CI to test your changes on Linux,
+Mac (and hopefully soon Windows). You can find a successful example
+test build here: https://travis-ci.org/git/git/builds/120473209
+
+Follow these steps for the initial setup:
+
+ (1) Fork https://github.com/git/git to your GitHub account.
+ You can find detailed instructions how to fork here:
+ https://help.github.com/articles/fork-a-repo/
+
+ (2) Open the Travis CI website: https://travis-ci.org
+
+ (3) Press the "Sign in with GitHub" button.
+
+ (4) Grant Travis CI permissions to access your GitHub account.
+ You can find more information about the required permissions here:
+ https://docs.travis-ci.com/user/github-oauth-scopes
+
+ (5) Open your Travis CI profile page: https://travis-ci.org/profile
+
+ (6) Enable Travis CI builds for your Git fork.
+
+After the initial setup, Travis CI will run whenever you push new changes
+to your fork of Git on GitHub. You can monitor the test state of all your
+branches here: https://travis-ci.org/<Your GitHub handle>/git/branches
+
+If a branch did not pass all test cases then it is marked with a red
+cross. In that case you can click on the failing Travis CI job and
+scroll all the way down in the log. Find the line "<-- Click here to see
+detailed test output!" and click on the triangle next to the log line
+number to expand the detailed test output. Here is such a failing
+example: https://travis-ci.org/git/git/jobs/122676187
+
+Fix the problem and push your fix to your Git fork. This will trigger
+a new Travis CI build to ensure all tests pass.
+
+
------------------------------------------------
MUA specific hints
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 2cd6bdd7d2..2e1b2e486e 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -81,13 +81,16 @@ Includes
You can include one config file from another by setting the special
`include.path` variable to the name of the file to be included. The
+variable takes a pathname as its value, and is subject to tilde
+expansion.
+
+The
included file is expanded immediately, as if its contents had been
found at the location of the include directive. If the value of the
`include.path` variable is a relative path, the path is considered to be
relative to the configuration file in which the include directive was
-found. The value of `include.path` is subject to tilde expansion: `~/`
-is expanded to the value of `$HOME`, and `~user/` to the specified
-user's home directory. See below for examples.
+found. See below for examples.
+
Example
~~~~~~~
@@ -114,7 +117,7 @@ Example
[include]
path = /path/to/foo.inc ; include by absolute path
path = foo ; expand "foo" relative to the current file
- path = ~/foo ; expand "foo" in your $HOME directory
+ path = ~/foo ; expand "foo" in your `$HOME` directory
Values
@@ -169,6 +172,13 @@ thing on the same output line (e.g. opening parenthesis before the
list of branch names in `log --decorate` output) is set to be
painted with `bold` or some other attribute.
+pathname::
+ A variable that takes a pathname value can be given a
+ string that begins with "`~/`" or "`~user/`", and the usual
+ tilde expansion happens to such a string: `~/`
+ is expanded to the value of `$HOME`, and `~user/` to the
+ specified user's home directory.
+
Variables
~~~~~~~~~
@@ -269,6 +279,12 @@ See linkgit:git-update-index[1].
+
The default is true (when core.filemode is not specified in the config file).
+core.hideDotFiles::
+ (Windows-only) If true, mark newly-created directories and files whose
+ name starts with a dot as hidden. If 'dotGitOnly', only the `.git/`
+ directory is hidden, but no other files starting with a dot. The
+ default mode is 'dotGitOnly'.
+
core.ignoreCase::
If true, this option enables various workarounds to enable
Git to work better on filesystems that are not case sensitive,
@@ -337,9 +353,9 @@ core.quotePath::
core.eol::
Sets the line ending type to use in the working directory for
- files that have the `text` property set. Alternatives are
- 'lf', 'crlf' and 'native', which uses the platform's native
- line ending. The default value is `native`. See
+ files that have the `text` property set when core.autocrlf is false.
+ Alternatives are 'lf', 'crlf' and 'native', which uses the platform's
+ native line ending. The default value is `native`. See
linkgit:gitattributes[5] for more information on end-of-line
conversion.
@@ -486,10 +502,10 @@ repository's usual working tree).
core.logAllRefUpdates::
Enable the reflog. Updates to a ref <ref> is logged to the file
- "$GIT_DIR/logs/<ref>", by appending the new and old
+ "`$GIT_DIR/logs/<ref>`", by appending the new and old
SHA-1, the date/time and the reason of the update, but
only when the file exists. If this configuration
- variable is set to true, missing "$GIT_DIR/logs/<ref>"
+ variable is set to true, missing "`$GIT_DIR/logs/<ref>`"
file is automatically created for branch heads (i.e. under
refs/heads/), remote refs (i.e. under refs/remotes/),
note refs (i.e. under refs/notes/), and the symbolic ref HEAD.
@@ -593,12 +609,11 @@ be delta compressed, but larger binary media files won't be.
Common unit suffixes of 'k', 'm', or 'g' are supported.
core.excludesFile::
- In addition to '.gitignore' (per-directory) and
- '.git/info/exclude', Git looks into this file for patterns
- of files which are not meant to be tracked. "`~/`" is expanded
- to the value of `$HOME` and "`~user/`" to the specified user's
- home directory. Its default value is $XDG_CONFIG_HOME/git/ignore.
- If $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/ignore
+ Specifies the pathname to the file that contains patterns to
+ describe paths that are not meant to be tracked, in addition
+ to '.gitignore' (per-directory) and '.git/info/exclude'.
+ Defaults to `$XDG_CONFIG_HOME/git/ignore`.
+ If `$XDG_CONFIG_HOME` is either not set or empty, `$HOME/.config/git/ignore`
is used instead. See linkgit:gitignore[5].
core.askPass::
@@ -615,8 +630,25 @@ core.attributesFile::
'.git/info/attributes', Git looks into this file for attributes
(see linkgit:gitattributes[5]). Path expansions are made the same
way as for `core.excludesFile`. Its default value is
- $XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME is either not
- set or empty, $HOME/.config/git/attributes is used instead.
+ `$XDG_CONFIG_HOME/git/attributes`. If `$XDG_CONFIG_HOME` is either not
+ set or empty, `$HOME/.config/git/attributes` is used instead.
+
+core.hooksPath::
+ By default Git will look for your hooks in the
+ '$GIT_DIR/hooks' directory. Set this to different path,
+ e.g. '/etc/git/hooks', and Git will try to find your hooks in
+ that directory, e.g. '/etc/git/hooks/pre-receive' instead of
+ in '$GIT_DIR/hooks/pre-receive'.
++
+The path can be either absolute or relative. A relative path is
+taken as relative to the directory where the hooks are run (see
+the "DESCRIPTION" section of linkgit:githooks[5]).
++
+This configuration variable is useful in cases where you'd like to
+centrally configure your Git hooks instead of configuring them on a
+per-repository basis, or as a more flexible and centralized
+alternative to having an `init.templateDir` where you've changed
+default hooks.
core.editor::
Commands such as `commit` and `tag` that lets you edit
@@ -1106,15 +1138,19 @@ commit.status::
message. Defaults to true.
commit.template::
- Specify a file to use as the template for new commit messages.
- "`~/`" is expanded to the value of `$HOME` and "`~user/`" to the
- specified user's home directory.
+ Specify the pathname of a file to use as the template for
+ new commit messages.
+
+commit.verbose::
+ A boolean or int to specify the level of verbose with `git commit`.
+ See linkgit:git-commit[1].
credential.helper::
Specify an external helper to be called when a username or
password credential is needed; the helper may consult external
- storage to avoid prompting the user for the credentials. See
- linkgit:gitcredentials[7] for details.
+ storage to avoid prompting the user for the credentials. Note
+ that multiple helpers may be defined. See linkgit:gitcredentials[7]
+ for details.
credential.useHttpPath::
When acquiring credentials, consider the "path" component of an http
@@ -1258,6 +1294,10 @@ format.outputDirectory::
Set a custom directory to store the resulting files instead of the
current working directory.
+format.useAutoBase::
+ A boolean value which lets you enable the `--base=auto` option of
+ format-patch by default.
+
filter.<driver>.clean::
The command which is used to convert the content of a worktree
file to a blob upon checkin. See linkgit:gitattributes[5] for
@@ -1334,7 +1374,7 @@ gc.worktreePruneExpire::
'git worktree prune --expire 3.months.ago'.
This config variable can be used to set a different grace
period. The value "now" may be used to disable the grace
- period and prune $GIT_DIR/worktrees immediately, or "never"
+ period and prune `$GIT_DIR/worktrees` immediately, or "never"
may be used to suppress pruning.
gc.reflogExpire::
@@ -1474,13 +1514,13 @@ grep.fallbackToNoIndex::
is executed outside of a git repository. Defaults to false.
gpg.program::
- Use this custom program instead of "gpg" found on $PATH when
+ Use this custom program instead of "`gpg`" found on `$PATH` when
making or verifying a PGP signature. The program must support the
same command-line interface as GPG, namely, to verify a detached
- signature, "gpg --verify $file - <$signature" is run, and the
+ signature, "`gpg --verify $file - <$signature`" is run, and the
program is expected to signal a good signature by exiting with
code 0, and to generate an ASCII-armored detached signature, the
- standard input of "gpg -bsau $key" is fed with the contents to be
+ standard input of "`gpg -bsau $key`" is fed with the contents to be
signed, and the program is expected to send the result to its
standard output.
@@ -1493,7 +1533,7 @@ gui.diffContext::
made by the linkgit:git-gui[1]. The default is "5".
gui.displayUntracked::
- Determines if linkgit::git-gui[1] shows untracked files
+ Determines if linkgit:git-gui[1] shows untracked files
in the file list. The default is "true".
gui.encoding::
@@ -1654,12 +1694,19 @@ http.emptyAuth::
a username in the URL, as libcurl normally requires a username for
authentication.
+http.extraHeader::
+ Pass an additional HTTP header when communicating with a server. If
+ more than one such entry exists, all of them are added as extra
+ headers. To allow overriding the settings inherited from the system
+ config, an empty value will reset the extra headers to the empty list.
+
http.cookieFile::
- File containing previously stored cookie lines which should be used
+ The pathname of a file containing previously stored cookie lines,
+ which should be used
in the Git http session, if they match the server. The file format
of the file to read cookies from should be plain HTTP headers or
- the Netscape/Mozilla cookie file format (see linkgit:curl[1]).
- NOTE that the file specified with http.cookieFile is only used as
+ the Netscape/Mozilla cookie file format (see `curl(1)`).
+ NOTE that the file specified with http.cookieFile is used only as
input unless http.saveCookies is set.
http.saveCookies::
@@ -1886,6 +1933,14 @@ interactive.singleKey::
setting is silently ignored if portable keystroke input
is not available; requires the Perl module Term::ReadKey.
+interactive.diffFilter::
+ When an interactive command (such as `git add --patch`) shows
+ a colorized diff, git will pipe the diff through the shell
+ command defined by this configuration variable. The command may
+ mark up the diff further for human consumption, provided that it
+ retains a one-to-one correspondence with the lines in the
+ original diff. Defaults to disabled (no filtering).
+
log.abbrevCommit::
If true, makes linkgit:git-log[1], linkgit:git-show[1], and
linkgit:git-whatchanged[1] assume `--abbrev-commit`. You may
@@ -1901,7 +1956,10 @@ log.decorate::
command. If 'short' is specified, the ref name prefixes 'refs/heads/',
'refs/tags/' and 'refs/remotes/' will not be printed. If 'full' is
specified, the full ref name (including prefix) will be printed.
- This is the same as the log commands '--decorate' option.
+ If 'auto' is specified, then if the output is going to a terminal,
+ the ref names are shown as if 'short' were given, otherwise no ref
+ names are shown. This is the same as the '--decorate' option
+ of the `git log`.
log.follow::
If `true`, `git log` will act as if the `--follow` option was used when
@@ -2147,8 +2205,11 @@ pack.packSizeLimit::
The maximum size of a pack. This setting only affects
packing to a file when repacking, i.e. the git:// protocol
is unaffected. It can be overridden by the `--max-pack-size`
- option of linkgit:git-repack[1]. The minimum size allowed is
- limited to 1 MiB. The default is unlimited.
+ option of linkgit:git-repack[1]. Reaching this limit results
+ in the creation of multiple packfiles; which in turn prevents
+ bitmaps from being created.
+ The minimum size allowed is limited to 1 MiB.
+ The default is unlimited.
Common unit suffixes of 'k', 'm', or 'g' are
supported.
@@ -2548,8 +2609,9 @@ repack.writeBitmaps::
objects to disk (e.g., when `git repack -a` is run). This
index can speed up the "counting objects" phase of subsequent
packs created for clones and fetches, at the cost of some disk
- space and extra time spent on the initial repack. Defaults to
- false.
+ space and extra time spent on the initial repack. This has
+ no effect if multiple packfiles are created.
+ Defaults to false.
rerere.autoUpdate::
When set to true, `git-rerere` updates the index with the
@@ -2729,6 +2791,17 @@ submodule.<name>.ignore::
"--ignore-submodules" option. The 'git submodule' commands are not
affected by this setting.
+submodule.fetchJobs::
+ Specifies how many submodules are fetched/cloned at the same time.
+ A positive integer allows up to that number of submodules fetched
+ in parallel. A value of 0 will give some reasonable default.
+ If unset, it defaults to 1.
+
+tag.forceSignAnnotated::
+ A boolean to specify whether annotated tags created should be GPG signed.
+ If `--annotate` is specified on the command line, it takes
+ precedence over this option.
+
tag.sort::
This variable controls the sort ordering of tags when displayed by
linkgit:git-tag[1]. Without the "--sort=<value>" option provided, the
diff --git a/Documentation/diff-config.txt b/Documentation/diff-config.txt
index 6eaa45271c..edba56522b 100644
--- a/Documentation/diff-config.txt
+++ b/Documentation/diff-config.txt
@@ -108,9 +108,13 @@ diff.renameLimit::
detection; equivalent to the 'git diff' option '-l'.
diff.renames::
- Tells Git to detect renames. If set to any boolean value, it
- will enable basic rename detection. If set to "copies" or
- "copy", it will detect copies, as well.
+ Whether and how Git detects renames. If set to "false",
+ rename detection is disabled. If set to "true", basic rename
+ detection is enabled. If set to "copies" or "copy", Git will
+ detect copies, as well. Defaults to true. Note that this
+ affects only 'git diff' Porcelain like linkgit:git-diff[1] and
+ linkgit:git-log[1], and not lower level commands such as
+ linkgit:git-diff-files[1].
diff.suppressBlankEmpty::
A boolean to inhibit the standard behavior of printing a space
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 4b0318e2ac..3cb301556e 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -271,7 +271,7 @@ For example, `--word-diff-regex=.` will treat each character as a word
and, correspondingly, show differences character by character.
+
The regex can also be set via a diff driver or configuration option, see
-linkgit:gitattributes[1] or linkgit:git-config[1]. Giving it explicitly
+linkgit:gitattributes[5] or linkgit:git-config[1]. Giving it explicitly
overrides any diff driver or configuration setting. Diff drivers
override configuration settings.
diff --git a/Documentation/everyday.txto b/Documentation/everyday.txto
index c5047d8f9b..ae555bd47e 100644
--- a/Documentation/everyday.txto
+++ b/Documentation/everyday.txto
@@ -1,7 +1,7 @@
Everyday Git With 20 Commands Or So
===================================
-This document has been moved to linkgit:giteveryday[1].
+This document has been moved to linkgit:giteveryday[7].
Please let the owners of the referring site know so that they can update the
link you clicked to get here.
diff --git a/Documentation/git-check-ignore.txt b/Documentation/git-check-ignore.txt
index e94367a5ed..611754f10b 100644
--- a/Documentation/git-check-ignore.txt
+++ b/Documentation/git-check-ignore.txt
@@ -112,7 +112,7 @@ EXIT STATUS
SEE ALSO
--------
linkgit:gitignore[5]
-linkgit:gitconfig[5]
+linkgit:git-config[1]
linkgit:git-ls-files[1]
GIT
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index b7c467a001..1b15cd7b16 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -14,8 +14,8 @@ SYNOPSIS
[-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
[--dissociate] [--separate-git-dir <git dir>]
[--depth <depth>] [--[no-]single-branch]
- [--recursive | --recurse-submodules] [--] <repository>
- [<directory>]
+ [--recursive | --recurse-submodules] [--[no-]shallow-submodules]
+ [--jobs <n>] [--] <repository> [<directory>]
DESCRIPTION
-----------
@@ -191,7 +191,9 @@ objects from the source repository into a pack in the cloned repository.
Create a 'shallow' clone with a history truncated to the
specified number of commits. Implies `--single-branch` unless
`--no-single-branch` is given to fetch the histories near the
- tips of all branches.
+ tips of all branches. This implies `--shallow-submodules`. If
+ you want to have a shallow superproject clone, but full submodules,
+ also pass `--no-shallow-submodules`.
--[no-]single-branch::
Clone only the history leading to the tip of a single branch,
@@ -212,6 +214,9 @@ objects from the source repository into a pack in the cloned repository.
repository does not have a worktree/checkout (i.e. if any of
`--no-checkout`/`-n`, `--bare`, or `--mirror` is given)
+--[no-]shallow-submodules::
+ All submodules which are cloned will be shallow with a depth of 1.
+
--separate-git-dir=<git dir>::
Instead of placing the cloned repository where it is supposed
to be, place the cloned repository at the specified directory,
@@ -219,6 +224,10 @@ objects from the source repository into a pack in the cloned repository.
The result is Git repository can be separated from working
tree.
+-j <n>::
+--jobs <n>::
+ The number of submodules fetched at the same time.
+ Defaults to the `submodule.fetchJobs` option.
<repository>::
The (possibly remote) repository to clone from. See the
diff --git a/Documentation/git-commit-tree.txt b/Documentation/git-commit-tree.txt
index 48c33d7ed7..cb69faab68 100644
--- a/Documentation/git-commit-tree.txt
+++ b/Documentation/git-commit-tree.txt
@@ -61,8 +61,8 @@ OPTIONS
stuck to the option without a space.
--no-gpg-sign::
- Countermand `commit.gpgSign` configuration variable that is
- set to force each and every commit to be signed.
+ Do not GPG-sign commit, to countermand a `--gpg-sign` option
+ given earlier on the command line.
Commit Information
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 9ec6b3cc17..d474226eb7 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -290,7 +290,8 @@ configuration variable documented in linkgit:git-config[1].
what changes the commit has.
Note that this diff output doesn't have its
lines prefixed with '#'. This diff will not be a part
- of the commit message.
+ of the commit message. See the `commit.verbose` configuration
+ variable in linkgit:git-config[1].
+
If specified twice, show in addition the unified diff between
what would be committed and the worktree files, i.e. the unstaged
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 6fc08e3d89..6843114fc0 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -58,10 +58,10 @@ that location (you can say '--local' but that is the default).
This command will fail with non-zero status upon error. Some exit
codes are:
-- The config file is invalid (ret=3),
-- can not write to the config file (ret=4),
+- The section or key is invalid (ret=1),
- no section or name was provided (ret=2),
-- the section or key is invalid (ret=1),
+- the config file is invalid (ret=3),
+- the config file cannot be written (ret=4),
- you try to unset an option which does not exist (ret=5),
- you try to unset/set an option for which multiple lines match (ret=5), or
- you try to use an invalid regexp (ret=6).
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index 73fd9e8230..003731f6a9 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -205,7 +205,7 @@ to other tags will be rewritten to point to the underlying commit.
Remap to ancestor
~~~~~~~~~~~~~~~~~
-By using linkgit:rev-list[1] arguments, e.g., path limiters, you can limit the
+By using linkgit:git-rev-list[1] arguments, e.g., path limiters, you can limit the
set of revisions which get rewritten. However, positive refs on the command
line are distinguished: we don't let them be excluded by such limiters. For
this purpose, they are instead rewritten to point at the nearest ancestor that
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index c52578bb87..d9d406dcfb 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -179,7 +179,7 @@ returns an empty string instead.
As a special case for the date-type fields, you may specify a format for
the date by adding `:` followed by date format name (see the
-values the `--date` option to linkgit::git-rev-list[1] takes).
+values the `--date` option to linkgit:git-rev-list[1] takes).
EXAMPLES
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 6821441d7d..bdeecd59e0 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -265,6 +265,11 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.
Output an all-zero hash in each patch's From header instead
of the hash of the commit.
+--base=<commit>::
+ Record the base tree information to identify the state the
+ patch series applies to. See the BASE TREE INFORMATION section
+ below for details.
+
--root::
Treat the revision argument as a <revision range>, even if it
is just a single commit (that would normally be treated as a
@@ -520,6 +525,61 @@ This should help you to submit patches inline using KMail.
5. Back in the compose window: add whatever other text you wish to the
message, complete the addressing and subject fields, and press send.
+BASE TREE INFORMATION
+---------------------
+
+The base tree information block is used for maintainers or third party
+testers to know the exact state the patch series applies to. It consists
+of the 'base commit', which is a well-known commit that is part of the
+stable part of the project history everybody else works off of, and zero
+or more 'prerequisite patches', which are well-known patches in flight
+that is not yet part of the 'base commit' that need to be applied on top
+of 'base commit' in topological order before the patches can be applied.
+
+The 'base commit' is shown as "base-commit: " followed by the 40-hex of
+the commit object name. A 'prerequisite patch' is shown as
+"prerequisite-patch-id: " followed by the 40-hex 'patch id', which can
+be obtained by passing the patch through the `git patch-id --stable`
+command.
+
+Imagine that on top of the public commit P, you applied well-known
+patches X, Y and Z from somebody else, and then built your three-patch
+series A, B, C, the history would be like:
+
+................................................
+---P---X---Y---Z---A---B---C
+................................................
+
+With `git format-patch --base=P -3 C` (or variants thereof, e.g. with
+`--cover-letter` of using `Z..C` instead of `-3 C` to specify the
+range), the base tree information block is shown at the end of the
+first message the command outputs (either the first patch, or the
+cover letter), like this:
+
+------------
+base-commit: P
+prerequisite-patch-id: X
+prerequisite-patch-id: Y
+prerequisite-patch-id: Z
+------------
+
+For non-linear topology, such as
+
+................................................
+---P---X---A---M---C
+ \ /
+ Y---Z---B
+................................................
+
+You can also use `git format-patch --base=P -3 C` to generate patches
+for A, B and C, and the identifiers for P, X, Y, Z are appended at the
+end of the first message.
+
+If set `--base=auto` in cmdline, it will track base commit automatically,
+the base commit will be the merge base of tip commit of the remote-tracking
+branch and revision-range specified in cmdline.
+For a local branch, you need to track a remote branch by `git branch
+--set-upstream-to` before using this option.
EXAMPLES
--------
diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt
index 8174d27efd..6364e5dc45 100644
--- a/Documentation/git-init.txt
+++ b/Documentation/git-init.txt
@@ -130,7 +130,12 @@ The template directory will be one of the following (in order):
- the default template directory: `/usr/share/git-core/templates`.
The default template directory includes some directory structure, suggested
-"exclude patterns" (see linkgit:gitignore[5]), and sample hook files (see linkgit:githooks[5]).
+"exclude patterns" (see linkgit:gitignore[5]), and sample hook files.
+
+The sample hooks are all disabled by default, To enable one of the
+sample hooks rename it by removing its `.sample` suffix.
+
+See linkgit:githooks[5] for more general info on hook execution.
EXAMPLES
--------
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 03f958029a..dec379b3e2 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -29,12 +29,14 @@ OPTIONS
(works only for a single file).
--no-decorate::
---decorate[=short|full|no]::
+--decorate[=short|full|auto|no]::
Print out the ref names of any commits that are shown. If 'short' is
specified, the ref name prefixes 'refs/heads/', 'refs/tags/' and
'refs/remotes/' will not be printed. If 'full' is specified, the
- full ref name (including prefix) will be printed. The default option
- is 'short'.
+ full ref name (including prefix) will be printed. If 'auto' is
+ specified, then if the output is going to a terminal, the ref names
+ are shown as if 'short' were given, otherwise no ref names are
+ shown. The default option is 'short'.
--source::
Print out the ref name given on the command line by which each
diff --git a/Documentation/git-mailinfo.txt b/Documentation/git-mailinfo.txt
index 0947084140..3bbc731f67 100644
--- a/Documentation/git-mailinfo.txt
+++ b/Documentation/git-mailinfo.txt
@@ -85,7 +85,7 @@ with comments and suggestions on the message you are responding to, and to
conclude it with a patch submission, separating the discussion and the
beginning of the proposed commit log message with a scissors line.
+
-This can enabled by default with the configuration option mailinfo.scissors.
+This can be enabled by default with the configuration option mailinfo.scissors.
--no-scissors::
Ignore scissors lines. Useful for overriding mailinfo.scissors settings.
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 07f7295ec8..b758d5556c 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -11,6 +11,7 @@ SYNOPSIS
[verse]
'git merge' [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
[-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
+ [--[no-]allow-unrelated-histories]
[--[no-]rerere-autoupdate] [-m <msg>] [<commit>...]
'git merge' <msg> HEAD <commit>...
'git merge' --abort
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
index 8de349968a..9c4fd6812c 100644
--- a/Documentation/git-notes.txt
+++ b/Documentation/git-notes.txt
@@ -402,4 +402,4 @@ on the `notes.rewrite.<command>` and `notes.rewriteRef` settings.
GIT
---
-Part of the linkgit:git[7] suite
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt
index 35e3170918..88ba42b455 100644
--- a/Documentation/git-p4.txt
+++ b/Documentation/git-p4.txt
@@ -551,6 +551,17 @@ git-p4.keepEmptyCommits::
A changelist that contains only excluded files will be imported
as an empty commit if this boolean option is set to true.
+git-p4.mapUser::
+ Map a P4 user to a name and email address in Git. Use a string
+ with the following format to create a mapping:
++
+-------------
+git config --add git-p4.mapUser "p4user = First Last <mail@address.com>"
+-------------
++
+A mapping will override any user information from P4. Mappings for
+multiple P4 user can be defined.
+
Submit variables
~~~~~~~~~~~~~~~~
git-p4.detectRenames::
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index bbea5294ca..19cdcd0341 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -110,7 +110,8 @@ base-name::
--max-pack-size=<n>::
Maximum size of each output pack file. The size can be suffixed with
"k", "m", or "g". The minimum size allowed is limited to 1 MiB.
- If specified, multiple packfiles may be created.
+ If specified, multiple packfiles may be created, which also
+ prevents the creation of a bitmap index.
The default is unlimited, unless the config variable
`pack.packSizeLimit` is set.
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index a62a2a615d..d033b258e5 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -128,6 +128,15 @@ unless you have read linkgit:git-rebase[1] carefully.
--no-rebase::
Override earlier --rebase.
+--autostash::
+--no-autostash::
+ Before starting rebase, stash local modifications away (see
+ linkgit:git-stash[1]) if needed, and apply the stash when
+ done. `--no-autostash` is useful to override the `rebase.autoStash`
+ configuration variable (see linkgit:git-config[1]).
++
+This option is only valid when "--rebase" is used.
+
Options related to fetching
~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 6ed610a031..0387b40e0a 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -391,9 +391,6 @@ idea unless you know what you are doing (see BUGS below).
final history. <cmd> will be interpreted as one or more shell
commands.
+
-This option can only be used with the `--interactive` option
-(see INTERACTIVE MODE below).
-+
You may execute several commands by either using one instance of `--exec`
with several commands:
+
@@ -406,6 +403,9 @@ or by giving more than one `--exec`:
If `--autosquash` is used, "exec" lines will not be appended for
the intermediate commits, and will only appear at the end of each
squash/fixup series.
++
+This uses the `--interactive` machinery internally, but it can be run
+without an explicit `--interactive`.
--root::
Rebase all commits reachable from <branch>, instead of
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index af230d0647..b9c02ce481 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -106,7 +106,8 @@ other objects in that pack they already have locally.
--max-pack-size=<n>::
Maximum size of each output pack file. The size can be suffixed with
"k", "m", or "g". The minimum size allowed is limited to 1 MiB.
- If specified, multiple packfiles may be created.
+ If specified, multiple packfiles may be created, which also
+ prevents the creation of a bitmap index.
The default is unlimited, unless the config variable
`pack.packSizeLimit` is set.
@@ -115,7 +116,8 @@ other objects in that pack they already have locally.
Write a reachability bitmap index as part of the repack. This
only makes sense when used with `-a` or `-A`, as the bitmaps
must be able to refer to all reachable objects. This option
- overrides the setting of `pack.writeBitmaps`.
+ overrides the setting of `repack.writeBitmaps`. This option
+ has no effect if multiple packfiles are created.
--pack-kept-objects::
Include objects in `.keep` files when repacking. Note that we
@@ -123,7 +125,7 @@ other objects in that pack they already have locally.
This means that we may duplicate objects, but this makes the
option safe to use when there are concurrent pushes or fetches.
This option is generally only useful if you are writing bitmaps
- with `-b` or `pack.writeBitmaps`, as it ensures that the
+ with `-b` or `repack.writeBitmaps`, as it ensures that the
bitmapped packfile has the necessary objects.
Configuration
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 771a7b5b09..edbba3a2ef 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -450,6 +450,19 @@ edit ~/.gitconfig to specify your account settings:
smtpUser = yourname@gmail.com
smtpServerPort = 587
+If you have multifactor authentication setup on your gmail acocunt, you will
+need to generate an app-specific password for use with 'git send-email'. Visit
+https://security.google.com/settings/security/apppasswords to setup an
+app-specific password. Once setup, you can store it with the credentials
+helper:
+
+ $ git credential fill
+ protocol=smtp
+ host=smtp.gmail.com
+ username=youname@gmail.com
+ password=app-password
+
+
Once your commits are ready to be sent to the mailing list, run the
following commands:
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index 1572f058f5..9226c4380c 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -13,10 +13,10 @@ SYNOPSIS
[--reference <repository>] [--depth <depth>] [--] <repository> [<path>]
'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
'git submodule' [--quiet] init [--] [<path>...]
-'git submodule' [--quiet] deinit [-f|--force] [--] <path>...
+'git submodule' [--quiet] deinit [-f|--force] (--all|[--] <path>...)
'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch]
[-f|--force] [--rebase|--merge] [--reference <repository>]
- [--depth <depth>] [--recursive] [--] [<path>...]
+ [--depth <depth>] [--recursive] [--jobs <n>] [--] [<path>...]
'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]
[commit] [--] [<path>...]
'git submodule' [--quiet] foreach [--recursive] <command>
@@ -140,12 +140,15 @@ deinit::
tree. Further calls to `git submodule update`, `git submodule foreach`
and `git submodule sync` will skip any unregistered submodules until
they are initialized again, so use this command if you don't want to
- have a local checkout of the submodule in your work tree anymore. If
+ have a local checkout of the submodule in your working tree anymore. If
you really want to remove a submodule from the repository and commit
that use linkgit:git-rm[1] instead.
+
-If `--force` is specified, the submodule's work tree will be removed even if
-it contains local modifications.
+When the command is run without pathspec, it errors out,
+instead of deinit-ing everything, to prevent mistakes.
++
+If `--force` is specified, the submodule's working tree will
+be removed even if it contains local modifications.
update::
+
@@ -247,6 +250,10 @@ OPTIONS
--quiet::
Only print error messages.
+--all::
+ This option is only valid for the deinit command. Unregister all
+ submodules in the working tree.
+
-b::
--branch::
Branch of repository to add as submodule.
@@ -257,8 +264,8 @@ OPTIONS
--force::
This option is only valid for add, deinit and update commands.
When running add, allow adding an otherwise ignored submodule path.
- When running deinit the submodule work trees will be removed even if
- they contain local changes.
+ When running deinit the submodule working trees will be removed even
+ if they contain local changes.
When running update (only effective with the checkout procedure),
throw away local changes in submodules when switching to a
different commit; and always run a checkout operation in the
@@ -377,6 +384,11 @@ for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully.
clone with a history truncated to the specified number of revisions.
See linkgit:git-clone[1]
+-j <n>::
+--jobs <n>::
+ This option is only valid for the update command.
+ Clone new submodules in parallel with as many jobs.
+ Defaults to the `submodule.fetchJobs` option.
<path>...::
Paths to submodule(s). When specified this will restrict the command
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 62c76c1c89..c62234538b 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -9,7 +9,7 @@ git-worktree - Manage multiple working trees
SYNOPSIS
--------
[verse]
-'git worktree add' [-f] [--detach] [-b <new-branch>] <path> [<branch>]
+'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
'git worktree prune' [-n] [-v] [--expire <expire>]
'git worktree list' [--porcelain]
@@ -87,6 +87,12 @@ OPTIONS
With `add`, detach HEAD in the new working tree. See "DETACHED HEAD"
in linkgit:git-checkout[1].
+--[no-]checkout::
+ By default, `add` checks out `<branch>`, however, `--no-checkout` can
+ be used to suppress checkout in order to make customizations,
+ such as configuring sparse-checkout. See "Sparse checkout"
+ in linkgit:git-read-tree[1].
+
-n::
--dry-run::
With `prune`, do not remove anything; just report what it would
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 34ff007a98..dd6dbf7dd9 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -43,11 +43,12 @@ unreleased) version of Git, that is available from the 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v2.8.2/git.html[documentation for release 2.8.2]
+* link:v2.8.3/git.html[documentation for release 2.8.3]
* release notes for
- link:RelNotes/2.8.2.txt[2.8.2].
- link:RelNotes/2.8.1.txt[2.8.1].
+ link:RelNotes/2.8.3.txt[2.8.3],
+ link:RelNotes/2.8.2.txt[2.8.2],
+ link:RelNotes/2.8.1.txt[2.8.1],
link:RelNotes/2.8.0.txt[2.8].
* link:v2.7.3/git.html[documentation for release 2.7.3]
diff --git a/Documentation/gitcredentials.txt b/Documentation/gitcredentials.txt
index 1c75be0803..f3a75d1ce1 100644
--- a/Documentation/gitcredentials.txt
+++ b/Documentation/gitcredentials.txt
@@ -106,6 +106,11 @@ variable, each helper will be tried in turn, and may provide a username,
password, or nothing. Once Git has acquired both a username and a
password, no more helpers will be tried.
+If `credential.helper` is configured to the empty string, this resets
+the helper list to empty (so you may override a helper set by a
+lower-priority config file by configuring the empty-string helper,
+followed by whatever set of helpers you would like).
+
CREDENTIAL CONTEXTS
-------------------
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index a2f59b194c..d82e912e55 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -7,24 +7,35 @@ githooks - Hooks used by Git
SYNOPSIS
--------
-$GIT_DIR/hooks/*
+$GIT_DIR/hooks/* (or \`git config core.hooksPath`/*)
DESCRIPTION
-----------
-Hooks are little scripts you can place in `$GIT_DIR/hooks`
-directory to trigger action at certain points. When
-'git init' is run, a handful of example hooks are copied into the
-`hooks` directory of the new repository, but by default they are
-all disabled. To enable a hook, rename it by removing its `.sample`
-suffix.
+Hooks are programs you can place in a hooks directory to trigger
+actions at certain points in git's execution. Hooks that don't have
+the executable bit set are ignored.
-NOTE: It is also a requirement for a given hook to be executable.
-However - in a freshly initialized repository - the `.sample` files are
-executable by default.
+By default the hooks directory is `$GIT_DIR/hooks`, but that can be
+changed via the `core.hooksPath` configuration variable (see
+linkgit:git-config[1]).
-This document describes the currently defined hooks.
+Before Git invokes a hook, it changes its working directory to either
+the root of the working tree in a non-bare repository, or to the
+$GIT_DIR in a bare repository.
+
+Hooks can get their arguments via the environment, command-line
+arguments, and stdin. See the documentation for each hook below for
+details.
+
+'git init' may copy hooks to the new repository, depending on its
+configuration. See the "TEMPLATE DIRECTORY" section in
+linkgit:git-init[1] for details. When the rest of this document refers
+to "default hooks" it's talking about the default template shipped
+with Git.
+
+The currently supported hooks are described below.
HOOKS
-----
@@ -32,15 +43,15 @@ HOOKS
applypatch-msg
~~~~~~~~~~~~~~
-This hook is invoked by 'git am' script. It takes a single
+This hook is invoked by 'git am'. It takes a single
parameter, the name of the file that holds the proposed commit
-log message. Exiting with non-zero status causes
-'git am' to abort before applying the patch.
+log message. Exiting with a non-zero status causes 'git am' to abort
+before applying the patch.
The hook is allowed to edit the message file in place, and can
be used to normalize the message into some project standard
-format (if the project has one). It can also be used to refuse
-the commit after inspecting the message file.
+format. It can also be used to refuse the commit after inspecting
+the message file.
The default 'applypatch-msg' hook, when enabled, runs the
'commit-msg' hook, if the latter is enabled.
@@ -73,10 +84,10 @@ pre-commit
~~~~~~~~~~
This hook is invoked by 'git commit', and can be bypassed
-with `--no-verify` option. It takes no parameter, and is
+with the `--no-verify` option. It takes no parameters, and is
invoked before obtaining the proposed commit log message and
-making a commit. Exiting with non-zero status from this script
-causes the 'git commit' to abort.
+making a commit. Exiting with a non-zero status from this script
+causes the 'git commit' command to abort before creating a commit.
The default 'pre-commit' hook, when enabled, catches introduction
of lines with trailing whitespaces and aborts the commit when
@@ -115,15 +126,15 @@ commit-msg
~~~~~~~~~~
This hook is invoked by 'git commit', and can be bypassed
-with `--no-verify` option. It takes a single parameter, the
+with the `--no-verify` option. It takes a single parameter, the
name of the file that holds the proposed commit log message.
-Exiting with non-zero status causes the 'git commit' to
+Exiting with a non-zero status causes the 'git commit' to
abort.
-The hook is allowed to edit the message file in place, and can
-be used to normalize the message into some project standard
-format (if the project has one). It can also be used to refuse
-the commit after inspecting the message file.
+The hook is allowed to edit the message file in place, and can be used
+to normalize the message into some project standard format. It
+can also be used to refuse the commit after inspecting the message
+file.
The default 'commit-msg' hook, when enabled, detects duplicate
"Signed-off-by" lines, and aborts the commit if one is found.
@@ -131,8 +142,8 @@ The default 'commit-msg' hook, when enabled, detects duplicate
post-commit
~~~~~~~~~~~
-This hook is invoked by 'git commit'. It takes no
-parameter, and is invoked after a commit is made.
+This hook is invoked by 'git commit'. It takes no parameters, and is
+invoked after a commit is made.
This hook is meant primarily for notification, and cannot affect
the outcome of 'git commit'.
@@ -267,9 +278,11 @@ does not know the entire set of branches, so it would end up
firing one e-mail per ref when used naively, though. The
<<post-receive,'post-receive'>> hook is more suited to that.
-Another use suggested on the mailing list is to use this hook to
-implement access control which is finer grained than the one
-based on filesystem group.
+In an environment that restricts the users' access only to git
+commands over the wire, this hook can be used to implement access
+control without relying on filesystem ownership and group
+membership. See linkgit:git-shell[1] for how you might use the login
+shell to restrict the user's access to only git commands.
Both standard output and standard error output are forwarded to
'git send-pack' on the other end, so you can simply `echo` messages
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index cafc284359..8ad29e61a9 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -145,7 +145,7 @@ current branch integrates with) obviously do not work, as there is no
A fast-forward is a special type of <<def_merge,merge>> where you have a
<<def_revision,revision>> and you are "merging" another
<<def_branch,branch>>'s changes that happen to be a descendant of what
- you have. In such these cases, you do not make a new <<def_merge,merge>>
+ you have. In such a case, you do not make a new <<def_merge,merge>>
<<def_commit,commit>> but instead just update to his
revision. This will happen frequently on a
<<def_remote_tracking_branch,remote-tracking branch>> of a remote
diff --git a/Documentation/lint-gitlink.perl b/Documentation/lint-gitlink.perl
new file mode 100755
index 0000000000..476cc30b83
--- /dev/null
+++ b/Documentation/lint-gitlink.perl
@@ -0,0 +1,71 @@
+#!/usr/bin/perl
+
+use File::Find;
+use Getopt::Long;
+
+my $basedir = ".";
+GetOptions("basedir=s" => \$basedir)
+ or die("Cannot parse command line arguments\n");
+
+my $found_errors = 0;
+
+sub report {
+ my ($where, $what, $error) = @_;
+ print "$where: $error: $what\n";
+ $found_errors = 1;
+}
+
+sub grab_section {
+ my ($page) = @_;
+ open my $fh, "<", "$basedir/$page.txt";
+ my $firstline = <$fh>;
+ chomp $firstline;
+ close $fh;
+ my ($section) = ($firstline =~ /.*\((\d)\)$/);
+ return $section;
+}
+
+sub lint {
+ my ($file) = @_;
+ open my $fh, "<", $file
+ or return;
+ while (<$fh>) {
+ my $where = "$file:$.";
+ while (s/linkgit:((.*?)\[(\d)\])//) {
+ my ($target, $page, $section) = ($1, $2, $3);
+
+ # De-AsciiDoc
+ $page =~ s/{litdd}/--/g;
+
+ if ($page !~ /^git/) {
+ report($where, $target, "nongit link");
+ next;
+ }
+ if (! -f "$basedir/$page.txt") {
+ report($where, $target, "no such source");
+ next;
+ }
+ $real_section = grab_section($page);
+ if ($real_section != $section) {
+ report($where, $target,
+ "wrong section (should be $real_section)");
+ next;
+ }
+ }
+ }
+ close $fh;
+}
+
+sub lint_it {
+ lint($File::Find::name) if -f && /\.txt$/;
+}
+
+if (!@ARGV) {
+ find({ wanted => \&lint_it, no_chdir => 1 }, $basedir);
+} else {
+ for (@ARGV) {
+ lint($_);
+ }
+}
+
+exit $found_errors;
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index f08e9b80c5..5b4a62e936 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -89,8 +89,11 @@ option can be used to override --squash.
--verify-signatures::
--no-verify-signatures::
- Verify that the commits being merged have good and trusted GPG signatures
- and abort the merge in case they do not.
+ Verify that the tip commit of the side branch being merged is
+ signed with a valid key, i.e. a key that has a valid uid: in the
+ default trust model, this means the signing key has been signed by
+ a trusted key. If the tip commit of the side branch is not signed
+ with a valid key, the merge is aborted.
--summary::
--no-summary::
@@ -114,3 +117,11 @@ ifndef::git-pull[]
reporting.
endif::git-pull[]
+
+--allow-unrelated-histories::
+ By default, `git merge` command refuses to merge histories
+ that do not share a common ancestor. This option can be
+ used to override this safety when merging histories of two
+ projects that started their lives independently. As that is
+ a very rare occasion, no configuration variable to enable
+ this by default exists and will not be added.
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 671cebd95c..29b19b992f 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -143,8 +143,8 @@ ifndef::git-rev-list[]
- '%N': commit notes
endif::git-rev-list[]
- '%GG': raw verification message from GPG for a signed commit
-- '%G?': show "G" for a Good signature, "B" for a Bad signature, "U" for a good,
- untrusted signature and "N" for no signature
+- '%G?': show "G" for a good (valid) signature, "B" for a bad signature,
+ "U" for a good signature with unknown validity and "N" for no signature
- '%GS': show the name of the signer for a signed commit
- '%GK': show the key used to sign a signed commit
- '%gD': reflog selector, e.g., `refs/stash@{1}`
diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt
index 54b88b6dca..6c67182728 100644
--- a/Documentation/pretty-options.txt
+++ b/Documentation/pretty-options.txt
@@ -42,6 +42,20 @@ people using 80-column terminals.
verbatim; this means that invalid sequences in the original
commit may be copied to the output.
+--expand-tabs=<n>::
+--expand-tabs::
+--no-expand-tabs::
+ Perform a tab expansion (replace each tab with enough spaces
+ to fill to the next display column that is multiple of '<n>')
+ in the log message before showing it in the output.
+ `--expand-tabs` is a short-hand for `--expand-tabs=8`, and
+ `--no-expand-tabs` is a short-hand for `--expand-tabs=0`,
+ which disables tab expansion.
++
+By default, tabs are expanded in pretty formats that indent the log
+message by 4 spaces (i.e. 'medium', which is the default, 'full',
+and 'fuller').
+
ifndef::git-rev-list[]
--notes[=<treeish>]::
Show the notes (see linkgit:git-notes[1]) that annotate the
diff --git a/Documentation/technical/api-credentials.txt b/Documentation/technical/api-credentials.txt
index e44426dd04..75368f26ca 100644
--- a/Documentation/technical/api-credentials.txt
+++ b/Documentation/technical/api-credentials.txt
@@ -243,7 +243,7 @@ appended to its command line, which is one of:
The details of the credential will be provided on the helper's stdin
stream. The exact format is the same as the input/output format of the
`git credential` plumbing command (see the section `INPUT/OUTPUT
-FORMAT` in linkgit:git-credential[7] for a detailed specification).
+FORMAT` in linkgit:git-credential[1] for a detailed specification).
For a `get` operation, the helper should produce a list of attributes
on stdout in the same format. A helper is free to produce a subset, or
@@ -268,4 +268,4 @@ See also
linkgit:gitcredentials[7]
-linkgit:git-config[5] (See configuration variables `credential.*`)
+linkgit:git-config[1] (See configuration variables `credential.*`)
diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 695bd4bf43..27bd701c0d 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -144,8 +144,12 @@ There are some macros to easily define options:
`OPT_COUNTUP(short, long, &int_var, description)`::
Introduce a count-up option.
- `int_var` is incremented on each use of `--option`, and
- reset to zero with `--no-option`.
+ Each use of `--option` increments `int_var`, starting from zero
+ (even if initially negative), and `--no-option` resets it to
+ zero. To determine if `--option` or `--no-option` was encountered at
+ all, initialize `int_var` to a negative value, and if it is still
+ negative after parse_options(), then neither `--option` nor
+ `--no-option` was seen.
`OPT_BIT(short, long, &int_var, description, mask)`::
Introduce a boolean option.
diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt
index c6977bbc5a..8b36343802 100644
--- a/Documentation/technical/pack-protocol.txt
+++ b/Documentation/technical/pack-protocol.txt
@@ -526,7 +526,7 @@ Push Certificate
A push certificate begins with a set of header lines. After the
header and an empty line, the protocol commands follow, one per
-line. Note that the the trailing LF in push-cert PKT-LINEs is _not_
+line. Note that the trailing LF in push-cert PKT-LINEs is _not_
optional; it must be present.
Currently, the following header fields are defined:
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 5f99f23cad..e3d0bed2a9 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.8.2
+DEF_VER=v2.9.0-rc0
LF='
'
diff --git a/Makefile b/Makefile
index a83e322f46..0d59718bf7 100644
--- a/Makefile
+++ b/Makefile
@@ -440,7 +440,6 @@ DIFF = diff
TAR = tar
FIND = find
INSTALL = install
-RPMBUILD = rpmbuild
TCL_PATH = tclsh
TCLTK_PATH = wish
XGETTEXT = xgettext
@@ -621,7 +620,7 @@ TEST_PROGRAMS_NEED_X += test-svn-fe
TEST_PROGRAMS_NEED_X += test-urlmatch-normalization
TEST_PROGRAMS_NEED_X += test-wildmatch
-TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
+TEST_PROGRAMS = $(patsubst %,t/helper/%$X,$(TEST_PROGRAMS_NEED_X))
# List built-in command $C whose implementation cmd_$C() is not in
# builtin/$C.o but is linked in as part of some other command.
@@ -1898,7 +1897,7 @@ VCSSVN_OBJS += vcs-svn/fast_export.o
VCSSVN_OBJS += vcs-svn/svndiff.o
VCSSVN_OBJS += vcs-svn/svndump.o
-TEST_OBJS := $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
+TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS))
OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
$(XDIFF_OBJS) \
$(VCSSVN_OBJS) \
@@ -2063,7 +2062,7 @@ XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \
--keyword=gettextln --keyword=eval_gettextln
XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl
LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
-LOCALIZED_SH = $(SCRIPT_SH)
+LOCALIZED_SH = $(SCRIPT_SH) git-parse-remote.sh
LOCALIZED_PERL = $(SCRIPT_PERL)
ifdef XGETTEXT_INCLUDE_TESTS
@@ -2205,7 +2204,7 @@ bin-wrappers/%: wrap-for-bin.sh
@mkdir -p bin-wrappers
$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-e 's|@@BUILD_DIR@@|$(shell pwd)|' \
- -e 's|@@PROG@@|$(@F)|' < $< > $@ && \
+ -e 's|@@PROG@@|$(patsubst test-%,t/helper/test-%,$(@F))|' < $< > $@ && \
chmod +x $@
# GNU make supports exporting all variables by "export" without parameters.
@@ -2225,25 +2224,25 @@ perf: all
.PHONY: test perf
-test-ctype$X: ctype.o
+t/helper/test-ctype$X: ctype.o
-test-date$X: date.o ctype.o
+t/helper/test-date$X: date.o ctype.o
-test-delta$X: diff-delta.o patch-delta.o
+t/helper/test-delta$X: diff-delta.o patch-delta.o
-test-line-buffer$X: vcs-svn/lib.a
+t/helper/test-line-buffer$X: vcs-svn/lib.a
-test-parse-options$X: parse-options.o parse-options-cb.o
+t/helper/test-parse-options$X: parse-options.o parse-options-cb.o
-test-svn-fe$X: vcs-svn/lib.a
+t/helper/test-svn-fe$X: vcs-svn/lib.a
.PRECIOUS: $(TEST_OBJS)
-test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS)
+t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
-check-sha1:: test-sha1$X
- ./test-sha1.sh
+check-sha1:: t/helper/test-sha1$X
+ t/helper/test-sha1.sh
SP_OBJ = $(patsubst %.o,%.sp,$(C_OBJ))
@@ -2390,31 +2389,25 @@ quick-install-html:
### Maintainer's dist rules
-git.spec: git.spec.in GIT-VERSION-FILE
- sed -e 's/@@VERSION@@/$(GIT_VERSION)/g' < $< > $@+
- mv $@+ $@
-
GIT_TARNAME = git-$(GIT_VERSION)
-dist: git.spec git-archive$(X) configure
+dist: git-archive$(X) configure
./git-archive --format=tar \
--prefix=$(GIT_TARNAME)/ HEAD^{tree} > $(GIT_TARNAME).tar
@mkdir -p $(GIT_TARNAME)
- @cp git.spec configure $(GIT_TARNAME)
+ @cp configure $(GIT_TARNAME)
@echo $(GIT_VERSION) > $(GIT_TARNAME)/version
@$(MAKE) -C git-gui TARDIR=../$(GIT_TARNAME)/git-gui dist-version
$(TAR) rf $(GIT_TARNAME).tar \
- $(GIT_TARNAME)/git.spec \
$(GIT_TARNAME)/configure \
$(GIT_TARNAME)/version \
$(GIT_TARNAME)/git-gui/version
@$(RM) -r $(GIT_TARNAME)
gzip -f -9 $(GIT_TARNAME).tar
-rpm: dist
- $(RPMBUILD) \
- --define "_source_filedigest_algorithm md5" \
- --define "_binary_filedigest_algorithm md5" \
- -ta $(GIT_TARNAME).tar.gz
+rpm::
+ @echo >&2 "Use distro packaged sources to run rpmbuild"
+ @false
+.PHONY: rpm
htmldocs = git-htmldocs-$(GIT_VERSION)
manpages = git-manpages-$(GIT_VERSION)
@@ -2450,8 +2443,8 @@ profile-clean:
$(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
clean: profile-clean coverage-clean
- $(RM) *.o *.res refs/*.o block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o
- $(RM) xdiff/*.o vcs-svn/*.o ewah/*.o builtin/*.o
+ $(RM) *.res
+ $(RM) $(OBJECTS)
$(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
$(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
@@ -2490,6 +2483,7 @@ ALL_COMMANDS += git-gui git-citool
.PHONY: check-docs
check-docs::
+ $(MAKE) -C Documentation lint-docs
@(for v in $(ALL_COMMANDS); \
do \
case "$$v" in \
diff --git a/README.md b/README.md
index d1ffbb6170..2087748f0c 100644
--- a/README.md
+++ b/README.md
@@ -17,11 +17,11 @@ including full documentation and Git related tools.
See [Documentation/gittutorial.txt][] to get started, then see
[Documentation/giteveryday.txt][] for a useful minimum set of commands, and
-[Documentation/git-commandname.txt][] for documentation of each command.
+Documentation/git-*commandname*.txt for documentation of each command.
If git has been correctly installed, then the tutorial can also be
read with "man gittutorial" or "git help tutorial", and the
-documentation of each command with "man git-commandname" or "git help
-commandname".
+documentation of each command with "man git-*commandname*" or "git help
+*commandname*".
CVS users may also want to read [Documentation/gitcvs-migration.txt][]
("man gitcvs-migration" or "git help cvs-migration" if git is
@@ -57,6 +57,5 @@ and the name as (depending on your mood):
[INSTALL]: INSTALL
[Documentation/gittutorial.txt]: Documentation/gittutorial.txt
[Documentation/giteveryday.txt]: Documentation/giteveryday.txt
-[Documentation/git-commandname.txt]: Documentation/git-commandname.txt
[Documentation/gitcvs-migration.txt]: Documentation/gitcvs-migration.txt
[Documentation/SubmittingPatches]: Documentation/SubmittingPatches
diff --git a/RelNotes b/RelNotes
index 04710daa95..66606735cb 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.8.2.txt \ No newline at end of file
+Documentation/RelNotes/2.9.0.txt \ No newline at end of file
diff --git a/archive-tar.c b/archive-tar.c
index 501ca97760..cb99df2814 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -181,7 +181,7 @@ static void prepare_header(struct archiver_args *args,
memcpy(header->magic, "ustar", 6);
memcpy(header->version, "00", 2);
- snprintf(header->chksum, sizeof(header->chksum), "%07o", ustar_header_chksum(header));
+ xsnprintf(header->chksum, sizeof(header->chksum), "%07o", ustar_header_chksum(header));
}
static int write_extended_header(struct archiver_args *args,
diff --git a/bisect.c b/bisect.c
index 7996c2907b..6d93edbcb9 100644
--- a/bisect.c
+++ b/bisect.c
@@ -860,8 +860,8 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
/* Create file BISECT_ANCESTORS_OK. */
fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (fd < 0)
- warning("could not create file '%s': %s",
- filename, strerror(errno));
+ warning_errno("could not create file '%s'",
+ filename);
else
close(fd);
done:
@@ -910,8 +910,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
*read_good = "good";
return;
} else {
- die("could not read file '%s': %s", filename,
- strerror(errno));
+ die_errno("could not read file '%s'", filename);
}
} else {
strbuf_getline_lf(&str, fp);
diff --git a/branch.c b/branch.c
index 4162443707..a5a8dcbd0e 100644
--- a/branch.c
+++ b/branch.c
@@ -334,15 +334,16 @@ void remove_branch_state(void)
unlink(git_path_squash_msg());
}
-void die_if_checked_out(const char *branch)
+void die_if_checked_out(const char *branch, int ignore_current_worktree)
{
- char *existing;
+ const struct worktree *wt;
- existing = find_shared_symref("HEAD", branch);
- if (existing) {
- skip_prefix(branch, "refs/heads/", &branch);
- die(_("'%s' is already checked out at '%s'"), branch, existing);
- }
+ wt = find_shared_symref("HEAD", branch);
+ if (!wt || (ignore_current_worktree && wt->is_current))
+ return;
+ skip_prefix(branch, "refs/heads/", &branch);
+ die(_("'%s' is already checked out at '%s'"),
+ branch, wt->path);
}
int replace_each_worktree_head_symref(const char *oldref, const char *newref)
@@ -357,7 +358,8 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref)
if (strcmp(oldref, worktrees[i]->head_ref))
continue;
- if (set_worktree_head_symref(worktrees[i]->git_dir, newref)) {
+ if (set_worktree_head_symref(get_worktree_git_dir(worktrees[i]),
+ newref)) {
ret = -1;
error(_("HEAD of working tree %s is not updated"),
worktrees[i]->path);
diff --git a/branch.h b/branch.h
index d69163daf7..b2f9649332 100644
--- a/branch.h
+++ b/branch.h
@@ -58,7 +58,7 @@ extern int read_branch_desc(struct strbuf *, const char *branch_name);
* worktree and die (with a message describing its checkout location) if
* it is.
*/
-extern void die_if_checked_out(const char *branch);
+extern void die_if_checked_out(const char *branch, int ignore_current_worktree);
/*
* Update all per-worktree HEADs pointing at the old ref to point the new ref.
diff --git a/builtin/am.c b/builtin/am.c
index d003939bc5..3dfe70b7a0 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -769,15 +769,15 @@ static int split_mail_conv(mail_conv_fn fn, struct am_state *state,
in = fopen(*paths, "r");
if (!in)
- return error(_("could not open '%s' for reading: %s"),
- *paths, strerror(errno));
+ return error_errno(_("could not open '%s' for reading"),
+ *paths);
mail = mkpath("%s/%0*d", state->dir, state->prec, i + 1);
out = fopen(mail, "w");
if (!out)
- return error(_("could not open '%s' for writing: %s"),
- mail, strerror(errno));
+ return error_errno(_("could not open '%s' for writing"),
+ mail);
ret = fn(out, in, keep_cr);
@@ -857,8 +857,7 @@ static int split_mail_stgit_series(struct am_state *state, const char **paths,
fp = fopen(*paths, "r");
if (!fp)
- return error(_("could not open '%s' for reading: %s"), *paths,
- strerror(errno));
+ return error_errno(_("could not open '%s' for reading"), *paths);
while (!strbuf_getline_lf(&sb, fp)) {
if (*sb.buf == '#')
diff --git a/builtin/apply.c b/builtin/apply.c
index ce3b77853c..8e4da2e1bd 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -4383,6 +4383,8 @@ static int apply_patch(int fd, const char *filename, int options)
listp = &patch->next;
}
else {
+ if (apply_verbosely)
+ say_patch_name(stderr, _("Skipped patch '%s'."), patch);
free_patch(patch);
skipped_patch++;
}
diff --git a/builtin/branch.c b/builtin/branch.c
index 0adba629d2..2ecde53bf8 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -220,12 +220,12 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
name = mkpathdup(fmt, bname.buf);
if (kinds == FILTER_REFS_BRANCHES) {
- char *worktree = find_shared_symref("HEAD", name);
- if (worktree) {
+ const struct worktree *wt =
+ find_shared_symref("HEAD", name);
+ if (wt) {
error(_("Cannot delete branch '%s' "
"checked out at '%s'"),
- bname.buf, worktree);
- free(worktree);
+ bname.buf, wt->path);
ret = 1;
continue;
}
@@ -375,12 +375,14 @@ static char *get_head_description(void)
strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
state.branch);
else if (state.detached_from) {
- /* TRANSLATORS: make sure these match _("HEAD detached at ")
- and _("HEAD detached from ") in wt-status.c */
if (state.detached_at)
+ /* TRANSLATORS: make sure this matches
+ "HEAD detached at " in wt-status.c */
strbuf_addf(&desc, _("(HEAD detached at %s)"),
state.detached_from);
else
+ /* TRANSLATORS: make sure this matches
+ "HEAD detached from " in wt-status.c */
strbuf_addf(&desc, _("(HEAD detached from %s)"),
state.detached_from);
}
@@ -524,6 +526,29 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
ref_array_clear(&array);
}
+static void reject_rebase_or_bisect_branch(const char *target)
+{
+ struct worktree **worktrees = get_worktrees();
+ int i;
+
+ for (i = 0; worktrees[i]; i++) {
+ struct worktree *wt = worktrees[i];
+
+ if (!wt->is_detached)
+ continue;
+
+ if (is_worktree_being_rebased(wt, target))
+ die(_("Branch %s is being rebased at %s"),
+ target, wt->path);
+
+ if (is_worktree_being_bisected(wt, target))
+ die(_("Branch %s is being bisected at %s"),
+ target, wt->path);
+ }
+
+ free_worktrees(worktrees);
+}
+
static void rename_branch(const char *oldname, const char *newname, int force)
{
struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
@@ -553,6 +578,8 @@ static void rename_branch(const char *oldname, const char *newname, int force)
validate_new_branchname(newname, &newref, force, clobber_head_ok);
+ reject_rebase_or_bisect_branch(oldref.buf);
+
strbuf_addf(&logmsg, "Branch: renamed %s to %s",
oldref.buf, newref.buf);
@@ -593,8 +620,7 @@ static int edit_branch_description(const char *branch_name)
branch_name, comment_line_char);
if (write_file_gently(git_path(edit_description), "%s", buf.buf)) {
strbuf_release(&buf);
- return error(_("could not write branch description template: %s"),
- strerror(errno));
+ return error_errno(_("could not write branch description template"));
}
strbuf_reset(&buf);
if (launch_editor(git_path(edit_description), &buf, NULL)) {
@@ -630,7 +656,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
BRANCH_TRACK_EXPLICIT),
OPT_SET_INT( 0, "set-upstream", &track, N_("change upstream info"),
BRANCH_TRACK_OVERRIDE),
- OPT_STRING('u', "set-upstream-to", &new_upstream, "upstream", "change the upstream info"),
+ OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
OPT_BOOL(0, "unset-upstream", &unset_upstream, "Unset the upstream info"),
OPT__COLOR(&branch_use_color, N_("use colored output")),
OPT_SET_INT('r', "remotes", &filter.kind, N_("act on remote-tracking branches"),
@@ -838,8 +864,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (argc == 1 && track == BRANCH_TRACK_OVERRIDE &&
!branch_existed && remote_tracking) {
fprintf(stderr, _("\nIf you wanted to make '%s' track '%s', do this:\n\n"), head, branch->name);
- fprintf(stderr, _(" git branch -d %s\n"), branch->name);
- fprintf(stderr, _(" git branch --set-upstream-to %s\n"), branch->name);
+ fprintf(stderr, " git branch -d %s\n", branch->name);
+ fprintf(stderr, " git branch --set-upstream-to %s\n", branch->name);
}
} else
diff --git a/builtin/checkout.c b/builtin/checkout.c
index efcbd8f6b5..3398c61e9a 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -242,7 +242,6 @@ static int checkout_paths(const struct checkout_opts *opts,
struct checkout state;
static char *ps_matched;
unsigned char rev[20];
- int flag;
struct commit *head;
int errs = 0;
struct lock_file *lock_file;
@@ -375,7 +374,7 @@ static int checkout_paths(const struct checkout_opts *opts,
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
- read_ref_full("HEAD", 0, rev, &flag);
+ read_ref_full("HEAD", 0, rev, NULL);
head = lookup_commit_reference_gently(rev, 1);
errs |= post_checkout_hook(head, head, 0);
@@ -1111,7 +1110,7 @@ static int checkout_branch(struct checkout_opts *opts,
char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
if (head_ref &&
(!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
- die_if_checked_out(new->path);
+ die_if_checked_out(new->path, 1);
free(head_ref);
}
diff --git a/builtin/clone.c b/builtin/clone.c
index 661639255c..5f867e67d8 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -40,6 +40,7 @@ static const char * const builtin_clone_usage[] = {
static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;
static int option_local = -1, option_no_hardlinks, option_shared, option_recursive;
+static int option_shallow_submodules = -1;
static char *option_template, *option_depth;
static char *option_origin = NULL;
static char *option_branch = NULL;
@@ -51,6 +52,7 @@ static enum transport_family family;
static struct string_list option_config;
static struct string_list option_reference;
static int option_dissociate;
+static int max_jobs = -1;
static struct option builtin_clone_options[] = {
OPT__VERBOSITY(&option_verbosity),
@@ -73,6 +75,8 @@ static struct option builtin_clone_options[] = {
N_("initialize submodules in the clone")),
OPT_BOOL(0, "recurse-submodules", &option_recursive,
N_("initialize submodules in the clone")),
+ OPT_INTEGER('j', "jobs", &max_jobs,
+ N_("number of submodules cloned in parallel")),
OPT_STRING(0, "template", &option_template, N_("template-directory"),
N_("directory from which templates will be used")),
OPT_STRING_LIST(0, "reference", &option_reference, N_("repo"),
@@ -89,6 +93,8 @@ static struct option builtin_clone_options[] = {
N_("create a shallow clone of that depth")),
OPT_BOOL(0, "single-branch", &option_single_branch,
N_("clone only one branch, HEAD or --branch")),
+ OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules,
+ N_("any cloned submodules will be shallow")),
OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
N_("separate git dir from working tree")),
OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
@@ -100,10 +106,6 @@ static struct option builtin_clone_options[] = {
OPT_END()
};
-static const char *argv_submodule[] = {
- "submodule", "update", "--init", "--recursive", NULL
-};
-
static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
{
static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
@@ -732,8 +734,20 @@ static int checkout(void)
err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
sha1_to_hex(sha1), "1", NULL);
- if (!err && option_recursive)
- err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
+ if (!err && option_recursive) {
+ struct argv_array args = ARGV_ARRAY_INIT;
+ argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);
+
+ if (option_shallow_submodules == 1
+ || (option_shallow_submodules == -1 && option_depth))
+ argv_array_push(&args, "--depth=1");
+
+ if (max_jobs != -1)
+ argv_array_pushf(&args, "--jobs=%d", max_jobs);
+
+ err = run_command_v_opt(args.argv, RUN_GIT_CMD);
+ argv_array_clear(&args);
+ }
return err;
}
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index 3feeffeab1..8a674bc9e7 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -33,10 +33,6 @@ static int commit_tree_config(const char *var, const char *value, void *cb)
int status = git_gpg_config(var, value, NULL);
if (status)
return status;
- if (!strcmp(var, "commit.gpgsign")) {
- sign_commit = git_config_bool(var, value) ? "" : NULL;
- return 0;
- }
return git_default_config(var, value, cb);
}
diff --git a/builtin/commit.c b/builtin/commit.c
index e13303787a..443ff9196d 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -114,6 +114,7 @@ static char *fixup_message, *squash_message;
static int all, also, interactive, patch_interactive, only, amend, signoff;
static int edit_flag = -1; /* unspecified */
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
+static int config_commit_verbose = -1; /* unspecified */
static int no_post_rewrite, allow_empty_message;
static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
static char *sign_commit;
@@ -186,6 +187,7 @@ static void status_init_config(struct wt_status *s, config_fn_t fn)
gitmodules_config();
git_config(fn, s);
determine_whence(s);
+ init_diff_ui_defaults();
s->hints = advice_status_hints; /* must come after git_config() */
}
@@ -1514,6 +1516,11 @@ static int git_commit_config(const char *k, const char *v, void *cb)
sign_commit = git_config_bool(k, v) ? "" : NULL;
return 0;
}
+ if (!strcmp(k, "commit.verbose")) {
+ int is_bool;
+ config_commit_verbose = git_config_bool_or_int(k, v, &is_bool);
+ return 0;
+ }
status = git_gpg_config(k, v, NULL);
if (status)
@@ -1660,9 +1667,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (parse_commit(current_head))
die(_("could not parse HEAD commit"));
}
+ verbose = -1; /* unspecified */
argc = parse_and_validate_options(argc, argv, builtin_commit_options,
builtin_commit_usage,
prefix, current_head, &s);
+ if (verbose == -1)
+ verbose = (config_commit_verbose < 0) ? 0 : config_commit_verbose;
+
if (dry_run)
return dry_run_commit(argc, argv, prefix, current_head, &s);
index_file = prepare_index(argc, argv, prefix, current_head, 0);
diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 8ed2eb8813..15c61fd8d1 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -24,6 +24,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
gitmodules_config();
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
rev.abbrev = 0;
+ precompose_argv(argc, argv);
argc = setup_revisions(argc, argv, &rev, NULL);
while (1 < argc && argv[1][0] == '-') {
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index d979824f93..1af373d002 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -21,6 +21,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
gitmodules_config();
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
rev.abbrev = 0;
+ precompose_argv(argc, argv);
argc = setup_revisions(argc, argv, &rev, NULL);
for (i = 1; i < argc; i++) {
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index 2a12b81e06..806dd7a885 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -114,6 +114,8 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
opt->disable_stdin = 1;
memset(&s_r_opt, 0, sizeof(s_r_opt));
s_r_opt.tweak = diff_tree_tweak_rev;
+
+ precompose_argv(argc, argv);
argc = setup_revisions(argc, argv, opt, &s_r_opt);
while (--argc > 0) {
diff --git a/builtin/diff.c b/builtin/diff.c
index 52c98a9217..b7a9405d9f 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -318,7 +318,9 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
if (!no_index)
gitmodules_config();
+ init_diff_ui_defaults();
git_config(git_diff_ui_config, NULL);
+ precompose_argv(argc, argv);
init_revisions(&rev, prefix);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index e4639d8eb1..1582ca7184 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -37,7 +37,7 @@ static int prune = -1; /* unspecified */
static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
static int tags = TAGS_DEFAULT, unshallow, update_shallow;
-static int max_children = 1;
+static int max_children = -1;
static enum transport_family family;
static const char *depth;
static const char *upload_pack;
@@ -607,7 +607,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
fp = fopen(filename, "a");
if (!fp)
- return error(_("cannot open %s: %s\n"), filename, strerror(errno));
+ return error_errno(_("cannot open %s"), filename);
if (raw_url)
url = transport_anonymize_url(raw_url);
@@ -848,7 +848,7 @@ static int truncate_fetch_head(void)
FILE *fp = fopen_for_writing(filename);
if (!fp)
- return error(_("cannot open %s: %s\n"), filename, strerror(errno));
+ return error_errno(_("cannot open %s"), filename);
fclose(fp);
return 0;
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 55eac756f7..3f27456883 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -493,13 +493,12 @@ static void fsck_object_dir(const char *path)
static int fsck_head_link(void)
{
- int flag;
int null_is_error = 0;
if (verbose)
fprintf(stderr, "Checking HEAD link\n");
- head_points_at = resolve_ref_unsafe("HEAD", 0, head_oid.hash, &flag);
+ head_points_at = resolve_ref_unsafe("HEAD", 0, head_oid.hash, NULL);
if (!head_points_at) {
errors_found |= ERROR_REFS;
return error("Invalid HEAD");
diff --git a/builtin/grep.c b/builtin/grep.c
index 111b6f6cf1..462e607901 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -438,7 +438,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
strbuf_add(base, entry.path, te_len);
if (S_ISREG(entry.mode)) {
- hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len,
+ hit |= grep_sha1(opt, entry.oid->hash, base->buf, tn_len,
check_attr ? base->buf + tn_len : NULL);
}
else if (S_ISDIR(entry.mode)) {
@@ -447,10 +447,10 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
void *data;
unsigned long size;
- data = lock_and_read_sha1_file(entry.sha1, &type, &size);
+ data = lock_and_read_sha1_file(entry.oid->hash, &type, &size);
if (!data)
die(_("unable to read tree (%s)"),
- sha1_to_hex(entry.sha1));
+ oid_to_hex(entry.oid));
strbuf_addch(base, '/');
init_tree_desc(&sub, data, size);
diff --git a/builtin/help.c b/builtin/help.c
index 3c55ce4563..88480131cf 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -127,7 +127,7 @@ static void exec_woman_emacs(const char *path, const char *page)
path = "emacsclient";
strbuf_addf(&man_page, "(woman \"%s\")", page);
execlp(path, "emacsclient", "-e", man_page.buf, (char *)NULL);
- warning(_("failed to exec '%s': %s"), path, strerror(errno));
+ warning_errno(_("failed to exec '%s'"), path);
}
}
@@ -148,7 +148,7 @@ static void exec_man_konqueror(const char *path, const char *page)
path = "kfmclient";
strbuf_addf(&man_page, "man:%s(1)", page);
execlp(path, filename, "newTab", man_page.buf, (char *)NULL);
- warning(_("failed to exec '%s': %s"), path, strerror(errno));
+ warning_errno(_("failed to exec '%s'"), path);
}
}
@@ -157,7 +157,7 @@ static void exec_man_man(const char *path, const char *page)
if (!path)
path = "man";
execlp(path, "man", page, (char *)NULL);
- warning(_("failed to exec '%s': %s"), path, strerror(errno));
+ warning_errno(_("failed to exec '%s'"), path);
}
static void exec_man_cmd(const char *cmd, const char *page)
@@ -165,7 +165,7 @@ static void exec_man_cmd(const char *cmd, const char *page)
struct strbuf shell_cmd = STRBUF_INIT;
strbuf_addf(&shell_cmd, "%s %s", cmd, page);
execl(SHELL_PATH, SHELL_PATH, "-c", shell_cmd.buf, (char *)NULL);
- warning(_("failed to exec '%s': %s"), cmd, strerror(errno));
+ warning(_("failed to exec '%s'"), cmd);
}
static void add_man_viewer(const char *name)
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 2d1eb8bb8a..e8c71fc1d2 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1250,7 +1250,9 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha
nr_unresolved * sizeof(*objects));
f = sha1fd(output_fd, curr_pack);
fix_unresolved_deltas(f);
- strbuf_addf(&msg, _("completed with %d local objects"),
+ strbuf_addf(&msg, Q_("completed with %d local object",
+ "completed with %d local objects",
+ nr_objects - nr_objects_initial),
nr_objects - nr_objects_initial);
stop_progress_msg(&progress, msg.buf);
strbuf_release(&msg);
diff --git a/builtin/log.c b/builtin/log.c
index 0d738d6ddc..099f4f7be9 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -100,6 +100,12 @@ static int log_line_range_callback(const struct option *option, const char *arg,
return 0;
}
+static void init_log_defaults(void)
+{
+ init_grep_defaults();
+ init_diff_ui_defaults();
+}
+
static void cmd_log_init_defaults(struct rev_info *rev)
{
if (fmt_pretty)
@@ -416,7 +422,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
struct rev_info rev;
struct setup_revision_opt opt;
- init_grep_defaults();
+ init_log_defaults();
git_config(git_log_config, NULL);
init_revisions(&rev, prefix);
@@ -527,7 +533,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
struct pathspec match_all;
int i, count, ret = 0;
- init_grep_defaults();
+ init_log_defaults();
git_config(git_log_config, NULL);
memset(&match_all, 0, sizeof(match_all));
@@ -608,7 +614,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
struct rev_info rev;
struct setup_revision_opt opt;
- init_grep_defaults();
+ init_log_defaults();
git_config(git_log_config, NULL);
init_revisions(&rev, prefix);
@@ -647,7 +653,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
struct rev_info rev;
struct setup_revision_opt opt;
- init_grep_defaults();
+ init_log_defaults();
git_config(git_log_config, NULL);
init_revisions(&rev, prefix);
@@ -696,6 +702,7 @@ static void add_header(const char *value)
#define THREAD_DEEP 2
static int thread;
static int do_signoff;
+static int base_auto;
static const char *signature = git_version_string;
static const char *signature_file;
static int config_cover_letter;
@@ -780,6 +787,10 @@ static int git_format_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "format.outputdirectory"))
return git_config_string(&config_output_directory, var, value);
+ if (!strcmp(var, "format.useautobase")) {
+ base_auto = git_config_bool(var, value);
+ return 0;
+ }
return git_log_config(var, value, cb);
}
@@ -1185,6 +1196,155 @@ static int from_callback(const struct option *opt, const char *arg, int unset)
return 0;
}
+struct base_tree_info {
+ struct object_id base_commit;
+ int nr_patch_id, alloc_patch_id;
+ struct object_id *patch_id;
+};
+
+static struct commit *get_base_commit(const char *base_commit,
+ struct commit **list,
+ int total)
+{
+ struct commit *base = NULL;
+ struct commit **rev;
+ int i = 0, rev_nr = 0;
+
+ if (base_commit && strcmp(base_commit, "auto")) {
+ base = lookup_commit_reference_by_name(base_commit);
+ if (!base)
+ die(_("Unknown commit %s"), base_commit);
+ } else if ((base_commit && !strcmp(base_commit, "auto")) || base_auto) {
+ struct branch *curr_branch = branch_get(NULL);
+ const char *upstream = branch_get_upstream(curr_branch, NULL);
+ if (upstream) {
+ struct commit_list *base_list;
+ struct commit *commit;
+ unsigned char sha1[20];
+
+ if (get_sha1(upstream, sha1))
+ die(_("Failed to resolve '%s' as a valid ref."), upstream);
+ commit = lookup_commit_or_die(sha1, "upstream base");
+ base_list = get_merge_bases_many(commit, total, list);
+ /* There should be one and only one merge base. */
+ if (!base_list || base_list->next)
+ die(_("Could not find exact merge base."));
+ base = base_list->item;
+ free_commit_list(base_list);
+ } else {
+ die(_("Failed to get upstream, if you want to record base commit automatically,\n"
+ "please use git branch --set-upstream-to to track a remote branch.\n"
+ "Or you could specify base commit by --base=<base-commit-id> manually."));
+ }
+ }
+
+ ALLOC_ARRAY(rev, total);
+ for (i = 0; i < total; i++)
+ rev[i] = list[i];
+
+ rev_nr = total;
+ /*
+ * Get merge base through pair-wise computations
+ * and store it in rev[0].
+ */
+ while (rev_nr > 1) {
+ for (i = 0; i < rev_nr / 2; i++) {
+ struct commit_list *merge_base;
+ merge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]);
+ if (!merge_base || merge_base->next)
+ die(_("Failed to find exact merge base"));
+
+ rev[i] = merge_base->item;
+ }
+
+ if (rev_nr % 2)
+ rev[i] = rev[2 * i];
+ rev_nr = (rev_nr + 1) / 2;
+ }
+
+ if (!in_merge_bases(base, rev[0]))
+ die(_("base commit should be the ancestor of revision list"));
+
+ for (i = 0; i < total; i++) {
+ if (base == list[i])
+ die(_("base commit shouldn't be in revision list"));
+ }
+
+ free(rev);
+ return base;
+}
+
+static void prepare_bases(struct base_tree_info *bases,
+ struct commit *base,
+ struct commit **list,
+ int total)
+{
+ struct commit *commit;
+ struct rev_info revs;
+ struct diff_options diffopt;
+ int i;
+
+ if (!base)
+ return;
+
+ diff_setup(&diffopt);
+ DIFF_OPT_SET(&diffopt, RECURSIVE);
+ diff_setup_done(&diffopt);
+
+ oidcpy(&bases->base_commit, &base->object.oid);
+
+ init_revisions(&revs, NULL);
+ revs.max_parents = 1;
+ revs.topo_order = 1;
+ for (i = 0; i < total; i++) {
+ list[i]->object.flags &= ~UNINTERESTING;
+ add_pending_object(&revs, &list[i]->object, "rev_list");
+ list[i]->util = (void *)1;
+ }
+ base->object.flags |= UNINTERESTING;
+ add_pending_object(&revs, &base->object, "base");
+
+ if (prepare_revision_walk(&revs))
+ die(_("revision walk setup failed"));
+ /*
+ * Traverse the commits list, get prerequisite patch ids
+ * and stuff them in bases structure.
+ */
+ while ((commit = get_revision(&revs)) != NULL) {
+ unsigned char sha1[20];
+ struct object_id *patch_id;
+ if (commit->util)
+ continue;
+ if (commit_patch_id(commit, &diffopt, sha1))
+ die(_("cannot get patch id"));
+ ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id);
+ patch_id = bases->patch_id + bases->nr_patch_id;
+ hashcpy(patch_id->hash, sha1);
+ bases->nr_patch_id++;
+ }
+}
+
+static void print_bases(struct base_tree_info *bases)
+{
+ int i;
+
+ /* Only do this once, either for the cover or for the first one */
+ if (is_null_oid(&bases->base_commit))
+ return;
+
+ /* Show the base commit */
+ printf("base-commit: %s\n", oid_to_hex(&bases->base_commit));
+
+ /* Show the prerequisite patches */
+ for (i = bases->nr_patch_id - 1; i >= 0; i--)
+ printf("prerequisite-patch-id: %s\n", oid_to_hex(&bases->patch_id[i]));
+
+ free(bases->patch_id);
+ bases->nr_patch_id = 0;
+ bases->alloc_patch_id = 0;
+ oidclr(&bases->base_commit);
+}
+
int cmd_format_patch(int argc, const char **argv, const char *prefix)
{
struct commit *commit;
@@ -1209,6 +1369,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
int reroll_count = -1;
char *branch_name = NULL;
char *from = NULL;
+ char *base_commit = NULL;
+ struct base_tree_info bases;
+
const struct option builtin_format_patch_options[] = {
{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
N_("use [PATCH n/m] even with a single patch"),
@@ -1271,6 +1434,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
PARSE_OPT_OPTARG, thread_callback },
OPT_STRING(0, "signature", &signature, N_("signature"),
N_("add a signature")),
+ OPT_STRING(0, "base", &base_commit, N_("base-commit"),
+ N_("add prerequisite tree info to the patch series")),
OPT_FILENAME(0, "signature-file", &signature_file,
N_("add a signature from a file")),
OPT__QUIET(&quiet, N_("don't print the patch filenames")),
@@ -1280,10 +1445,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
extra_hdr.strdup_strings = 1;
extra_to.strdup_strings = 1;
extra_cc.strdup_strings = 1;
- init_grep_defaults();
+ init_log_defaults();
git_config(git_format_config, NULL);
init_revisions(&rev, prefix);
rev.commit_format = CMIT_FMT_EMAIL;
+ rev.expand_tabs_in_log_default = 0;
rev.verbose_header = 1;
rev.diff = 1;
rev.max_parents = 1;
@@ -1507,6 +1673,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
signature = strbuf_detach(&buf, NULL);
}
+ memset(&bases, 0, sizeof(bases));
+ if (base_commit || base_auto) {
+ struct commit *base = get_base_commit(base_commit, list, nr);
+ reset_revision_walk();
+ prepare_bases(&bases, base, list, nr);
+ }
+
if (in_reply_to || thread || cover_letter)
rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
if (in_reply_to) {
@@ -1520,6 +1693,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
gen_message_id(&rev, "cover");
make_cover_letter(&rev, use_stdout,
origin, nr, list, branch_name, quiet);
+ print_bases(&bases);
total++;
start_number--;
}
@@ -1585,6 +1759,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.mime_boundary);
else
print_signature();
+ print_bases(&bases);
}
if (!use_stdout)
fclose(stdout);
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 104277acc4..4859ede38a 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -109,7 +109,7 @@ static int populate_maildir_list(struct string_list *list, const char *path)
if ((dir = opendir(name)) == NULL) {
if (errno == ENOENT)
continue;
- error("cannot opendir %s (%s)", name, strerror(errno));
+ error_errno("cannot opendir %s", name);
goto out;
}
@@ -174,12 +174,12 @@ static int split_maildir(const char *maildir, const char *dir,
f = fopen(file, "r");
if (!f) {
- error("cannot open mail %s (%s)", file, strerror(errno));
+ error_errno("cannot open mail %s", file);
goto out;
}
if (strbuf_getwholeline(&buf, f, '\n')) {
- error("cannot read mail %s (%s)", file, strerror(errno));
+ error_errno("cannot read mail %s", file);
goto out;
}
@@ -210,7 +210,7 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,
int file_done = 0;
if (!f) {
- error("cannot open mbox %s", file);
+ error_errno("cannot open mbox %s", file);
goto out;
}
@@ -318,7 +318,7 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
}
if (stat(arg, &argstat) == -1) {
- error("cannot stat %s (%s)", arg, strerror(errno));
+ error_errno("cannot stat %s", arg);
return 1;
}
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index 55447053f2..13e22a2f0b 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -62,8 +62,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
usage_with_options(merge_file_usage, options);
if (quiet) {
if (!freopen("/dev/null", "w", stderr))
- return error("failed to redirect stderr to /dev/null: "
- "%s", strerror(errno));
+ return error_errno("failed to redirect stderr to /dev/null");
}
if (prefix)
@@ -95,12 +94,13 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
if (!f)
- ret = error("Could not open %s for writing", filename);
+ ret = error_errno("Could not open %s for writing",
+ filename);
else if (result.size &&
fwrite(result.ptr, result.size, 1, f) != 1)
- ret = error("Could not write to %s", filename);
+ ret = error_errno("Could not write to %s", filename);
else if (fclose(f))
- ret = error("Could not close %s", filename);
+ ret = error_errno("Could not close %s", filename);
free(result.ptr);
}
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index ca570041df..5b7ab9b967 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -150,15 +150,15 @@ static void show_result(void)
/* An empty entry never compares same, not even to another empty entry */
static int same_entry(struct name_entry *a, struct name_entry *b)
{
- return a->sha1 &&
- b->sha1 &&
- !hashcmp(a->sha1, b->sha1) &&
+ return a->oid &&
+ b->oid &&
+ !oidcmp(a->oid, b->oid) &&
a->mode == b->mode;
}
static int both_empty(struct name_entry *a, struct name_entry *b)
{
- return !(a->sha1 || b->sha1);
+ return !(a->oid || b->oid);
}
static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path)
@@ -188,8 +188,8 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s
return;
path = traverse_path(info, result);
- orig = create_entry(2, ours->mode, ours->sha1, path);
- final = create_entry(0, result->mode, result->sha1, path);
+ orig = create_entry(2, ours->mode, ours->oid->hash, path);
+ final = create_entry(0, result->mode, result->oid->hash, path);
final->link = orig;
@@ -213,7 +213,7 @@ static void unresolved_directory(const struct traverse_info *info,
newbase = traverse_path(info, p);
-#define ENTRY_SHA1(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->sha1 : NULL)
+#define ENTRY_SHA1(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->oid->hash : NULL)
buf0 = fill_tree_descriptor(t+0, ENTRY_SHA1(n + 0));
buf1 = fill_tree_descriptor(t+1, ENTRY_SHA1(n + 1));
buf2 = fill_tree_descriptor(t+2, ENTRY_SHA1(n + 2));
@@ -239,7 +239,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info
path = entry->path;
else
path = traverse_path(info, n);
- link = create_entry(stage, n->mode, n->sha1, path);
+ link = create_entry(stage, n->mode, n->oid->hash, path);
link->link = entry;
return link;
}
@@ -314,7 +314,7 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s
}
if (same_entry(entry+0, entry+1)) {
- if (entry[2].sha1 && !S_ISDIR(entry[2].mode)) {
+ if (entry[2].oid && !S_ISDIR(entry[2].mode)) {
/* We did not touch, they modified -- take theirs */
resolve(info, entry+1, entry+2);
return mask;
diff --git a/builtin/merge.c b/builtin/merge.c
index bf2f2614fb..b555a1bf9c 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -64,6 +64,7 @@ static int option_renormalize;
static int verbosity;
static int allow_rerere_auto;
static int abort_current_merge;
+static int allow_unrelated_histories;
static int show_progress = -1;
static int default_to_upstream = 1;
static const char *sign_commit;
@@ -221,6 +222,8 @@ static struct option builtin_merge_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "abort", &abort_current_merge,
N_("abort the current in-progress merge")),
+ OPT_BOOL(0, "allow-unrelated-histories", &allow_unrelated_histories,
+ N_("allow merging unrelated histories")),
OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
@@ -819,6 +822,14 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
{
unsigned char result_tree[20], result_commit[20];
struct commit_list *parents, **pptr = &parents;
+ static struct lock_file lock;
+
+ hold_locked_index(&lock, 1);
+ refresh_cache(REFRESH_QUIET);
+ if (active_cache_changed &&
+ write_locked_index(&the_index, &lock, COMMIT_LOCK))
+ return error(_("Unable to write index."));
+ rollback_lock_file(&lock);
write_tree_trivial(result_tree);
printf(_("Wonderful.\n"));
@@ -1165,7 +1176,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
struct commit *head_commit;
struct strbuf buf = STRBUF_INIT;
const char *head_arg;
- int flag, i, ret = 0, head_subsumed;
+ int i, ret = 0, head_subsumed;
int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
struct commit_list *common = NULL;
const char *best_strategy = NULL, *wt_strategy = NULL;
@@ -1179,7 +1190,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
*/
- branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, &flag);
+ branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, NULL);
if (branch && starts_with(branch, "refs/heads/"))
branch += 11;
if (!branch || is_null_sha1(head_sha1))
@@ -1187,6 +1198,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else
head_commit = lookup_commit_or_die(head_sha1, "HEAD");
+ init_diff_ui_defaults();
git_config(git_merge_config, NULL);
if (branch_mergeoptions)
@@ -1397,9 +1409,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.oid.hash,
NULL, 0, UPDATE_REFS_DIE_ON_ERR);
- if (remoteheads && !common)
- ; /* No common ancestors found. We need a real merge. */
- else if (!remoteheads ||
+ if (remoteheads && !common) {
+ /* No common ancestors found. */
+ if (!allow_unrelated_histories)
+ die(_("refusing to merge unrelated histories"));
+ /* otherwise, we need a real merge. */
+ } else if (!remoteheads ||
(!remoteheads->next && !common->next &&
common->item == remoteheads->item)) {
/*
diff --git a/builtin/mv.c b/builtin/mv.c
index aeae855e2b..a2014266b6 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -252,15 +252,18 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
int pos;
if (show_only || verbose)
printf(_("Renaming %s to %s\n"), src, dst);
- if (!show_only && mode != INDEX) {
- if (rename(src, dst) < 0 && !ignore_errors)
- die_errno(_("renaming '%s' failed"), src);
- if (submodule_gitfile[i]) {
- if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR)
- connect_work_tree_and_git_dir(dst, submodule_gitfile[i]);
- if (!update_path_in_gitmodules(src, dst))
- gitmodules_modified = 1;
- }
+ if (show_only)
+ continue;
+ if (mode != INDEX && rename(src, dst) < 0) {
+ if (ignore_errors)
+ continue;
+ die_errno(_("renaming '%s' failed"), src);
+ }
+ if (submodule_gitfile[i]) {
+ if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR)
+ connect_work_tree_and_git_dir(dst, submodule_gitfile[i]);
+ if (!update_path_in_gitmodules(src, dst))
+ gitmodules_modified = 1;
}
if (mode == WORKING_DIRECTORY)
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 092e03c3cc..57be35faf5 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -10,6 +10,7 @@
typedef struct rev_name {
const char *tip_name;
+ unsigned long taggerdate;
int generation;
int distance;
} rev_name;
@@ -20,7 +21,8 @@ static long cutoff = LONG_MAX;
#define MERGE_TRAVERSAL_WEIGHT 65535
static void name_rev(struct commit *commit,
- const char *tip_name, int generation, int distance,
+ const char *tip_name, unsigned long taggerdate,
+ int generation, int distance,
int deref)
{
struct rev_name *name = (struct rev_name *)commit->util;
@@ -43,9 +45,12 @@ static void name_rev(struct commit *commit,
name = xmalloc(sizeof(rev_name));
commit->util = name;
goto copy_data;
- } else if (name->distance > distance) {
+ } else if (name->taggerdate > taggerdate ||
+ (name->taggerdate == taggerdate &&
+ name->distance > distance)) {
copy_data:
name->tip_name = tip_name;
+ name->taggerdate = taggerdate;
name->generation = generation;
name->distance = distance;
} else
@@ -66,11 +71,11 @@ copy_data:
new_name = xstrfmt("%.*s^%d", (int)len, tip_name,
parent_number);
- name_rev(parents->item, new_name, 0,
+ name_rev(parents->item, new_name, taggerdate, 0,
distance + MERGE_TRAVERSAL_WEIGHT, 0);
} else {
- name_rev(parents->item, tip_name, generation + 1,
- distance + 1, 0);
+ name_rev(parents->item, tip_name, taggerdate,
+ generation + 1, distance + 1, 0);
}
}
}
@@ -140,6 +145,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
struct name_ref_data *data = cb_data;
int can_abbreviate_output = data->tags_only && data->name_only;
int deref = 0;
+ unsigned long taggerdate = ULONG_MAX;
if (data->tags_only && !starts_with(path, "refs/tags/"))
return 0;
@@ -164,12 +170,13 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
break; /* broken repository */
o = parse_object(t->tagged->oid.hash);
deref = 1;
+ taggerdate = t->date;
}
if (o && o->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *)o;
path = name_ref_abbrev(path, can_abbreviate_output);
- name_rev(commit, xstrdup(path), 0, 0, deref);
+ name_rev(commit, xstrdup(path), taggerdate, 0, 0, deref);
}
return 0;
}
diff --git a/builtin/notes.c b/builtin/notes.c
index ed6f2222f4..c65b59ad9a 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -744,13 +744,14 @@ static int merge_commit(struct notes_merge_options *o)
static int git_config_get_notes_strategy(const char *key,
enum notes_merge_strategy *strategy)
{
- const char *value;
+ char *value;
- if (git_config_get_string_const(key, &value))
+ if (git_config_get_string(key, &value))
return 1;
if (parse_notes_merge_strategy(value, strategy))
git_die_config(key, "unknown notes merge strategy %s", value);
+ free(value);
return 0;
}
@@ -846,15 +847,15 @@ static int merge(int argc, const char **argv, const char *prefix)
update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
0, UPDATE_REFS_DIE_ON_ERR);
else { /* Merge has unresolved conflicts */
- char *existing;
+ const struct worktree *wt;
/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
0, UPDATE_REFS_DIE_ON_ERR);
/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
- existing = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
- if (existing)
+ wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
+ if (wt)
die(_("A notes merge into %s is already in-progress at %s"),
- default_notes_ref(), existing);
+ default_notes_ref(), wt->path);
if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
die("Failed to store link to current notes ref (%s)",
default_notes_ref());
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index a27de5b323..8f5e358e22 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -759,6 +759,10 @@ static off_t write_reused_pack(struct sha1file *f)
return reuse_packfile_offset - sizeof(struct pack_header);
}
+static const char no_split_warning[] = N_(
+"disabling bitmap writing, packs are split due to pack.packSizeLimit"
+);
+
static void write_pack_file(void)
{
uint32_t i = 0, j;
@@ -813,7 +817,10 @@ static void write_pack_file(void)
fixup_pack_header_footer(fd, sha1, pack_tmp_name,
nr_written, sha1, offset);
close(fd);
- write_bitmap_index = 0;
+ if (write_bitmap_index) {
+ warning(_(no_split_warning));
+ write_bitmap_index = 0;
+ }
}
if (!pack_to_stdout) {
@@ -828,8 +835,7 @@ static void write_pack_file(void)
* to preserve this property.
*/
if (stat(pack_tmp_name, &st) < 0) {
- warning("failed to stat %s: %s",
- pack_tmp_name, strerror(errno));
+ warning_errno("failed to stat %s", pack_tmp_name);
} else if (!last_mtime) {
last_mtime = st.st_mtime;
} else {
@@ -837,8 +843,7 @@ static void write_pack_file(void)
utb.actime = st.st_atime;
utb.modtime = --last_mtime;
if (utime(pack_tmp_name, &utb) < 0)
- warning("failed utime() on %s: %s",
- pack_tmp_name, strerror(errno));
+ warning_errno("failed utime() on %s", pack_tmp_name);
}
strbuf_addf(&tmpname, "%s-", base_name);
@@ -1186,7 +1191,7 @@ static void add_pbase_object(struct tree_desc *tree,
if (cmp < 0)
return;
if (name[cmplen] != '/') {
- add_object_entry(entry.sha1,
+ add_object_entry(entry.oid->hash,
object_type(entry.mode),
fullname, 1);
return;
@@ -1197,7 +1202,7 @@ static void add_pbase_object(struct tree_desc *tree,
const char *down = name+cmplen+1;
int downlen = name_cmp_len(down);
- tree = pbase_tree_get(entry.sha1);
+ tree = pbase_tree_get(entry.oid->hash);
if (!tree)
return;
init_tree_desc(&sub, tree->tree_data, tree->tree_size);
diff --git a/builtin/pull.c b/builtin/pull.c
index 10eff03967..1d7333c8a1 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -86,9 +86,12 @@ static char *opt_commit;
static char *opt_edit;
static char *opt_ff;
static char *opt_verify_signatures;
+static int opt_autostash = -1;
+static int config_autostash;
static struct argv_array opt_strategies = ARGV_ARRAY_INIT;
static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;
static char *opt_gpg_sign;
+static int opt_allow_unrelated_histories;
/* Options passed to git-fetch */
static char *opt_all;
@@ -149,6 +152,8 @@ static struct option pull_options[] = {
OPT_PASSTHRU(0, "verify-signatures", &opt_verify_signatures, NULL,
N_("verify that the named commit has a valid GPG signature"),
PARSE_OPT_NOARG),
+ OPT_BOOL(0, "autostash", &opt_autostash,
+ N_("automatically stash/stash pop before and after rebase")),
OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
N_("merge strategy to use"),
0),
@@ -159,6 +164,9 @@ static struct option pull_options[] = {
OPT_PASSTHRU('S', "gpg-sign", &opt_gpg_sign, N_("key-id"),
N_("GPG sign commit"),
PARSE_OPT_OPTARG),
+ OPT_SET_INT(0, "allow-unrelated-histories",
+ &opt_allow_unrelated_histories,
+ N_("allow merging unrelated histories"), 1),
/* Options passed to git-fetch */
OPT_GROUP(N_("Options related to fetching")),
@@ -306,6 +314,18 @@ static enum rebase_type config_get_rebase(void)
}
/**
+ * Read config variables.
+ */
+static int git_pull_config(const char *var, const char *value, void *cb)
+{
+ if (!strcmp(var, "rebase.autostash")) {
+ config_autostash = git_config_bool(var, value);
+ return 0;
+ }
+ return git_default_config(var, value, cb);
+}
+
+/**
* Returns 1 if there are unstaged changes, 0 otherwise.
*/
static int has_unstaged_changes(const char *prefix)
@@ -458,13 +478,13 @@ static void NORETURN die_no_merge_candidates(const char *repo, const char **refs
fprintf_ln(stderr, _("Please specify which branch you want to merge with."));
fprintf_ln(stderr, _("See git-pull(1) for details."));
fprintf(stderr, "\n");
- fprintf_ln(stderr, " git pull <remote> <branch>");
+ fprintf_ln(stderr, " git pull %s %s", _("<remote>"), _("<branch>"));
fprintf(stderr, "\n");
} else if (!curr_branch->merge_nr) {
const char *remote_name = NULL;
if (for_each_remote(get_only_remote, &remote_name) || !remote_name)
- remote_name = "<remote>";
+ remote_name = _("<remote>");
fprintf_ln(stderr, _("There is no tracking information for the current branch."));
if (opt_rebase)
@@ -473,12 +493,12 @@ static void NORETURN die_no_merge_candidates(const char *repo, const char **refs
fprintf_ln(stderr, _("Please specify which branch you want to merge with."));
fprintf_ln(stderr, _("See git-pull(1) for details."));
fprintf(stderr, "\n");
- fprintf_ln(stderr, " git pull <remote> <branch>");
+ fprintf_ln(stderr, " git pull %s %s", _("<remote>"), _("<branch>"));
fprintf(stderr, "\n");
- fprintf_ln(stderr, _("If you wish to set tracking information for this branch you can do so with:\n"
- "\n"
- " git branch --set-upstream-to=%s/<branch> %s\n"),
- remote_name, curr_branch->name);
+ fprintf_ln(stderr, _("If you wish to set tracking information for this branch you can do so with:"));
+ fprintf(stderr, "\n");
+ fprintf_ln(stderr, " git branch --set-upstream-to=%s/%s %s\n",
+ remote_name, _("<branch>"), curr_branch->name);
} else
fprintf_ln(stderr, _("Your configuration specifies to merge with the ref '%s'\n"
"from the remote, but no such ref was fetched."),
@@ -612,6 +632,8 @@ static int run_merge(void)
argv_array_pushv(&args, opt_strategy_opts.argv);
if (opt_gpg_sign)
argv_array_push(&args, opt_gpg_sign);
+ if (opt_allow_unrelated_histories > 0)
+ argv_array_push(&args, "--allow-unrelated-histories");
argv_array_push(&args, "FETCH_HEAD");
ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
@@ -789,6 +811,10 @@ static int run_rebase(const unsigned char *curr_head,
argv_array_pushv(&args, opt_strategy_opts.argv);
if (opt_gpg_sign)
argv_array_push(&args, opt_gpg_sign);
+ if (opt_autostash == 0)
+ argv_array_push(&args, "--no-autostash");
+ else if (opt_autostash == 1)
+ argv_array_push(&args, "--autostash");
argv_array_push(&args, "--onto");
argv_array_push(&args, sha1_to_hex(merge_head));
@@ -823,7 +849,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
if (opt_rebase < 0)
opt_rebase = config_get_rebase();
- git_config(git_default_config, NULL);
+ git_config(git_pull_config, NULL);
if (read_cache_unmerged())
die_resolve_conflict("Pull");
@@ -834,13 +860,17 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
if (get_sha1("HEAD", orig_head))
hashclr(orig_head);
+ if (!opt_rebase && opt_autostash != -1)
+ die(_("--[no-]autostash option is only valid with --rebase."));
+
if (opt_rebase) {
- int autostash = 0;
+ int autostash = config_autostash;
+ if (opt_autostash != -1)
+ autostash = opt_autostash;
if (is_null_sha1(orig_head) && !is_cache_unborn())
die(_("Updating an unborn branch with changes added to the index."));
- git_config_get_bool("rebase.autostash", &autostash);
if (!autostash)
die_on_unclean_work_tree(prefix);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index c8e32b297c..a744437b58 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -21,7 +21,10 @@
#include "sigchain.h"
#include "fsck.h"
-static const char receive_pack_usage[] = "git receive-pack <git-dir>";
+static const char * const receive_pack_usage[] = {
+ N_("git receive-pack <git-dir>"),
+ NULL
+};
enum deny_action {
DENY_UNCONFIGURED,
@@ -49,7 +52,7 @@ static int quiet;
static int prefer_ofs_delta = 1;
static int auto_update_server_info;
static int auto_gc = 1;
-static int fix_thin = 1;
+static int reject_thin;
static int stateless_rpc;
static const char *service_dir;
static const char *head_name;
@@ -1081,13 +1084,13 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
if (!(flag & REF_ISSYMREF))
return;
- dst_name = strip_namespace(dst_name);
if (!dst_name) {
rp_error("refusing update to broken symref '%s'", cmd->ref_name);
cmd->skip_update = 1;
cmd->error_string = "broken symref";
return;
}
+ dst_name = strip_namespace(dst_name);
if ((item = string_list_lookup(list, dst_name)) == NULL)
return;
@@ -1548,7 +1551,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)
if (fsck_objects)
argv_array_pushf(&child.args, "--strict%s",
fsck_msg_types.buf);
- if (fix_thin)
+ if (!reject_thin)
argv_array_push(&child.args, "--fix-thin");
child.out = -1;
child.err = err_fd;
@@ -1707,45 +1710,29 @@ static int delete_only(struct command *commands)
int cmd_receive_pack(int argc, const char **argv, const char *prefix)
{
int advertise_refs = 0;
- int i;
struct command *commands;
struct sha1_array shallow = SHA1_ARRAY_INIT;
struct sha1_array ref = SHA1_ARRAY_INIT;
struct shallow_info si;
- packet_trace_identity("receive-pack");
+ struct option options[] = {
+ OPT__QUIET(&quiet, N_("quiet")),
+ OPT_HIDDEN_BOOL(0, "stateless-rpc", &stateless_rpc, NULL),
+ OPT_HIDDEN_BOOL(0, "advertise-refs", &advertise_refs, NULL),
+ OPT_HIDDEN_BOOL(0, "reject-thin-pack-for-testing", &reject_thin, NULL),
+ OPT_END()
+ };
- argv++;
- for (i = 1; i < argc; i++) {
- const char *arg = *argv++;
+ packet_trace_identity("receive-pack");
- if (*arg == '-') {
- if (!strcmp(arg, "--quiet")) {
- quiet = 1;
- continue;
- }
+ argc = parse_options(argc, argv, prefix, options, receive_pack_usage, 0);
- if (!strcmp(arg, "--advertise-refs")) {
- advertise_refs = 1;
- continue;
- }
- if (!strcmp(arg, "--stateless-rpc")) {
- stateless_rpc = 1;
- continue;
- }
- if (!strcmp(arg, "--reject-thin-pack-for-testing")) {
- fix_thin = 0;
- continue;
- }
+ if (argc > 1)
+ usage_msg_opt(_("Too many arguments."), receive_pack_usage, options);
+ if (argc == 0)
+ usage_msg_opt(_("You must specify a directory."), receive_pack_usage, options);
- usage(receive_pack_usage);
- }
- if (service_dir)
- usage(receive_pack_usage);
- service_dir = arg;
- }
- if (!service_dir)
- usage(receive_pack_usage);
+ service_dir = argv[0];
setup_path();
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 2d46b6482a..7a7136e53e 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -84,8 +84,8 @@ static int tree_is_complete(const unsigned char *sha1)
init_tree_desc(&desc, tree->buffer, tree->size);
complete = 1;
while (tree_entry(&desc, &entry)) {
- if (!has_sha1_file(entry.sha1) ||
- (S_ISDIR(entry.mode) && !tree_is_complete(entry.sha1))) {
+ if (!has_sha1_file(entry.oid->hash) ||
+ (S_ISDIR(entry.mode) && !tree_is_complete(entry.oid->hash))) {
tree->object.flags |= INCOMPLETE;
complete = 0;
}
diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c
index 7457c743e8..88eb8f9013 100644
--- a/builtin/remote-ext.c
+++ b/builtin/remote-ext.c
@@ -168,7 +168,7 @@ static int command_loop(const char *child)
size_t i;
if (!fgets(buffer, MAXCOMMAND - 1, stdin)) {
if (ferror(stdin))
- die("Comammand input error");
+ die("Command input error");
exit(0);
}
/* Strip end of line characters. */
diff --git a/builtin/remote.c b/builtin/remote.c
index fda5c2e53d..d33766be39 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -1154,6 +1154,8 @@ static int show(int argc, const char **argv)
url_nr = states.remote->url_nr;
}
for (i = 0; i < url_nr; i++)
+ /* TRANSLATORS: the colon ':' should align with
+ the one in " Fetch URL: %s" translation */
printf_ln(_(" Push URL: %s"), url[i]);
if (!i)
printf_ln(_(" Push URL: %s"), "(no URL)");
diff --git a/builtin/rm.c b/builtin/rm.c
index 8829b09d0b..8abb0207fa 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -152,7 +152,7 @@ static int check_local_mod(unsigned char *head, int index_only)
if (lstat(ce->name, &st) < 0) {
if (errno != ENOENT && errno != ENOTDIR)
- warning("'%s': %s", ce->name, strerror(errno));
+ warning_errno(_("failed to stat '%s'"), ce->name);
/* It already vanished from the working tree */
continue;
}
@@ -314,7 +314,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);
if (list.entry[list.nr++].is_submodule &&
!is_staging_gitmodules_ok())
- die (_("Please, stage your changes to .gitmodules or stash them to proceed"));
+ die (_("Please stage your changes to .gitmodules or stash them to proceed"));
}
if (pathspec.nr) {
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 3bea3aaa50..8da263f0b0 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -9,6 +9,211 @@
#include "submodule-config.h"
#include "string-list.h"
#include "run-command.h"
+#include "remote.h"
+#include "refs.h"
+#include "connect.h"
+
+static char *get_default_remote(void)
+{
+ char *dest = NULL, *ret;
+ unsigned char sha1[20];
+ struct strbuf sb = STRBUF_INIT;
+ const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
+
+ if (!refname)
+ die(_("No such ref: %s"), "HEAD");
+
+ /* detached HEAD */
+ if (!strcmp(refname, "HEAD"))
+ return xstrdup("origin");
+
+ if (!skip_prefix(refname, "refs/heads/", &refname))
+ die(_("Expecting a full ref name, got %s"), refname);
+
+ strbuf_addf(&sb, "branch.%s.remote", refname);
+ if (git_config_get_string(sb.buf, &dest))
+ ret = xstrdup("origin");
+ else
+ ret = dest;
+
+ strbuf_release(&sb);
+ return ret;
+}
+
+static int starts_with_dot_slash(const char *str)
+{
+ return str[0] == '.' && is_dir_sep(str[1]);
+}
+
+static int starts_with_dot_dot_slash(const char *str)
+{
+ return str[0] == '.' && str[1] == '.' && is_dir_sep(str[2]);
+}
+
+/*
+ * Returns 1 if it was the last chop before ':'.
+ */
+static int chop_last_dir(char **remoteurl, int is_relative)
+{
+ char *rfind = find_last_dir_sep(*remoteurl);
+ if (rfind) {
+ *rfind = '\0';
+ return 0;
+ }
+
+ rfind = strrchr(*remoteurl, ':');
+ if (rfind) {
+ *rfind = '\0';
+ return 1;
+ }
+
+ if (is_relative || !strcmp(".", *remoteurl))
+ die(_("cannot strip one component off url '%s'"),
+ *remoteurl);
+
+ free(*remoteurl);
+ *remoteurl = xstrdup(".");
+ return 0;
+}
+
+/*
+ * The `url` argument is the URL that navigates to the submodule origin
+ * repo. When relative, this URL is relative to the superproject origin
+ * URL repo. The `up_path` argument, if specified, is the relative
+ * path that navigates from the submodule working tree to the superproject
+ * working tree. Returns the origin URL of the submodule.
+ *
+ * Return either an absolute URL or filesystem path (if the superproject
+ * origin URL is an absolute URL or filesystem path, respectively) or a
+ * relative file system path (if the superproject origin URL is a relative
+ * file system path).
+ *
+ * When the output is a relative file system path, the path is either
+ * relative to the submodule working tree, if up_path is specified, or to
+ * the superproject working tree otherwise.
+ *
+ * NEEDSWORK: This works incorrectly on the domain and protocol part.
+ * remote_url url outcome expectation
+ * http://a.com/b ../c http://a.com/c as is
+ * http://a.com/b ../../c http://c error out
+ * http://a.com/b ../../../c http:/c error out
+ * http://a.com/b ../../../../c http:c error out
+ * http://a.com/b ../../../../../c .:c error out
+ * NEEDSWORK: Given how chop_last_dir() works, this function is broken
+ * when a local part has a colon in its path component, too.
+ */
+static char *relative_url(const char *remote_url,
+ const char *url,
+ const char *up_path)
+{
+ int is_relative = 0;
+ int colonsep = 0;
+ char *out;
+ char *remoteurl = xstrdup(remote_url);
+ struct strbuf sb = STRBUF_INIT;
+ size_t len = strlen(remoteurl);
+
+ if (is_dir_sep(remoteurl[len]))
+ remoteurl[len] = '\0';
+
+ if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
+ is_relative = 0;
+ else {
+ is_relative = 1;
+ /*
+ * Prepend a './' to ensure all relative
+ * remoteurls start with './' or '../'
+ */
+ if (!starts_with_dot_slash(remoteurl) &&
+ !starts_with_dot_dot_slash(remoteurl)) {
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "./%s", remoteurl);
+ free(remoteurl);
+ remoteurl = strbuf_detach(&sb, NULL);
+ }
+ }
+ /*
+ * When the url starts with '../', remove that and the
+ * last directory in remoteurl.
+ */
+ while (url) {
+ if (starts_with_dot_dot_slash(url)) {
+ url += 3;
+ colonsep |= chop_last_dir(&remoteurl, is_relative);
+ } else if (starts_with_dot_slash(url))
+ url += 2;
+ else
+ break;
+ }
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url);
+ free(remoteurl);
+
+ if (starts_with_dot_slash(sb.buf))
+ out = xstrdup(sb.buf + 2);
+ else
+ out = xstrdup(sb.buf);
+ strbuf_reset(&sb);
+
+ if (!up_path || !is_relative)
+ return out;
+
+ strbuf_addf(&sb, "%s%s", up_path, out);
+ free(out);
+ return strbuf_detach(&sb, NULL);
+}
+
+static int resolve_relative_url(int argc, const char **argv, const char *prefix)
+{
+ char *remoteurl = NULL;
+ char *remote = get_default_remote();
+ const char *up_path = NULL;
+ char *res;
+ const char *url;
+ struct strbuf sb = STRBUF_INIT;
+
+ if (argc != 2 && argc != 3)
+ die("resolve-relative-url only accepts one or two arguments");
+
+ url = argv[1];
+ strbuf_addf(&sb, "remote.%s.url", remote);
+ free(remote);
+
+ if (git_config_get_string(sb.buf, &remoteurl))
+ /* the repository is its own authoritative upstream */
+ remoteurl = xgetcwd();
+
+ if (argc == 3)
+ up_path = argv[2];
+
+ res = relative_url(remoteurl, url, up_path);
+ puts(res);
+ free(res);
+ free(remoteurl);
+ return 0;
+}
+
+static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)
+{
+ char *remoteurl, *res;
+ const char *up_path, *url;
+
+ if (argc != 4)
+ die("resolve-relative-url-test only accepts three arguments: <up_path> <remoteurl> <url>");
+
+ up_path = argv[1];
+ remoteurl = xstrdup(argv[2]);
+ url = argv[3];
+
+ if (!strcmp(up_path, "(null)"))
+ up_path = NULL;
+
+ res = relative_url(remoteurl, url, up_path);
+ puts(res);
+ free(res);
+ free(remoteurl);
+ return 0;
+}
struct module_list {
const struct cache_entry **entries;
@@ -100,6 +305,125 @@ static int module_list(int argc, const char **argv, const char *prefix)
return 0;
}
+static void init_submodule(const char *path, const char *prefix, int quiet)
+{
+ const struct submodule *sub;
+ struct strbuf sb = STRBUF_INIT;
+ char *upd = NULL, *url = NULL, *displaypath;
+
+ /* Only loads from .gitmodules, no overlay with .git/config */
+ gitmodules_config();
+
+ if (prefix) {
+ strbuf_addf(&sb, "%s%s", prefix, path);
+ displaypath = strbuf_detach(&sb, NULL);
+ } else
+ displaypath = xstrdup(path);
+
+ sub = submodule_from_path(null_sha1, path);
+
+ if (!sub)
+ die(_("No url found for submodule path '%s' in .gitmodules"),
+ displaypath);
+
+ /*
+ * Copy url setting when it is not set yet.
+ * To look up the url in .git/config, we must not fall back to
+ * .gitmodules, so look it up directly.
+ */
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "submodule.%s.url", sub->name);
+ if (git_config_get_string(sb.buf, &url)) {
+ url = xstrdup(sub->url);
+
+ if (!url)
+ die(_("No url found for submodule path '%s' in .gitmodules"),
+ displaypath);
+
+ /* Possibly a url relative to parent */
+ if (starts_with_dot_dot_slash(url) ||
+ starts_with_dot_slash(url)) {
+ char *remoteurl, *relurl;
+ char *remote = get_default_remote();
+ struct strbuf remotesb = STRBUF_INIT;
+ strbuf_addf(&remotesb, "remote.%s.url", remote);
+ free(remote);
+
+ if (git_config_get_string(remotesb.buf, &remoteurl))
+ /*
+ * The repository is its own
+ * authoritative upstream
+ */
+ remoteurl = xgetcwd();
+ relurl = relative_url(remoteurl, url, NULL);
+ strbuf_release(&remotesb);
+ free(remoteurl);
+ free(url);
+ url = relurl;
+ }
+
+ if (git_config_set_gently(sb.buf, url))
+ die(_("Failed to register url for submodule path '%s'"),
+ displaypath);
+ if (!quiet)
+ fprintf(stderr,
+ _("Submodule '%s' (%s) registered for path '%s'\n"),
+ sub->name, url, displaypath);
+ }
+
+ /* Copy "update" setting when it is not set yet */
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "submodule.%s.update", sub->name);
+ if (git_config_get_string(sb.buf, &upd) &&
+ sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
+ if (sub->update_strategy.type == SM_UPDATE_COMMAND) {
+ fprintf(stderr, _("warning: command update mode suggested for submodule '%s'\n"),
+ sub->name);
+ upd = xstrdup("none");
+ } else
+ upd = xstrdup(submodule_strategy_to_string(&sub->update_strategy));
+
+ if (git_config_set_gently(sb.buf, upd))
+ die(_("Failed to register update mode for submodule path '%s'"), displaypath);
+ }
+ strbuf_release(&sb);
+ free(displaypath);
+ free(url);
+ free(upd);
+}
+
+static int module_init(int argc, const char **argv, const char *prefix)
+{
+ struct pathspec pathspec;
+ struct module_list list = MODULE_LIST_INIT;
+ int quiet = 0;
+ int i;
+
+ struct option module_init_options[] = {
+ OPT_STRING(0, "prefix", &prefix,
+ N_("path"),
+ N_("alternative anchor for relative paths")),
+ OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule--helper init [<path>]"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, module_init_options,
+ git_submodule_helper_usage, 0);
+
+ if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+ return 1;
+
+ for (i = 0; i < list.nr; i++)
+ init_submodule(list.entries[i]->name, prefix, quiet);
+
+ return 0;
+}
+
static int module_name(int argc, const char **argv, const char *prefix)
{
const struct submodule *sub;
@@ -118,6 +442,7 @@ static int module_name(int argc, const char **argv, const char *prefix)
return 0;
}
+
static int clone_submodule(const char *path, const char *gitdir, const char *url,
const char *depth, const char *reference, int quiet)
{
@@ -139,7 +464,7 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url
argv_array_push(&cp.args, path);
cp.git_cmd = 1;
- cp.env = local_repo_env;
+ prepare_submodule_repo_env(&cp.env_array);
cp.no_stdin = 1;
return run_command(&cp);
@@ -180,16 +505,17 @@ static int module_clone(int argc, const char **argv, const char *prefix)
const char *const git_submodule_helper_usage[] = {
N_("git submodule--helper clone [--prefix=<path>] [--quiet] "
- "[--reference <repository>] [--name <name>] [--url <url>]"
- "[--depth <depth>] [--] [<path>...]"),
+ "[--reference <repository>] [--name <name>] [--depth <depth>] "
+ "--url <url> --path <path>"),
NULL
};
argc = parse_options(argc, argv, prefix, module_clone_options,
git_submodule_helper_usage, 0);
- if (!path || !*path)
- die(_("submodule--helper: unspecified or empty --path"));
+ if (argc || !url || !path || !*path)
+ usage_with_options(git_submodule_helper_usage,
+ module_clone_options);
strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name);
sm_gitdir = xstrdup(absolute_path(sb.buf));
@@ -244,6 +570,269 @@ static int module_clone(int argc, const char **argv, const char *prefix)
return 0;
}
+struct submodule_update_clone {
+ /* index into 'list', the list of submodules to look into for cloning */
+ int current;
+ struct module_list list;
+ unsigned warn_if_uninitialized : 1;
+
+ /* update parameter passed via commandline */
+ struct submodule_update_strategy update;
+
+ /* configuration parameters which are passed on to the children */
+ int quiet;
+ const char *reference;
+ const char *depth;
+ const char *recursive_prefix;
+ const char *prefix;
+
+ /* Machine-readable status lines to be consumed by git-submodule.sh */
+ struct string_list projectlines;
+
+ /* If we want to stop as fast as possible and return an error */
+ unsigned quickstop : 1;
+};
+#define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
+ SUBMODULE_UPDATE_STRATEGY_INIT, 0, NULL, NULL, NULL, NULL, \
+ STRING_LIST_INIT_DUP, 0}
+
+
+static void next_submodule_warn_missing(struct submodule_update_clone *suc,
+ struct strbuf *out, const char *displaypath)
+{
+ /*
+ * Only mention uninitialized submodules when their
+ * paths have been specified.
+ */
+ if (suc->warn_if_uninitialized) {
+ strbuf_addf(out,
+ _("Submodule path '%s' not initialized"),
+ displaypath);
+ strbuf_addch(out, '\n');
+ strbuf_addstr(out,
+ _("Maybe you want to use 'update --init'?"));
+ strbuf_addch(out, '\n');
+ }
+}
+
+/**
+ * Determine whether 'ce' needs to be cloned. If so, prepare the 'child' to
+ * run the clone. Returns 1 if 'ce' needs to be cloned, 0 otherwise.
+ */
+static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
+ struct child_process *child,
+ struct submodule_update_clone *suc,
+ struct strbuf *out)
+{
+ const struct submodule *sub = NULL;
+ struct strbuf displaypath_sb = STRBUF_INIT;
+ struct strbuf sb = STRBUF_INIT;
+ const char *displaypath = NULL;
+ char *url = NULL;
+ int needs_cloning = 0;
+
+ if (ce_stage(ce)) {
+ if (suc->recursive_prefix)
+ strbuf_addf(&sb, "%s/%s", suc->recursive_prefix, ce->name);
+ else
+ strbuf_addf(&sb, "%s", ce->name);
+ strbuf_addf(out, _("Skipping unmerged submodule %s"), sb.buf);
+ strbuf_addch(out, '\n');
+ goto cleanup;
+ }
+
+ sub = submodule_from_path(null_sha1, ce->name);
+
+ if (suc->recursive_prefix)
+ displaypath = relative_path(suc->recursive_prefix,
+ ce->name, &displaypath_sb);
+ else
+ displaypath = ce->name;
+
+ if (!sub) {
+ next_submodule_warn_missing(suc, out, displaypath);
+ goto cleanup;
+ }
+
+ if (suc->update.type == SM_UPDATE_NONE
+ || (suc->update.type == SM_UPDATE_UNSPECIFIED
+ && sub->update_strategy.type == SM_UPDATE_NONE)) {
+ strbuf_addf(out, _("Skipping submodule '%s'"), displaypath);
+ strbuf_addch(out, '\n');
+ goto cleanup;
+ }
+
+ /*
+ * Looking up the url in .git/config.
+ * We must not fall back to .gitmodules as we only want
+ * to process configured submodules.
+ */
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "submodule.%s.url", sub->name);
+ git_config_get_string(sb.buf, &url);
+ if (!url) {
+ next_submodule_warn_missing(suc, out, displaypath);
+ goto cleanup;
+ }
+
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s/.git", ce->name);
+ needs_cloning = !file_exists(sb.buf);
+
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%06o %s %d %d\t%s\n", ce->ce_mode,
+ sha1_to_hex(ce->sha1), ce_stage(ce),
+ needs_cloning, ce->name);
+ string_list_append(&suc->projectlines, sb.buf);
+
+ if (!needs_cloning)
+ goto cleanup;
+
+ child->git_cmd = 1;
+ child->no_stdin = 1;
+ child->stdout_to_stderr = 1;
+ child->err = -1;
+ argv_array_push(&child->args, "submodule--helper");
+ argv_array_push(&child->args, "clone");
+ if (suc->quiet)
+ argv_array_push(&child->args, "--quiet");
+ if (suc->prefix)
+ argv_array_pushl(&child->args, "--prefix", suc->prefix, NULL);
+ argv_array_pushl(&child->args, "--path", sub->path, NULL);
+ argv_array_pushl(&child->args, "--name", sub->name, NULL);
+ argv_array_pushl(&child->args, "--url", url, NULL);
+ if (suc->reference)
+ argv_array_push(&child->args, suc->reference);
+ if (suc->depth)
+ argv_array_push(&child->args, suc->depth);
+
+cleanup:
+ free(url);
+ strbuf_reset(&displaypath_sb);
+ strbuf_reset(&sb);
+
+ return needs_cloning;
+}
+
+static int update_clone_get_next_task(struct child_process *child,
+ struct strbuf *err,
+ void *suc_cb,
+ void **void_task_cb)
+{
+ struct submodule_update_clone *suc = suc_cb;
+
+ for (; suc->current < suc->list.nr; suc->current++) {
+ const struct cache_entry *ce = suc->list.entries[suc->current];
+ if (prepare_to_clone_next_submodule(ce, child, suc, err)) {
+ suc->current++;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int update_clone_start_failure(struct strbuf *err,
+ void *suc_cb,
+ void *void_task_cb)
+{
+ struct submodule_update_clone *suc = suc_cb;
+ suc->quickstop = 1;
+ return 1;
+}
+
+static int update_clone_task_finished(int result,
+ struct strbuf *err,
+ void *suc_cb,
+ void *void_task_cb)
+{
+ struct submodule_update_clone *suc = suc_cb;
+
+ if (!result)
+ return 0;
+
+ suc->quickstop = 1;
+ return 1;
+}
+
+static int update_clone(int argc, const char **argv, const char *prefix)
+{
+ const char *update = NULL;
+ int max_jobs = -1;
+ struct string_list_item *item;
+ struct pathspec pathspec;
+ struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
+
+ struct option module_update_clone_options[] = {
+ OPT_STRING(0, "prefix", &prefix,
+ N_("path"),
+ N_("path into the working tree")),
+ OPT_STRING(0, "recursive-prefix", &suc.recursive_prefix,
+ N_("path"),
+ N_("path into the working tree, across nested "
+ "submodule boundaries")),
+ OPT_STRING(0, "update", &update,
+ N_("string"),
+ N_("rebase, merge, checkout or none")),
+ OPT_STRING(0, "reference", &suc.reference, N_("repo"),
+ N_("reference repository")),
+ OPT_STRING(0, "depth", &suc.depth, "<depth>",
+ N_("Create a shallow clone truncated to the "
+ "specified number of revisions")),
+ OPT_INTEGER('j', "jobs", &max_jobs,
+ N_("parallel jobs")),
+ OPT__QUIET(&suc.quiet, N_("don't print cloning progress")),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule--helper update_clone [--prefix=<path>] [<path>...]"),
+ NULL
+ };
+ suc.prefix = prefix;
+
+ argc = parse_options(argc, argv, prefix, module_update_clone_options,
+ git_submodule_helper_usage, 0);
+
+ if (update)
+ if (parse_submodule_update_strategy(update, &suc.update) < 0)
+ die(_("bad value for update parameter"));
+
+ if (module_list_compute(argc, argv, prefix, &pathspec, &suc.list) < 0)
+ return 1;
+
+ if (pathspec.nr)
+ suc.warn_if_uninitialized = 1;
+
+ /* Overlay the parsed .gitmodules file with .git/config */
+ gitmodules_config();
+ git_config(submodule_config, NULL);
+
+ if (max_jobs < 0)
+ max_jobs = parallel_submodules();
+
+ run_processes_parallel(max_jobs,
+ update_clone_get_next_task,
+ update_clone_start_failure,
+ update_clone_task_finished,
+ &suc);
+
+ /*
+ * We saved the output and put it out all at once now.
+ * That means:
+ * - the listener does not have to interleave their (checkout)
+ * work with our fetching. The writes involved in a
+ * checkout involve more straightforward sequential I/O.
+ * - the listener can avoid doing any work if fetching failed.
+ */
+ if (suc.quickstop)
+ return 1;
+
+ for_each_string_list_item(item, &suc.projectlines)
+ utf8_fprintf(stdout, "%s", item->string);
+
+ return 0;
+}
+
struct cmd_struct {
const char *cmd;
int (*fn)(int, const char **, const char *);
@@ -253,19 +842,23 @@ static struct cmd_struct commands[] = {
{"list", module_list},
{"name", module_name},
{"clone", module_clone},
+ {"update-clone", update_clone},
+ {"resolve-relative-url", resolve_relative_url},
+ {"resolve-relative-url-test", resolve_relative_url_test},
+ {"init", module_init}
};
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
{
int i;
if (argc < 2)
- die(_("fatal: submodule--helper subcommand must be "
+ die(_("submodule--helper subcommand must be "
"called with a subcommand"));
for (i = 0; i < ARRAY_SIZE(commands); i++)
if (!strcmp(argv[1], commands[i].cmd))
return commands[i].fn(argc - 1, argv + 1, prefix);
- die(_("fatal: '%s' is not a valid submodule--helper "
+ die(_("'%s' is not a valid submodule--helper "
"subcommand"), argv[1]);
}
diff --git a/builtin/tag.c b/builtin/tag.c
index 1705c94665..50e4ae5678 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -29,6 +29,7 @@ static const char * const git_tag_usage[] = {
};
static unsigned int colopts;
+static int force_sign_annotate;
static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
{
@@ -104,13 +105,7 @@ static int delete_tag(const char *name, const char *ref,
static int verify_tag(const char *name, const char *ref,
const unsigned char *sha1)
{
- const char *argv_verify_tag[] = {"verify-tag",
- "-v", "SHA1_HEX", NULL};
- argv_verify_tag[2] = sha1_to_hex(sha1);
-
- if (run_command_v_opt(argv_verify_tag, RUN_GIT_CMD))
- return error(_("could not verify the tag '%s'"), name);
- return 0;
+ return gpg_verify_tag(sha1, name, GPG_VERIFY_VERBOSE);
}
static int do_sign(struct strbuf *buffer)
@@ -166,6 +161,11 @@ static int git_tag_config(const char *var, const char *value, void *cb)
status = git_gpg_config(var, value, cb);
if (status)
return status;
+ if (!strcmp(var, "tag.forcesignannotated")) {
+ force_sign_annotate = git_config_bool(var, value);
+ return 0;
+ }
+
if (starts_with(var, "column."))
return git_column_config(var, value, "tag", &colopts);
return git_default_config(var, value, cb);
@@ -327,7 +327,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
char *cleanup_arg = NULL;
int create_reflog = 0;
int annotate = 0, force = 0;
- int cmdmode = 0;
+ int cmdmode = 0, create_tag_object = 0;
const char *msgfile = NULL, *keyid = NULL;
struct msg_arg msg = { 0, STRBUF_INIT };
struct ref_transaction *transaction;
@@ -385,12 +385,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
opt.sign = 1;
set_signing_key(keyid);
}
- if (opt.sign)
- annotate = 1;
+ create_tag_object = (opt.sign || annotate || msg.given || msgfile);
+
if (argc == 0 && !cmdmode)
cmdmode = 'l';
- if ((annotate || msg.given || msgfile || force) && (cmdmode != 0))
+ if ((create_tag_object || force) && (cmdmode != 0))
usage_with_options(git_tag_usage, options);
finalize_colopts(&colopts, -1);
@@ -431,7 +431,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (msg.given || msgfile) {
if (msg.given && msgfile)
die(_("only one -F or -m option is allowed."));
- annotate = 1;
if (msg.given)
strbuf_addbuf(&buf, &(msg.buf));
else {
@@ -474,8 +473,11 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
else
die(_("Invalid cleanup mode %s"), cleanup_arg);
- if (annotate)
+ if (create_tag_object) {
+ if (force_sign_annotate && !annotate)
+ opt.sign = 1;
create_tag(object, tag, &buf, &opt, prev, object);
+ }
transaction = ref_transaction_begin(&err);
if (!transaction ||
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 1c94ca59bf..b8b8522249 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -255,7 +255,7 @@ static int process_lstat_error(const char *path, int err)
{
if (err == ENOENT || err == ENOTDIR)
return remove_one_path(path);
- return error("lstat(\"%s\"): %s", path, strerror(errno));
+ return error("lstat(\"%s\"): %s", path, strerror(err));
}
static int add_one_path(const struct cache_entry *old, const char *path, int len, struct stat *st)
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index dbfe14f3fe..2caedf1849 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -104,8 +104,7 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
pfd[1].events = POLLIN;
if (poll(pfd, 2, -1) < 0) {
if (errno != EINTR) {
- error("poll failed resuming: %s",
- strerror(errno));
+ error_errno("poll failed resuming");
sleep(1);
}
continue;
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index 00663f6a30..99f8148cf7 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -18,55 +18,6 @@ static const char * const verify_tag_usage[] = {
NULL
};
-static int run_gpg_verify(const char *buf, unsigned long size, unsigned flags)
-{
- struct signature_check sigc;
- int len;
- int ret;
-
- memset(&sigc, 0, sizeof(sigc));
-
- len = parse_signature(buf, size);
-
- if (size == len) {
- if (flags & GPG_VERIFY_VERBOSE)
- write_in_full(1, buf, len);
- return error("no signature found");
- }
-
- ret = check_signature(buf, len, buf + len, size - len, &sigc);
- print_signature_buffer(&sigc, flags);
-
- signature_check_clear(&sigc);
- return ret;
-}
-
-static int verify_tag(const char *name, unsigned flags)
-{
- enum object_type type;
- unsigned char sha1[20];
- char *buf;
- unsigned long size;
- int ret;
-
- if (get_sha1(name, sha1))
- return error("tag '%s' not found.", name);
-
- type = sha1_object_info(sha1, NULL);
- if (type != OBJ_TAG)
- return error("%s: cannot verify a non-tag object of type %s.",
- name, typename(type));
-
- buf = read_sha1_file(sha1, &type, &size);
- if (!buf)
- return error("%s: unable to read file.", name);
-
- ret = run_gpg_verify(buf, size, flags);
-
- free(buf);
- return ret;
-}
-
static int git_verify_tag_config(const char *var, const char *value, void *cb)
{
int status = git_gpg_config(var, value, cb);
@@ -95,11 +46,13 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
if (verbose)
flags |= GPG_VERIFY_VERBOSE;
- /* sometimes the program was terminated because this signal
- * was received in the process of writing the gpg input: */
- signal(SIGPIPE, SIG_IGN);
- while (i < argc)
- if (verify_tag(argv[i++], flags))
+ while (i < argc) {
+ unsigned char sha1[20];
+ const char *name = argv[i++];
+ if (get_sha1(name, sha1))
+ had_error = !!error("tag '%s' not found.", name);
+ else if (gpg_verify_tag(sha1, name, flags))
had_error = 1;
+ }
return had_error;
}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 38b56096bd..96a2834a18 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -21,6 +21,7 @@ static const char * const worktree_usage[] = {
struct add_opts {
int force;
int detach;
+ int checkout;
const char *new_branch;
int force_new_branch;
};
@@ -109,7 +110,7 @@ static void prune_worktrees(void)
if (ret < 0 && errno == ENOTDIR)
ret = unlink(path.buf);
if (ret)
- error(_("failed to remove: %s"), strerror(errno));
+ error_errno(_("failed to remove '%s'"), path.buf);
}
closedir(dir);
if (!show_only)
@@ -204,7 +205,7 @@ static int add_worktree(const char *path, const char *refname,
if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
ref_exists(symref.buf)) { /* it's a branch */
if (!opts->force)
- die_if_checked_out(symref.buf);
+ die_if_checked_out(symref.buf, 0);
} else { /* must be a commit */
commit = lookup_commit_reference_by_name(refname);
if (!commit)
@@ -284,18 +285,22 @@ static int add_worktree(const char *path, const char *refname,
if (ret)
goto done;
- cp.argv = NULL;
- argv_array_clear(&cp.args);
- argv_array_pushl(&cp.args, "reset", "--hard", NULL);
- cp.env = child_env.argv;
- ret = run_command(&cp);
- if (!ret) {
- is_junk = 0;
- free(junk_work_tree);
- free(junk_git_dir);
- junk_work_tree = NULL;
- junk_git_dir = NULL;
+ if (opts->checkout) {
+ cp.argv = NULL;
+ argv_array_clear(&cp.args);
+ argv_array_pushl(&cp.args, "reset", "--hard", NULL);
+ cp.env = child_env.argv;
+ ret = run_command(&cp);
+ if (ret)
+ goto done;
}
+
+ is_junk = 0;
+ free(junk_work_tree);
+ free(junk_git_dir);
+ junk_work_tree = NULL;
+ junk_git_dir = NULL;
+
done:
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/locked", sb_repo.buf);
@@ -320,10 +325,12 @@ static int add(int ac, const char **av, const char *prefix)
OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
N_("create or reset a branch")),
OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
+ OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
OPT_END()
};
memset(&opts, 0, sizeof(opts));
+ opts.checkout = 1;
ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
if (!!opts.detach + !!opts.new_branch + !!new_branch_force > 1)
die(_("-b, -B, and --detach are mutually exclusive"));
@@ -342,7 +349,7 @@ static int add(int ac, const char **av, const char *prefix)
if (!opts.force &&
!strbuf_check_branch_ref(&symref, opts.new_branch) &&
ref_exists(symref.buf))
- die_if_checked_out(symref.buf);
+ die_if_checked_out(symref.buf, 0);
strbuf_release(&symref);
}
diff --git a/bundle.c b/bundle.c
index 506ac49691..bbf4efa0a0 100644
--- a/bundle.c
+++ b/bundle.c
@@ -435,12 +435,14 @@ int create_bundle(struct bundle_header *header, const char *path,
/* write prerequisites */
if (compute_and_write_prerequisites(bundle_fd, &revs, argc, argv))
- return -1;
+ goto err;
argc = setup_revisions(argc, argv, &revs, NULL);
- if (argc > 1)
- return error(_("unrecognized argument: %s"), argv[1]);
+ if (argc > 1) {
+ error(_("unrecognized argument: %s"), argv[1]);
+ goto err;
+ }
object_array_remove_duplicates(&revs.pending);
@@ -448,17 +450,26 @@ int create_bundle(struct bundle_header *header, const char *path,
if (!ref_count)
die(_("Refusing to create empty bundle."));
else if (ref_count < 0)
- return -1;
+ goto err;
/* write pack */
- if (write_pack_data(bundle_fd, &revs))
- return -1;
+ if (write_pack_data(bundle_fd, &revs)) {
+ bundle_fd = -1; /* already closed by the above call */
+ goto err;
+ }
if (!bundle_to_stdout) {
if (commit_lock_file(&lock))
die_errno(_("cannot create '%s'"), path);
}
return 0;
+err:
+ if (!bundle_to_stdout) {
+ if (0 <= bundle_fd)
+ close(bundle_fd);
+ rollback_lock_file(&lock);
+ }
+ return -1;
}
int unbundle(struct bundle_header *header, int bundle_fd, int flags)
diff --git a/cache-tree.c b/cache-tree.c
index 3ebf9c3aa4..ddf0cc9f9a 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -663,7 +663,7 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
cnt++;
else {
struct cache_tree_sub *sub;
- struct tree *subtree = lookup_tree(entry.sha1);
+ struct tree *subtree = lookup_tree(entry.oid->hash);
if (!subtree->object.parsed)
parse_tree(subtree);
sub = cache_tree_sub(it, entry.path);
@@ -710,7 +710,7 @@ int cache_tree_matches_traversal(struct cache_tree *root,
it = find_cache_tree_from_traversal(root, info);
it = cache_tree_find(it, ent->path);
- if (it && it->entry_count > 0 && !hashcmp(ent->sha1, it->sha1))
+ if (it && it->entry_count > 0 && !hashcmp(ent->oid->hash, it->sha1))
return it->entry_count;
return 0;
}
diff --git a/cache.h b/cache.h
index 2711048cad..6049f86711 100644
--- a/cache.h
+++ b/cache.h
@@ -654,6 +654,7 @@ extern int warn_on_object_refname_ambiguity;
extern const char *apply_default_whitespace;
extern const char *apply_default_ignorewhitespace;
extern const char *git_attributes_file;
+extern const char *git_hooks_path;
extern int zlib_compression_level;
extern int core_compression_level;
extern int core_compression_seen;
@@ -700,6 +701,14 @@ extern int ref_paranoia;
extern char comment_line_char;
extern int auto_comment_line_char;
+/* Windows only */
+enum hide_dotfiles_type {
+ HIDE_DOTFILES_FALSE = 0,
+ HIDE_DOTFILES_TRUE,
+ HIDE_DOTFILES_DOTGITONLY
+};
+extern enum hide_dotfiles_type hide_dotfiles;
+
enum branch_track {
BRANCH_TRACK_UNSPECIFIED = -1,
BRANCH_TRACK_NEVER = 0,
@@ -799,11 +808,14 @@ extern void check_repository_format(void);
*/
extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
__attribute__((format (printf, 3, 4)));
extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
__attribute__((format (printf, 2, 3)));
+extern void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+ __attribute__((format (printf, 2, 3)));
extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
__attribute__((format (printf, 2, 3)));
extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
@@ -958,8 +970,6 @@ static inline int is_empty_blob_sha1(const unsigned char *sha1)
int git_mkstemp(char *path, size_t n, const char *template);
-int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);
-
/* set default permissions by passing mode arguments to open(2) */
int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
int git_mkstemp_mode(char *pattern, int mode);
@@ -1156,6 +1166,8 @@ extern int get_sha1_blob(const char *str, unsigned char *sha1);
extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc);
+extern int get_oid(const char *str, struct object_id *oid);
+
typedef int each_abbrev_fn(const unsigned char *sha1, void *);
extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
@@ -1766,8 +1778,8 @@ int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int
extern int diff_auto_refresh_index;
/* match-trees.c */
-void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
-void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *, const char *);
+void shift_tree(const struct object_id *, const struct object_id *, struct object_id *, int);
+void shift_tree_by(const struct object_id *, const struct object_id *, struct object_id *, const char *);
/*
* whitespace rules.
diff --git a/check-racy.c b/check-racy.c
index 00d92a1663..24b6542352 100644
--- a/check-racy.c
+++ b/check-racy.c
@@ -12,7 +12,7 @@ int main(int ac, char **av)
struct stat st;
if (lstat(ce->name, &st)) {
- error("lstat(%s): %s", ce->name, strerror(errno));
+ error_errno("lstat(%s)", ce->name);
continue;
}
diff --git a/ci/test-documentation.sh b/ci/test-documentation.sh
new file mode 100755
index 0000000000..579d540d32
--- /dev/null
+++ b/ci/test-documentation.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# Perform sanity checks on documentation and build it.
+#
+
+set -e
+
+make check-builtins
+make check-docs
+make doc
+
+test -s Documentation/git.html
+test -s Documentation/git.xml
+test -s Documentation/git.1
diff --git a/combine-diff.c b/combine-diff.c
index 0e1d4b0893..8f2313d502 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -1005,8 +1005,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
struct strbuf buf = STRBUF_INIT;
if (strbuf_readlink(&buf, elem->path, st.st_size) < 0) {
- error("readlink(%s): %s", elem->path,
- strerror(errno));
+ error_errno("readlink(%s)", elem->path);
return;
}
result_size = buf.len;
diff --git a/commit.h b/commit.h
index 5d58be0017..b06db4d5d9 100644
--- a/commit.h
+++ b/commit.h
@@ -147,6 +147,7 @@ struct pretty_print_context {
int preserve_subject;
struct date_mode date_mode;
unsigned date_mode_explicit:1;
+ int expand_tabs_in_log;
int need_8bit_cte;
char *notes_message;
struct reflog_walk_info *reflog_info;
diff --git a/compat/mingw.c b/compat/mingw.c
index 0413d5c3cd..a8218e6f0f 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -286,6 +286,49 @@ int mingw_rmdir(const char *pathname)
return ret;
}
+static inline int needs_hiding(const char *path)
+{
+ const char *basename;
+
+ if (hide_dotfiles == HIDE_DOTFILES_FALSE)
+ return 0;
+
+ /* We cannot use basename(), as it would remove trailing slashes */
+ mingw_skip_dos_drive_prefix((char **)&path);
+ if (!*path)
+ return 0;
+
+ for (basename = path; *path; path++)
+ if (is_dir_sep(*path)) {
+ do {
+ path++;
+ } while (is_dir_sep(*path));
+ /* ignore trailing slashes */
+ if (*path)
+ basename = path;
+ }
+
+ if (hide_dotfiles == HIDE_DOTFILES_TRUE)
+ return *basename == '.';
+
+ assert(hide_dotfiles == HIDE_DOTFILES_DOTGITONLY);
+ return !strncasecmp(".git", basename, 4) &&
+ (!basename[4] || is_dir_sep(basename[4]));
+}
+
+static int set_hidden_flag(const wchar_t *path, int set)
+{
+ DWORD original = GetFileAttributesW(path), modified;
+ if (set)
+ modified = original | FILE_ATTRIBUTE_HIDDEN;
+ else
+ modified = original & ~FILE_ATTRIBUTE_HIDDEN;
+ if (original == modified || SetFileAttributesW(path, modified))
+ return 0;
+ errno = err_win_to_posix(GetLastError());
+ return -1;
+}
+
int mingw_mkdir(const char *path, int mode)
{
int ret;
@@ -293,6 +336,8 @@ int mingw_mkdir(const char *path, int mode)
if (xutftowcs_path(wpath, path) < 0)
return -1;
ret = _wmkdir(wpath);
+ if (!ret && needs_hiding(path))
+ return set_hidden_flag(wpath, 1);
return ret;
}
@@ -319,6 +364,21 @@ int mingw_open (const char *filename, int oflags, ...)
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
errno = EISDIR;
}
+ if ((oflags & O_CREAT) && needs_hiding(filename)) {
+ /*
+ * Internally, _wopen() uses the CreateFile() API which errors
+ * out with an ERROR_ACCESS_DENIED if CREATE_ALWAYS was
+ * specified and an already existing file's attributes do not
+ * match *exactly*. As there is no mode or flag we can set that
+ * would correspond to FILE_ATTRIBUTE_HIDDEN, let's just try
+ * again *without* the O_CREAT flag (that corresponds to the
+ * CREATE_ALWAYS flag of CreateFile()).
+ */
+ if (fd < 0 && errno == EACCES)
+ fd = _wopen(wfilename, oflags & ~O_CREAT, mode);
+ if (fd >= 0 && set_hidden_flag(wfilename, 1))
+ warning("could not mark '%s' as hidden.", filename);
+ }
return fd;
}
@@ -350,6 +410,7 @@ int mingw_fgetc(FILE *stream)
#undef fopen
FILE *mingw_fopen (const char *filename, const char *otype)
{
+ int hide = needs_hiding(filename);
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
if (filename && !strcmp(filename, "/dev/null"))
@@ -357,12 +418,19 @@ FILE *mingw_fopen (const char *filename, const char *otype)
if (xutftowcs_path(wfilename, filename) < 0 ||
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
return NULL;
+ if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
+ error("could not unhide %s", filename);
+ return NULL;
+ }
file = _wfopen(wfilename, wotype);
+ if (file && hide && set_hidden_flag(wfilename, 1))
+ warning("could not mark '%s' as hidden.", filename);
return file;
}
FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
{
+ int hide = needs_hiding(filename);
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
if (filename && !strcmp(filename, "/dev/null"))
@@ -370,7 +438,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
if (xutftowcs_path(wfilename, filename) < 0 ||
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
return NULL;
+ if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
+ error("could not unhide %s", filename);
+ return NULL;
+ }
file = _wfreopen(wfilename, wotype, stream);
+ if (file && hide && set_hidden_flag(wfilename, 1))
+ warning("could not mark '%s' as hidden.", filename);
return file;
}
diff --git a/compat/mingw.h b/compat/mingw.h
index 1de70ffd62..69bb43dc35 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -142,6 +142,7 @@ static inline int fcntl(int fd, int cmd, ...)
#define sigemptyset(x) (void)0
static inline int sigaddset(sigset_t *set, int signum)
{ return 0; }
+#define SIG_BLOCK 0
#define SIG_UNBLOCK 0
static inline int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
{ return 0; }
@@ -416,9 +417,6 @@ int mingw_offset_1st_component(const char *path);
void mingw_open_html(const char *path);
#define open_html mingw_open_html
-void mingw_mark_as_git_dir(const char *dir);
-#define mark_as_git_dir mingw_mark_as_git_dir
-
/**
* Converts UTF-8 encoded string to UTF-16LE.
*
diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c
index dfbe6d8408..4293b53b17 100644
--- a/compat/precompose_utf8.c
+++ b/compat/precompose_utf8.c
@@ -147,7 +147,7 @@ struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *prec_dir)
if (errno || inleft) {
/*
* iconv() failed and errno could be E2BIG, EILSEQ, EINVAL, EBADF
- * MacOS X avoids illegal byte sequemces.
+ * MacOS X avoids illegal byte sequences.
* If they occur on a mounted drive (e.g. NFS) it is not worth to
* die() for that, but rather let the user see the original name
*/
diff --git a/compat/win32/pthread.h b/compat/win32/pthread.h
index b6ed9e7462..1c164088fb 100644
--- a/compat/win32/pthread.h
+++ b/compat/win32/pthread.h
@@ -104,4 +104,11 @@ static inline void *pthread_getspecific(pthread_key_t key)
return TlsGetValue(key);
}
+#ifndef __MINGW64_VERSION_MAJOR
+static inline int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
+{
+ return 0;
+}
+#endif
+
#endif /* PTHREAD_H */
diff --git a/compat/win32/syslog.c b/compat/win32/syslog.c
index b905aea31b..6c7c9b6053 100644
--- a/compat/win32/syslog.c
+++ b/compat/win32/syslog.c
@@ -28,13 +28,13 @@ void syslog(int priority, const char *fmt, ...)
va_end(ap);
if (str_len < 0) {
- warning("vsnprintf failed: '%s'", strerror(errno));
+ warning_errno("vsnprintf failed");
return;
}
str = malloc(st_add(str_len, 1));
if (!str) {
- warning("malloc failed: '%s'", strerror(errno));
+ warning_errno("malloc failed");
return;
}
@@ -45,7 +45,7 @@ void syslog(int priority, const char *fmt, ...)
while ((pos = strstr(str, "%1")) != NULL) {
str = realloc(str, st_add(++str_len, 1));
if (!str) {
- warning("realloc failed: '%s'", strerror(errno));
+ warning_errno("realloc failed");
return;
}
memmove(pos + 2, pos + 1, strlen(pos));
diff --git a/compat/win32mmap.c b/compat/win32mmap.c
index 80a8c9af4f..519d51f2b6 100644
--- a/compat/win32mmap.c
+++ b/compat/win32mmap.c
@@ -2,37 +2,42 @@
void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
{
- HANDLE hmap;
+ HANDLE osfhandle, hmap;
void *temp;
- off_t len;
- struct stat st;
+ LARGE_INTEGER len;
uint64_t o = offset;
uint32_t l = o & 0xFFFFFFFF;
uint32_t h = (o >> 32) & 0xFFFFFFFF;
- if (!fstat(fd, &st))
- len = st.st_size;
- else
+ osfhandle = (HANDLE)_get_osfhandle(fd);
+ if (!GetFileSizeEx(osfhandle, &len))
die("mmap: could not determine filesize");
- if ((length + offset) > len)
- length = xsize_t(len - offset);
+ if ((length + offset) > len.QuadPart)
+ length = xsize_t(len.QuadPart - offset);
if (!(flags & MAP_PRIVATE))
die("Invalid usage of mmap when built with USE_WIN32_MMAP");
- hmap = CreateFileMapping((HANDLE)_get_osfhandle(fd), NULL,
- PAGE_WRITECOPY, 0, 0, NULL);
+ hmap = CreateFileMapping(osfhandle, NULL,
+ prot == PROT_READ ? PAGE_READONLY : PAGE_WRITECOPY, 0, 0, NULL);
- if (!hmap)
+ if (!hmap) {
+ errno = EINVAL;
return MAP_FAILED;
+ }
- temp = MapViewOfFileEx(hmap, FILE_MAP_COPY, h, l, length, start);
+ temp = MapViewOfFileEx(hmap, prot == PROT_READ ?
+ FILE_MAP_READ : FILE_MAP_COPY, h, l, length, start);
if (!CloseHandle(hmap))
warning("unable to close file mapping handle");
- return temp ? temp : MAP_FAILED;
+ if (temp)
+ return temp;
+
+ errno = GetLastError() == ERROR_COMMITMENT_LIMIT ? EFBIG : EINVAL;
+ return MAP_FAILED;
}
int git_munmap(void *start, size_t length)
diff --git a/compat/winansi.c b/compat/winansi.c
index 5dfa5ed61f..3be60ce1c6 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -483,6 +483,7 @@ static size_t sizeof_ioinfo = 0;
#define IOINFO_L2E 5
#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
+#define FPIPE 0x08
#define FDEV 0x40
static inline ioinfo* _pioinfo(int fd)
@@ -530,6 +531,45 @@ static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
return old_handle;
}
+#ifdef DETECT_MSYS_TTY
+
+#include <winternl.h>
+#include <ntstatus.h>
+
+static void detect_msys_tty(int fd)
+{
+ ULONG result;
+ BYTE buffer[1024];
+ POBJECT_NAME_INFORMATION nameinfo = (POBJECT_NAME_INFORMATION) buffer;
+ PWSTR name;
+
+ /* check if fd is a pipe */
+ HANDLE h = (HANDLE) _get_osfhandle(fd);
+ if (GetFileType(h) != FILE_TYPE_PIPE)
+ return;
+
+ /* get pipe name */
+ if (!NT_SUCCESS(NtQueryObject(h, ObjectNameInformation,
+ buffer, sizeof(buffer) - 2, &result)))
+ return;
+ name = nameinfo->Name.Buffer;
+ name[nameinfo->Name.Length] = 0;
+
+ /* check if this could be a MSYS2 pty pipe ('msys-XXXX-ptyN-XX') */
+ if (!wcsstr(name, L"msys-") || !wcsstr(name, L"-pty"))
+ return;
+
+ /* init ioinfo size if we haven't done so */
+ if (init_sizeof_ioinfo())
+ return;
+
+ /* set FDEV flag, reset FPIPE flag */
+ _pioinfo(fd)->osflags &= ~FPIPE;
+ _pioinfo(fd)->osflags |= FDEV;
+}
+
+#endif
+
void winansi_init(void)
{
int con1, con2;
@@ -538,8 +578,15 @@ void winansi_init(void)
/* check if either stdout or stderr is a console output screen buffer */
con1 = is_console(1);
con2 = is_console(2);
- if (!con1 && !con2)
+ if (!con1 && !con2) {
+#ifdef DETECT_MSYS_TTY
+ /* check if stdin / stdout / stderr are MSYS2 pty pipes */
+ detect_msys_tty(0);
+ detect_msys_tty(1);
+ detect_msys_tty(2);
+#endif
return;
+ }
/* create a named pipe to communicate with the console thread */
xsnprintf(name, sizeof(name), "\\\\.\\pipe\\winansi%lu", GetCurrentProcessId());
@@ -575,8 +622,11 @@ void winansi_init(void)
HANDLE winansi_get_osfhandle(int fd)
{
HANDLE hnd = (HANDLE) _get_osfhandle(fd);
- if ((fd == 1 || fd == 2) && isatty(fd)
- && GetFileType(hnd) == FILE_TYPE_PIPE)
- return (fd == 1) ? hconsole1 : hconsole2;
+ if (isatty(fd) && GetFileType(hnd) == FILE_TYPE_PIPE) {
+ if (fd == 1 && hconsole1)
+ return hconsole1;
+ else if (fd == 2 && hconsole2)
+ return hconsole2;
+ }
return hnd;
}
diff --git a/config.c b/config.c
index 47b2ce1d5a..f51c56bf92 100644
--- a/config.c
+++ b/config.c
@@ -162,7 +162,7 @@ void git_config_push_parameter(const char *text)
{
struct strbuf env = STRBUF_INIT;
const char *old = getenv(CONFIG_DATA_ENVIRONMENT);
- if (old) {
+ if (old && *old) {
strbuf_addstr(&env, old);
strbuf_addch(&env, ' ');
}
@@ -717,6 +717,9 @@ static int git_default_core_config(const char *var, const char *value)
if (!strcmp(var, "core.attributesfile"))
return git_config_pathname(&git_attributes_file, var, value);
+ if (!strcmp(var, "core.hookspath"))
+ return git_config_pathname(&git_hooks_path, var, value);
+
if (!strcmp(var, "core.bare")) {
is_bare_repository_cfg = git_config_bool(var, value);
return 0;
@@ -803,8 +806,6 @@ static int git_default_core_config(const char *var, const char *value)
if (!strcmp(var, "core.autocrlf")) {
if (value && !strcasecmp(value, "input")) {
- if (core_eol == EOL_CRLF)
- return error("core.autocrlf=input conflicts with core.eol=crlf");
auto_crlf = AUTO_CRLF_INPUT;
return 0;
}
@@ -830,8 +831,6 @@ static int git_default_core_config(const char *var, const char *value)
core_eol = EOL_NATIVE;
else
core_eol = EOL_UNSET;
- if (core_eol == EOL_CRLF && auto_crlf == AUTO_CRLF_INPUT)
- return error("core.autocrlf=input conflicts with core.eol=crlf");
return 0;
}
@@ -912,6 +911,14 @@ static int git_default_core_config(const char *var, const char *value)
return 0;
}
+ if (!strcmp(var, "core.hidedotfiles")) {
+ if (value && !strcasecmp(value, "dotgitonly"))
+ hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
+ else
+ hide_dotfiles = git_config_bool(var, value);
+ return 0;
+ }
+
/* Add other config variables here and to Documentation/config.txt. */
return 0;
}
@@ -1309,14 +1316,11 @@ static struct config_set_element *configset_find_element(struct config_set *cs,
struct config_set_element k;
struct config_set_element *found_entry;
char *normalized_key;
- int ret;
/*
* `key` may come from the user, so normalize it before using it
* for querying entries from the hashmap.
*/
- ret = git_config_parse_key(key, &normalized_key, NULL);
-
- if (ret)
+ if (git_config_parse_key(key, &normalized_key, NULL))
return NULL;
hashmap_entry_init(&k, strhash(normalized_key));
@@ -2012,7 +2016,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
lock = xcalloc(1, sizeof(struct lock_file));
fd = hold_lock_file_for_update(lock, config_filename, 0);
if (fd < 0) {
- error("could not lock config file %s: %s", config_filename, strerror(errno));
+ error_errno("could not lock config file %s", config_filename);
free(store.key);
ret = CONFIG_NO_LOCK;
goto out_free;
@@ -2026,8 +2030,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
free(store.key);
if ( ENOENT != errno ) {
- error("opening %s: %s", config_filename,
- strerror(errno));
+ error_errno("opening %s", config_filename);
ret = CONFIG_INVALID_FILE; /* same as "invalid config file" */
goto out_free;
}
@@ -2111,8 +2114,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
if (contents == MAP_FAILED) {
if (errno == ENODEV && S_ISDIR(st.st_mode))
errno = EISDIR;
- error("unable to mmap '%s': %s",
- config_filename, strerror(errno));
+ error_errno("unable to mmap '%s'", config_filename);
ret = CONFIG_INVALID_FILE;
contents = NULL;
goto out_free;
@@ -2121,8 +2123,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
in_fd = -1;
if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
- error("chmod on %s failed: %s",
- get_lock_file_path(lock), strerror(errno));
+ error_errno("chmod on %s failed", get_lock_file_path(lock));
ret = CONFIG_NO_WRITE;
goto out_free;
}
@@ -2178,8 +2179,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
}
if (commit_lock_file(lock) < 0) {
- error("could not write config file %s: %s", config_filename,
- strerror(errno));
+ error_errno("could not write config file %s", config_filename);
ret = CONFIG_NO_WRITE;
lock = NULL;
goto out_free;
@@ -2330,8 +2330,8 @@ int git_config_rename_section_in_file(const char *config_filename,
fstat(fileno(config_file), &st);
if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
- ret = error("chmod on %s failed: %s",
- get_lock_file_path(lock), strerror(errno));
+ ret = error_errno("chmod on %s failed",
+ get_lock_file_path(lock));
goto out;
}
@@ -2385,8 +2385,8 @@ int git_config_rename_section_in_file(const char *config_filename,
fclose(config_file);
unlock_and_out:
if (commit_lock_file(lock) < 0)
- ret = error("could not write config file %s: %s",
- config_filename, strerror(errno));
+ ret = error_errno("could not write config file %s",
+ config_filename);
out:
free(filename_buf);
return ret;
diff --git a/config.mak.uname b/config.mak.uname
index 40d6b29eee..a88f13989a 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -557,7 +557,8 @@ else
BASIC_LDFLAGS += -Wl,--large-address-aware
endif
CC = gcc
- COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0
+ COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY
+ EXTLIBS += -lntdll
INSTALL = /bin/install
NO_R_TO_GCC_LINKER = YesPlease
INTERNAL_QSORT = YesPlease
diff --git a/connected.c b/connected.c
index 299c56090b..bf1b12e7ec 100644
--- a/connected.c
+++ b/connected.c
@@ -86,17 +86,14 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
memcpy(commit, sha1_to_hex(sha1), 40);
if (write_in_full(rev_list.in, commit, 41) < 0) {
if (errno != EPIPE && errno != EINVAL)
- error(_("failed write to rev-list: %s"),
- strerror(errno));
+ error_errno(_("failed write to rev-list"));
err = -1;
break;
}
} while (!fn(cb_data, sha1));
- if (close(rev_list.in)) {
- error(_("failed to close rev-list's stdin: %s"), strerror(errno));
- err = -1;
- }
+ if (close(rev_list.in))
+ err = error_errno(_("failed to close rev-list's stdin"));
sigchain_pop(SIGPIPE);
return finish_command(&rev_list) || err;
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index e3918c87e3..34024754d9 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1339,15 +1339,15 @@ _git_help ()
{
case "$cur" in
--*)
- __gitcomp "--all --info --man --web"
+ __gitcomp "--all --guides --info --man --web"
return
;;
esac
__git_compute_all_commands
__gitcomp "$__git_all_commands $(__git_aliases)
attributes cli core-tutorial cvs-migration
- diffcore gitk glossary hooks ignore modules
- namespaces repository-layout tutorial tutorial-2
+ diffcore everyday gitk glossary hooks ignore modules
+ namespaces repository-layout revisions tutorial tutorial-2
workflows
"
}
@@ -1458,6 +1458,7 @@ _git_log ()
--relative-date --date=
--pretty= --format= --oneline
--show-signature
+ --cherry-mark
--cherry-pick
--graph
--decorate --decorate=
diff --git a/contrib/hooks/multimail/CHANGES b/contrib/hooks/multimail/CHANGES
index 53c71b422a..100cc7a6d3 100644
--- a/contrib/hooks/multimail/CHANGES
+++ b/contrib/hooks/multimail/CHANGES
@@ -1,3 +1,11 @@
+Release 1.3.1 (bugfix-only release)
+===================================
+
+* Generate links to commits in combined emails (it was done only for
+ commit emails in 1.3.0).
+
+* Fix broken links on PyPi.
+
Release 1.3.0
=============
diff --git a/contrib/hooks/multimail/README b/contrib/hooks/multimail/README
index 1e04801978..0c91d19a57 100644
--- a/contrib/hooks/multimail/README
+++ b/contrib/hooks/multimail/README
@@ -1,4 +1,4 @@
-git-multimail 1.3.0
+git-multimail 1.3.1
===================
.. image:: https://travis-ci.org/git-multimail/git-multimail.svg?branch=master
diff --git a/contrib/hooks/multimail/README.Git b/contrib/hooks/multimail/README.Git
index ee1fa75f99..1210bde045 100644
--- a/contrib/hooks/multimail/README.Git
+++ b/contrib/hooks/multimail/README.Git
@@ -6,10 +6,10 @@ website:
https://github.com/git-multimail/git-multimail
The version in this directory was obtained from the upstream project
-on May 03 2016 and consists of the "git-multimail" subdirectory from
+on May 13 2016 and consists of the "git-multimail" subdirectory from
revision
- 26f3ae9f86aa7f8a054ba89235c4d3879f98b03d refs/tags/1.3.0
+ 3ce5470d4abf7251604cbf64e73a962e1b617f5e refs/tags/1.3.1
Please see the README file in this directory for information about how
to report bugs or contribute to git-multimail.
diff --git a/contrib/hooks/multimail/git_multimail.py b/contrib/hooks/multimail/git_multimail.py
index f2c92aeed8..54ab4a4942 100755
--- a/contrib/hooks/multimail/git_multimail.py
+++ b/contrib/hooks/multimail/git_multimail.py
@@ -1,6 +1,6 @@
#! /usr/bin/env python
-__version__ = '1.3.0'
+__version__ = '1.3.1'
# Copyright (c) 2015 Matthieu Moy and others
# Copyright (c) 2012-2014 Michael Haggerty and others
@@ -1704,6 +1704,14 @@ class BranchChange(ReferenceChange):
self.header_template = COMBINED_HEADER_TEMPLATE
self.intro_template = COMBINED_INTRO_TEMPLATE
self.footer_template = COMBINED_FOOTER_TEMPLATE
+
+ def revision_gen_link(base_url):
+ # revision is used only to generate the body, and
+ # _content_type is set while generating headers. Get it
+ # from the BranchChange object.
+ revision._content_type = self._content_type
+ return revision.generate_browse_link(base_url)
+ self.generate_browse_link = revision_gen_link
for line in self.generate_email(push, body_filter, values):
yield line
diff --git a/convert.c b/convert.c
index f524b8d7f4..b1614bf7ff 100644
--- a/convert.c
+++ b/convert.c
@@ -1380,27 +1380,22 @@ static struct stream_filter *ident_filter(const unsigned char *sha1)
struct stream_filter *get_stream_filter(const char *path, const unsigned char *sha1)
{
struct conv_attrs ca;
- enum crlf_action crlf_action;
struct stream_filter *filter = NULL;
convert_attrs(&ca, path);
-
if (ca.drv && (ca.drv->smudge || ca.drv->clean))
- return filter;
+ return NULL;
+
+ if (ca.crlf_action == CRLF_AUTO || ca.crlf_action == CRLF_AUTO_CRLF)
+ return NULL;
if (ca.ident)
filter = ident_filter(sha1);
- crlf_action = ca.crlf_action;
-
- if ((crlf_action == CRLF_BINARY) ||
- crlf_action == CRLF_AUTO_INPUT ||
- (crlf_action == CRLF_TEXT_INPUT))
- filter = cascade_filter(filter, &null_filter_singleton);
-
- else if (output_eol(crlf_action) == EOL_CRLF &&
- !(crlf_action == CRLF_AUTO || crlf_action == CRLF_AUTO_CRLF))
+ if (output_eol(ca.crlf_action) == EOL_CRLF)
filter = cascade_filter(filter, lf_to_crlf_filter());
+ else
+ filter = cascade_filter(filter, &null_filter_singleton);
return filter;
}
diff --git a/copy.c b/copy.c
index 574fa1f09d..4de6a110f0 100644
--- a/copy.c
+++ b/copy.c
@@ -42,15 +42,15 @@ int copy_file(const char *dst, const char *src, int mode)
status = copy_fd(fdi, fdo);
switch (status) {
case COPY_READ_ERROR:
- error("copy-fd: read returned %s", strerror(errno));
+ error_errno("copy-fd: read returned");
break;
case COPY_WRITE_ERROR:
- error("copy-fd: write returned %s", strerror(errno));
+ error_errno("copy-fd: write returned");
break;
}
close(fdi);
if (close(fdo) != 0)
- return error("%s: close error: %s", dst, strerror(errno));
+ return error_errno("%s: close error", dst);
if (!status && adjust_shared_perm(dst))
return -1;
diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
index 291c0fd5e9..1f14d56e98 100644
--- a/credential-cache--daemon.c
+++ b/credential-cache--daemon.c
@@ -179,12 +179,12 @@ static int serve_cache_loop(int fd)
client = accept(fd, NULL, NULL);
if (client < 0) {
- warning("accept failed: %s", strerror(errno));
+ warning_errno("accept failed");
return 1;
}
client2 = dup(client);
if (client2 < 0) {
- warning("dup failed: %s", strerror(errno));
+ warning_errno("dup failed");
close(client);
return 1;
}
diff --git a/credential-cache.c b/credential-cache.c
index f4afdc6988..86e21de49b 100644
--- a/credential-cache.c
+++ b/credential-cache.c
@@ -32,6 +32,7 @@ static int send_request(const char *socket, const struct strbuf *out)
write_or_die(1, in, r);
got_data = 1;
}
+ close(fd);
return got_data;
}
diff --git a/credential.c b/credential.c
index 7d6501d190..aa996669fc 100644
--- a/credential.c
+++ b/credential.c
@@ -63,9 +63,12 @@ static int credential_config_callback(const char *var, const char *value,
key = dot + 1;
}
- if (!strcmp(key, "helper"))
- string_list_append(&c->helpers, value);
- else if (!strcmp(key, "username")) {
+ if (!strcmp(key, "helper")) {
+ if (*value)
+ string_list_append(&c->helpers, value);
+ else
+ string_list_clear(&c->helpers, 0);
+ } else if (!strcmp(key, "username")) {
if (!c->username)
c->username = xstrdup(value);
}
diff --git a/diff-no-index.c b/diff-no-index.c
index 03daadb25a..1f8999b9ca 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -65,8 +65,7 @@ static int populate_from_stdin(struct diff_filespec *s)
size_t size = 0;
if (strbuf_read(&buf, 0, 0) < 0)
- return error("error while reading from stdin %s",
- strerror(errno));
+ return error_errno("error while reading from stdin");
s->should_munmap = 0;
s->data = strbuf_detach(&buf, &size);
diff --git a/diff.c b/diff.c
index 059123c5dc..d3734d3181 100644
--- a/diff.c
+++ b/diff.c
@@ -26,6 +26,7 @@
#endif
static int diff_detect_rename_default;
+static int diff_compaction_heuristic = 1;
static int diff_rename_limit_default = 400;
static int diff_suppress_blank_empty;
static int diff_use_color_default = -1;
@@ -168,6 +169,11 @@ long parse_algorithm_value(const char *value)
* never be affected by the setting of diff.renames
* the user happens to have in the configuration file.
*/
+void init_diff_ui_defaults(void)
+{
+ diff_detect_rename_default = 1;
+}
+
int git_diff_ui_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
@@ -184,6 +190,10 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
diff_detect_rename_default = git_config_rename(var, value);
return 0;
}
+ if (!strcmp(var, "diff.compactionheuristic")) {
+ diff_compaction_heuristic = git_config_bool(var, value);
+ return 0;
+ }
if (!strcmp(var, "diff.autorefreshindex")) {
diff_auto_refresh_index = git_config_bool(var, value);
return 0;
@@ -3273,6 +3283,8 @@ void diff_setup(struct diff_options *options)
options->use_color = diff_use_color_default;
options->detect_rename = diff_detect_rename_default;
options->xdl_opts |= diff_algorithm;
+ if (diff_compaction_heuristic)
+ DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
options->orderfile = diff_order_file_cfg;
@@ -3793,6 +3805,10 @@ int diff_opt_parse(struct diff_options *options,
DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
else if (!strcmp(arg, "--ignore-blank-lines"))
DIFF_XDL_SET(options, IGNORE_BLANK_LINES);
+ else if (!strcmp(arg, "--compaction-heuristic"))
+ DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
+ else if (!strcmp(arg, "--no-compaction-heuristic"))
+ DIFF_XDL_CLR(options, COMPACTION_HEURISTIC);
else if (!strcmp(arg, "--patience"))
options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
else if (!strcmp(arg, "--histogram"))
diff --git a/diff.h b/diff.h
index e7d68edaf9..125447be09 100644
--- a/diff.h
+++ b/diff.h
@@ -266,6 +266,7 @@ extern int parse_long_opt(const char *opt, const char **argv,
const char **optarg);
extern int git_diff_basic_config(const char *var, const char *value, void *cb);
+extern void init_diff_ui_defaults(void);
extern int git_diff_ui_config(const char *var, const char *value, void *cb);
extern void diff_setup(struct diff_options *);
extern int diff_opt_parse(struct diff_options *, const char **, int, const char *);
diff --git a/dir.c b/dir.c
index 996653b0d3..6172b3438d 100644
--- a/dir.c
+++ b/dir.c
@@ -53,24 +53,16 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
int check_only, const struct path_simplify *simplify);
static int get_dtype(struct dirent *de, const char *path, int len);
-/* helper string functions with support for the ignore_case flag */
-int strcmp_icase(const char *a, const char *b)
+int fspathcmp(const char *a, const char *b)
{
return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
}
-int strncmp_icase(const char *a, const char *b, size_t count)
+int fspathncmp(const char *a, const char *b, size_t count)
{
return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
}
-int fnmatch_icase(const char *pattern, const char *string, int flags)
-{
- return wildmatch(pattern, string,
- flags | (ignore_case ? WM_CASEFOLD : 0),
- NULL);
-}
-
int git_fnmatch(const struct pathspec_item *item,
const char *pattern, const char *string,
int prefix)
@@ -802,12 +794,12 @@ int match_basename(const char *basename, int basenamelen,
{
if (prefix == patternlen) {
if (patternlen == basenamelen &&
- !strncmp_icase(pattern, basename, basenamelen))
+ !fspathncmp(pattern, basename, basenamelen))
return 1;
} else if (flags & EXC_FLAG_ENDSWITH) {
/* "*literal" matching against "fooliteral" */
if (patternlen - 1 <= basenamelen &&
- !strncmp_icase(pattern + 1,
+ !fspathncmp(pattern + 1,
basename + basenamelen - (patternlen - 1),
patternlen - 1))
return 1;
@@ -844,7 +836,7 @@ int match_pathname(const char *pathname, int pathlen,
*/
if (pathlen < baselen + 1 ||
(baselen && pathname[baselen] != '/') ||
- strncmp_icase(pathname, base, baselen))
+ fspathncmp(pathname, base, baselen))
return 0;
namelen = baselen ? pathlen - baselen - 1 : pathlen;
@@ -858,7 +850,7 @@ int match_pathname(const char *pathname, int pathlen,
if (prefix > namelen)
return 0;
- if (strncmp_icase(pattern, name, prefix))
+ if (fspathncmp(pattern, name, prefix))
return 0;
pattern += prefix;
patternlen -= prefix;
diff --git a/dir.h b/dir.h
index 301b737a37..bfde698c48 100644
--- a/dir.h
+++ b/dir.h
@@ -270,9 +270,8 @@ extern int remove_dir_recursively(struct strbuf *path, int flag);
/* tries to remove the path with empty directories along it, ignores ENOENT */
extern int remove_path(const char *path);
-extern int strcmp_icase(const char *a, const char *b);
-extern int strncmp_icase(const char *a, const char *b, size_t count);
-extern int fnmatch_icase(const char *pattern, const char *string, int flags);
+extern int fspathcmp(const char *a, const char *b);
+extern int fspathncmp(const char *a, const char *b, size_t count);
/*
* The prefix part of pattern must not contains wildcards.
diff --git a/editor.c b/editor.c
index 01c644cddb..7519edecdc 100644
--- a/editor.c
+++ b/editor.c
@@ -63,7 +63,6 @@ int launch_editor(const char *path, struct strbuf *buffer, const char *const *en
if (!buffer)
return 0;
if (strbuf_read_file(buffer, path, 0) < 0)
- return error("could not read file '%s': %s",
- path, strerror(errno));
+ return error_errno("could not read file '%s'", path);
return 0;
}
diff --git a/entry.c b/entry.c
index a4109574fa..519e04227b 100644
--- a/entry.c
+++ b/entry.c
@@ -168,8 +168,8 @@ static int write_entry(struct cache_entry *ce,
ret = symlink(new, path);
free(new);
if (ret)
- return error("unable to create symlink %s (%s)",
- path, strerror(errno));
+ return error_errno("unable to create symlink %s",
+ path);
break;
}
@@ -186,8 +186,7 @@ static int write_entry(struct cache_entry *ce,
fd = open_output_fd(path, ce, to_tempfile);
if (fd < 0) {
free(new);
- return error("unable to create file %s (%s)",
- path, strerror(errno));
+ return error_errno("unable to create file %s", path);
}
wrote = write_in_full(fd, new, size);
@@ -284,8 +283,7 @@ int checkout_entry(struct cache_entry *ce,
return error("%s is a directory", path.buf);
remove_subtree(&path);
} else if (unlink(path.buf))
- return error("unable to unlink old '%s' (%s)",
- path.buf, strerror(errno));
+ return error_errno("unable to unlink old '%s'", path.buf);
} else if (state->not_new)
return 0;
diff --git a/environment.c b/environment.c
index 57acb2fe2a..ca72464a98 100644
--- a/environment.c
+++ b/environment.c
@@ -31,6 +31,7 @@ const char *git_log_output_encoding;
const char *apply_default_whitespace;
const char *apply_default_ignorewhitespace;
const char *git_attributes_file;
+const char *git_hooks_path;
int zlib_compression_level = Z_BEST_SPEED;
int core_compression_level;
int core_compression_seen;
@@ -63,6 +64,7 @@ int core_apply_sparse_checkout;
int merge_log_config = -1;
int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
unsigned long pack_size_limit_cfg;
+enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
#ifndef PROTECT_HFS_DEFAULT
#define PROTECT_HFS_DEFAULT 0
diff --git a/fast-import.c b/fast-import.c
index a975c348d8..c504ef752d 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -415,7 +415,7 @@ static void write_crash_report(const char *err)
struct recent_command *rc;
if (!rpt) {
- error("can't write crash report %s: %s", loc, strerror(errno));
+ error_errno("can't write crash report %s", loc);
free(loc);
return;
}
@@ -1513,7 +1513,7 @@ static int tree_content_set(
t = root->tree;
for (i = 0; i < t->entry_count; i++) {
e = t->entries[i];
- if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+ if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
if (!*slash1) {
if (!S_ISDIR(mode)
&& e->versions[1].mode == mode
@@ -1603,7 +1603,7 @@ static int tree_content_remove(
t = root->tree;
for (i = 0; i < t->entry_count; i++) {
e = t->entries[i];
- if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+ if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
if (*slash1 && !S_ISDIR(e->versions[1].mode))
/*
* If p names a file in some subdirectory, and a
@@ -1670,7 +1670,7 @@ static int tree_content_get(
t = root->tree;
for (i = 0; i < t->entry_count; i++) {
e = t->entries[i];
- if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+ if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
if (!*slash1)
goto found_entry;
if (!S_ISDIR(e->versions[1].mode))
@@ -1807,8 +1807,8 @@ static void dump_marks(void)
return;
if (hold_lock_file_for_update(&mark_lock, export_marks_file, 0) < 0) {
- failure |= error("Unable to write marks file %s: %s",
- export_marks_file, strerror(errno));
+ failure |= error_errno("Unable to write marks file %s",
+ export_marks_file);
return;
}
@@ -1823,8 +1823,8 @@ static void dump_marks(void)
dump_marks_helper(f, 0, marks);
if (commit_lock_file(&mark_lock)) {
- failure |= error("Unable to write file %s: %s",
- export_marks_file, strerror(errno));
+ failure |= error_errno("Unable to write file %s",
+ export_marks_file);
return;
}
}
diff --git a/fetch-pack.c b/fetch-pack.c
index f96f6dfb35..b501d5c320 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -15,7 +15,6 @@
#include "version.h"
#include "prio-queue.h"
#include "sha1-array.h"
-#include "sigchain.h"
static int transfer_unpack_limit = -1;
static int fetch_unpack_limit = -1;
@@ -674,10 +673,8 @@ static int sideband_demux(int in, int out, void *data)
int *xd = data;
int ret;
- sigchain_push(SIGPIPE, SIG_IGN);
ret = recv_sideband("fetch-pack", xd[0], out);
close(out);
- sigchain_pop(SIGPIPE);
return ret;
}
@@ -701,6 +698,7 @@ static int get_pack(struct fetch_pack_args *args,
demux.proc = sideband_demux;
demux.data = xd;
demux.out = -1;
+ demux.isolate_sigpipe = 1;
if (start_async(&demux))
die("fetch-pack: unable to fork off sideband"
" demultiplexer");
diff --git a/fsck.c b/fsck.c
index ca4c685377..05315451c5 100644
--- a/fsck.c
+++ b/fsck.c
@@ -59,6 +59,7 @@
FUNC(HAS_DOTGIT, WARN) \
FUNC(NULL_SHA1, WARN) \
FUNC(ZERO_PADDED_FILEMODE, WARN) \
+ FUNC(NUL_IN_COMMIT, WARN) \
/* infos (reported as warnings, but ignored by default) */ \
FUNC(BAD_TAG_NAME, INFO) \
FUNC(MISSING_TAGGER_ENTRY, INFO)
@@ -312,9 +313,9 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
if (S_ISGITLINK(entry.mode))
continue;
if (S_ISDIR(entry.mode))
- result = options->walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data, options);
+ result = options->walk(&lookup_tree(entry.oid->hash)->object, OBJ_TREE, data, options);
else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))
- result = options->walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data, options);
+ result = options->walk(&lookup_blob(entry.oid->hash)->object, OBJ_BLOB, data, options);
else {
result = error("in tree %s: entry %s has bad mode %.6o",
oid_to_hex(&tree->object.oid), entry.path, entry.mode);
@@ -450,11 +451,11 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
while (desc.size) {
unsigned mode;
const char *name;
- const unsigned char *sha1;
+ const struct object_id *oid;
- sha1 = tree_entry_extract(&desc, &name, &mode);
+ oid = tree_entry_extract(&desc, &name, &mode);
- has_null_sha1 |= is_null_sha1(sha1);
+ has_null_sha1 |= is_null_oid(oid);
has_full_path |= !!strchr(name, '/');
has_empty_name |= !*name;
has_dot |= !strcmp(name, ".");
@@ -610,6 +611,7 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,
struct commit_graft *graft;
unsigned parent_count, parent_line_count = 0, author_count;
int err;
+ const char *buffer_begin = buffer;
if (verify_headers(buffer, size, &commit->object, options))
return -1;
@@ -666,9 +668,17 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,
err = fsck_ident(&buffer, &commit->object, options);
if (err)
return err;
- if (!commit->tree)
- return report(options, &commit->object, FSCK_MSG_BAD_TREE, "could not load commit's tree %s", sha1_to_hex(tree_sha1));
-
+ if (!commit->tree) {
+ err = report(options, &commit->object, FSCK_MSG_BAD_TREE, "could not load commit's tree %s", sha1_to_hex(tree_sha1));
+ if (err)
+ return err;
+ }
+ if (memchr(buffer_begin, '\0', size)) {
+ err = report(options, &commit->object, FSCK_MSG_NUL_IN_COMMIT,
+ "NUL byte in the commit object body");
+ if (err)
+ return err;
+ }
return 0;
}
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 77876d433a..822f857038 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -45,6 +45,7 @@ my ($diff_new_color) =
my $normal_color = $repo->get_color("", "reset");
my $diff_algorithm = $repo->config('diff.algorithm');
+my $diff_filter = $repo->config('interactive.difffilter');
my $use_readkey = 0;
my $use_termcap = 0;
@@ -754,7 +755,14 @@ sub parse_diff {
my @diff = run_cmd_pipe("git", @diff_cmd, "--", $path);
my @colored = ();
if ($diff_use_color) {
- @colored = run_cmd_pipe("git", @diff_cmd, qw(--color --), $path);
+ my @display_cmd = ("git", @diff_cmd, qw(--color --), $path);
+ if (defined $diff_filter) {
+ # quotemeta is overkill, but sufficient for shell-quoting
+ my $diff = join(' ', map { quotemeta } @display_cmd);
+ @display_cmd = ("$diff | $diff_filter");
+ }
+
+ @colored = run_cmd_pipe(@display_cmd);
}
my (@hunk) = { TEXT => [], DISPLAY => [], TYPE => 'header' };
@@ -765,7 +773,7 @@ sub parse_diff {
}
push @{$hunk[-1]{TEXT}}, $diff[$i];
push @{$hunk[-1]{DISPLAY}},
- ($diff_use_color ? $colored[$i] : $diff[$i]);
+ (@colored ? $colored[$i] : $diff[$i]);
}
return @hunk;
}
diff --git a/git-compat-util.h b/git-compat-util.h
index 1f8b5f3b1f..49d4029b8d 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -409,7 +409,9 @@ extern NORETURN void usagef(const char *err, ...) __attribute__((format (printf,
extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
extern NORETURN void die_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
+extern int error_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
+extern void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
#ifndef NO_OPENSSL
#ifdef APPLE_COMMON_CRYPTO
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index 02c0445be1..d50c85ed7b 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -1156,7 +1156,7 @@ sub prepDirForOutput
# FUTURE: This would more accurately emulate CVS by sending
# another copy of sticky after processing the files in that
# directory. Or intermediate: perhaps send all sticky's for
- # $seendirs after after processing all files.
+ # $seendirs after processing all files.
}
# update \n
@@ -2824,7 +2824,7 @@ sub statecleanup
}
# Return working directory CVS revision "1.X" out
-# of the the working directory "entries" state, for the given filename.
+# of the working directory "entries" state, for the given filename.
# This is prefixed with a dash if the file is scheduled for removal
# when it is committed.
sub revparse
@@ -2935,7 +2935,7 @@ sub filecleanup
return $filename;
}
-# Remove prependdir from the path, so that is is relative to the directory
+# Remove prependdir from the path, so that it is relative to the directory
# the CVS client was started from, rather than the top of the project.
# Essentially the inverse of filecleanup().
sub remove_prependdir
diff --git a/git-difftool--helper.sh b/git-difftool--helper.sh
index 2b11b1d6fe..84d6cc021c 100755
--- a/git-difftool--helper.sh
+++ b/git-difftool--helper.sh
@@ -44,10 +44,10 @@ launch_merge_tool () {
"$GIT_DIFF_PATH_TOTAL" "$MERGED"
if use_ext_cmd
then
- printf "Launch '%s' [Y/n]: " \
+ printf "Launch '%s' [Y/n]? " \
"$GIT_DIFFTOOL_EXTCMD"
else
- printf "Launch '%s' [Y/n]: " "$merge_tool"
+ printf "Launch '%s' [Y/n]? " "$merge_tool"
fi
read ans || return
if test "$ans" = n
diff --git a/git-difftool.perl b/git-difftool.perl
index 488d14b153..ebd13baa6e 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -138,6 +138,7 @@ sub setup_dir_diff
my %submodule;
my %symlink;
my @working_tree = ();
+ my %working_tree_dups = ();
my @rawdiff = split('\0', $diffrtn);
my $i = 0;
@@ -188,6 +189,10 @@ EOF
}
if ($rmode ne $null_mode) {
+ # Avoid duplicate working_tree entries
+ if ($working_tree_dups{$dst_path}++) {
+ next;
+ }
my ($use, $wt_sha1) = use_wt_file($repo, $workdir,
$dst_path, $rsha1);
if ($use) {
@@ -273,7 +278,7 @@ EOF
# temporary file to both the left and right directories to show the
# change in the recorded SHA1 for the submodule.
for my $path (keys %submodule) {
- my $ok;
+ my $ok = 0;
if (defined($submodule{$path}{left})) {
$ok = write_to_file("$ldir/$path",
"Subproject commit $submodule{$path}{left}");
@@ -289,7 +294,7 @@ EOF
# shows only the link itself, not the contents of the link target.
# This loop replicates that behavior.
for my $path (keys %symlink) {
- my $ok;
+ my $ok = 0;
if (defined($symlink{$path}{left})) {
$ok = write_to_file("$ldir/$path",
$symlink{$path}{left});
diff --git a/git-merge-octopus.sh b/git-merge-octopus.sh
index 8643f74cb0..dc2fd1b5a4 100755
--- a/git-merge-octopus.sh
+++ b/git-merge-octopus.sh
@@ -44,6 +44,12 @@ esac
# MRC is the current "merge reference commit"
# MRT is the current "merge result tree"
+if ! git diff-index --quiet --cached HEAD --
+then
+ echo "Error: Your local changes to the following files would be overwritten by merge"
+ git diff-index --cached --name-only HEAD -- | sed -e 's/^/ /'
+ exit 2
+fi
MRC=$(git rev-parse --verify -q $head)
MRT=$(git write-tree)
NON_FF_MERGE=0
diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh
index 54ac8e4846..9abd00be21 100644
--- a/git-mergetool--lib.sh
+++ b/git-mergetool--lib.sh
@@ -100,7 +100,7 @@ check_unchanged () {
while true
do
echo "$MERGED seems unchanged."
- printf "Was the merge successful? [y/n] "
+ printf "Was the merge successful [y/n]? "
read answer || return 1
case "$answer" in
y*|Y*) return 0 ;;
@@ -372,3 +372,28 @@ get_merge_tool () {
fi
echo "$merge_tool"
}
+
+mergetool_find_win32_cmd () {
+ executable=$1
+ sub_directory=$2
+
+ # Use $executable if it exists in $PATH
+ if type -p "$executable" >/dev/null 2>&1
+ then
+ printf '%s' "$executable"
+ return
+ fi
+
+ # Look for executable in the typical locations
+ for directory in $(env | grep -Ei '^PROGRAM(FILES(\(X86\))?|W6432)=' |
+ cut -d '=' -f 2- | sort -u)
+ do
+ if test -n "$directory" && test -x "$directory/$sub_directory/$executable"
+ then
+ printf '%s' "$directory/$sub_directory/$executable"
+ return
+ fi
+ done
+
+ printf '%s' "$executable"
+}
diff --git a/git-mergetool.sh b/git-mergetool.sh
index f67bab55e8..bf862705d8 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -413,7 +413,7 @@ done
prompt_after_failed_merge () {
while true
do
- printf "Continue merging other unresolved paths (y/n) ? "
+ printf "Continue merging other unresolved paths [y/n]? "
read ans || return 1
case "$ans" in
[yY]*)
diff --git a/git-p4.py b/git-p4.py
index 825b9f32d5..b6593cf9a1 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -1064,8 +1064,15 @@ class GitLFS(LargeFileSystem):
if pointerProcess.wait():
os.remove(contentFile)
die('git-lfs pointer command failed. Did you install the extension?')
- pointerContents = [i+'\n' for i in pointerFile.split('\n')[2:][:-1]]
- oid = pointerContents[1].split(' ')[1].split(':')[1][:-1]
+
+ # Git LFS removed the preamble in the output of the 'pointer' command
+ # starting from version 1.2.0. Check for the preamble here to support
+ # earlier versions.
+ # c.f. https://github.com/github/git-lfs/commit/da2935d9a739592bc775c98d8ef4df9c72ea3b43
+ if pointerFile.startswith('Git LFS pointer for'):
+ pointerFile = re.sub(r'Git LFS pointer for.*\n\n', '', pointerFile)
+
+ oid = re.search(r'^oid \w+:(\w+)', pointerFile, re.MULTILINE).group(1)
localLargeFile = os.path.join(
os.getcwd(),
'.git', 'lfs', 'objects', oid[:2], oid[2:4],
@@ -1073,7 +1080,7 @@ class GitLFS(LargeFileSystem):
)
# LFS Spec states that pointer files should not have the executable bit set.
gitMode = '100644'
- return (gitMode, pointerContents, localLargeFile)
+ return (gitMode, pointerFile, localLargeFile)
def pushFile(self, localLargeFile):
uploadProcess = subprocess.Popen(
@@ -1160,6 +1167,15 @@ class P4UserMap:
self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
self.emails[output["Email"]] = output["User"]
+ mapUserConfigRegex = re.compile(r"^\s*(\S+)\s*=\s*(.+)\s*<(\S+)>\s*$", re.VERBOSE)
+ for mapUserConfig in gitConfigList("git-p4.mapUser"):
+ mapUser = mapUserConfigRegex.findall(mapUserConfig)
+ if mapUser and len(mapUser[0]) == 3:
+ user = mapUser[0][0]
+ fullname = mapUser[0][1]
+ email = mapUser[0][2]
+ self.users[user] = fullname + " <" + email + ">"
+ self.emails[email] = user
s = ''
for (key, val) in self.users.items():
@@ -2311,6 +2327,15 @@ class P4Sync(Command, P4UserMap):
fnum = fnum + 1
return files
+ def extractJobsFromCommit(self, commit):
+ jobs = []
+ jnum = 0
+ while commit.has_key("job%s" % jnum):
+ job = commit["job%s" % jnum]
+ jobs.append(job)
+ jnum = jnum + 1
+ return jobs
+
def stripRepoPath(self, path, prefixes):
"""When streaming files, this is called to map a p4 depot path
to where it should go in git. The prefixes are either
@@ -2656,6 +2681,7 @@ class P4Sync(Command, P4UserMap):
def commit(self, details, files, branch, parent = ""):
epoch = details["time"]
author = details["user"]
+ jobs = self.extractJobsFromCommit(details)
if self.verbose:
print('commit into {0}'.format(branch))
@@ -2683,6 +2709,8 @@ class P4Sync(Command, P4UserMap):
self.gitStream.write("data <<EOT\n")
self.gitStream.write(details["desc"])
+ if len(jobs) > 0:
+ self.gitStream.write("\nJobs: %s" % (' '.join(jobs)))
self.gitStream.write("\n[git-p4: depot-paths = \"%s\": change = %s" %
(','.join(self.branchPrefixes), details["change"]))
if len(details['options']) > 0:
diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index 55fe8d56c9..d3c39980f3 100644
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -56,11 +56,13 @@ get_remote_merge_branch () {
error_on_missing_default_upstream () {
cmd="$1"
op_type="$2"
- op_prep="$3"
+ op_prep="$3" # FIXME: op_prep is no longer used
example="$4"
branch_name=$(git symbolic-ref -q HEAD)
+ display_branch_name="${branch_name#refs/heads/}"
# If there's only one remote, use that in the suggestion
- remote="<remote>"
+ remote="$(gettext "<remote>")"
+ branch="$(gettext "<branch>")"
if test $(git remote | wc -l) = 1
then
remote=$(git remote)
@@ -68,22 +70,32 @@ error_on_missing_default_upstream () {
if test -z "$branch_name"
then
- echo "You are not currently on a branch. Please specify which
-branch you want to $op_type $op_prep. See git-${cmd}(1) for details.
-
- $example
-"
+ gettextln "You are not currently on a branch."
else
- echo "There is no tracking information for the current branch.
-Please specify which branch you want to $op_type $op_prep.
-See git-${cmd}(1) for details
-
- $example
-
-If you wish to set tracking information for this branch you can do so with:
-
- git branch --set-upstream-to=$remote/<branch> ${branch_name#refs/heads/}
-"
+ gettextln "There is no tracking information for the current branch."
+ fi
+ case "$op_type" in
+ rebase)
+ gettextln "Please specify which branch you want to rebase against."
+ ;;
+ merge)
+ gettextln "Please specify which branch you want to merge with."
+ ;;
+ *)
+ echo >&2 "BUG: unknown operation type: $op_type"
+ exit 1
+ ;;
+ esac
+ eval_gettextln "See git-\${cmd}(1) for details."
+ echo
+ echo " $example"
+ echo
+ if test -n "$branch_name"
+ then
+ gettextln "If you wish to set tracking information for this branch you can do so with:"
+ echo
+ echo " git branch --set-upstream-to=$remote/$branch $display_branch_name"
+ echo
fi
exit 1
}
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 4cde685b43..9d2bfb7a16 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -82,6 +82,7 @@ rewritten_pending="$state_dir"/rewritten-pending
cr=$(printf "\015")
strategy_args=${strategy:+--strategy=$strategy}
+test -n "$strategy_opts" &&
eval '
for strategy_opt in '"$strategy_opts"'
do
@@ -548,7 +549,8 @@ do_next () {
mark_action_done
do_pick $sha1 "$rest"
- warn "Stopped at $sha1... $rest"
+ sha1_abbrev=$(git rev-parse --short $sha1)
+ warn "Stopped at $sha1_abbrev... $rest"
exit_with_patch $sha1 0
;;
squash|s|fixup|f)
@@ -865,12 +867,12 @@ add_exec_commands () {
# $3: the input filename
check_commit_sha () {
badsha=0
- if test -z $1
+ if test -z "$1"
then
badsha=1
else
sha1_verif="$(git rev-parse --verify --quiet $1^{commit})"
- if test -z $sha1_verif
+ if test -z "$sha1_verif"
then
badsha=1
fi
diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh
index 2cc2a6d273..8d43db9069 100644
--- a/git-rebase--merge.sh
+++ b/git-rebase--merge.sh
@@ -67,7 +67,9 @@ call_merge () {
GIT_MERGE_VERBOSITY=1 && export GIT_MERGE_VERBOSITY
fi
test -z "$strategy" && strategy=recursive
- eval 'git-merge-$strategy' $strategy_opts '"$cmt^" -- "$hd" "$cmt"'
+ # If cmt doesn't have a parent, don't include it as a base
+ base=$(git rev-parse --verify --quiet $cmt^)
+ eval 'git-merge-$strategy' $strategy_opts $base ' -- "$hd" "$cmt"'
rv=$?
case "$rv" in
0)
diff --git a/git-rebase.sh b/git-rebase.sh
index cf60c43908..44ede367ae 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -87,7 +87,10 @@ preserve_merges=
autosquash=
keep_empty=
test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
-gpg_sign_opt=
+case "$(git config --bool commit.gpgsign)" in
+true) gpg_sign_opt=-S ;;
+*) gpg_sign_opt= ;;
+esac
read_basic_state () {
test -f "$state_dir/head-name" &&
@@ -248,6 +251,7 @@ do
;;
--exec=*)
cmd="${cmd}exec ${1#--exec=}${LF}"
+ test -z "$interactive_rebase" && interactive_rebase=implied
;;
--interactive)
interactive_rebase=explicit
@@ -348,12 +352,6 @@ do
done
test $# -gt 2 && usage
-if test -n "$cmd" &&
- test "$interactive_rebase" != explicit
-then
- die "$(gettext "The --exec option must be used with the --interactive option")"
-fi
-
if test -n "$action"
then
test -z "$in_progress" && die "$(gettext "No rebase in progress?")"
diff --git a/git-send-email.perl b/git-send-email.perl
index 1406f64d78..69587856df 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -827,9 +827,10 @@ if (defined $sender) {
# But it's a no-op to run sanitize_address on an already sanitized address.
$sender = sanitize_address($sender);
+my $to_whom = "To whom should the emails be sent (if anyone)?";
my $prompting = 0;
if (!@initial_to && !defined $to_cmd) {
- my $to = ask("Who should the emails be sent to (if any)? ",
+ my $to = ask("$to_whom ",
default => "",
valid_re => qr/\@.*\./, confirm_only => 1);
push @initial_to, parse_address_line($to) if defined $to; # sanitized/validated later
@@ -924,7 +925,7 @@ sub validate_address {
cleanup_compose_files();
exit(0);
}
- $address = ask("Who should the email be sent to (if any)? ",
+ $address = ask("$to_whom ",
default => "",
valid_re => qr/\@.*\./, confirm_only => 1);
}
diff --git a/git-stash.sh b/git-stash.sh
index c7c65e25f5..c7509e8da4 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -185,7 +185,7 @@ store_stash () {
git update-ref --create-reflog -m "$stash_msg" $ref_stash $w_commit
ret=$?
- test $ret != 0 && test -z $quiet &&
+ test $ret != 0 && test -z "$quiet" &&
die "$(eval_gettext "Cannot update \$ref_stash with \$w_commit")"
return $ret
}
@@ -277,7 +277,7 @@ save_stash () {
git clean --force --quiet -d $CLEAN_X_OPTION
fi
- if test "$keep_index" = "t" && test -n $i_tree
+ if test "$keep_index" = "t" && test -n "$i_tree"
then
git read-tree --reset -u $i_tree
fi
diff --git a/git-submodule.sh b/git-submodule.sh
index 753a90d307..5a4dec050b 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -8,7 +8,7 @@ dashless=$(basename "$0" | sed -e 's/-/ /')
USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>]
or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
or: $dashless [--quiet] init [--] [<path>...]
- or: $dashless [--quiet] deinit [-f|--force] [--] <path>...
+ or: $dashless [--quiet] deinit [-f|--force] (--all| [--] <path>...)
or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--reference <repository>] [--recursive] [--] [<path>...]
or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
or: $dashless [--quiet] foreach [--recursive] <command>
@@ -46,79 +46,6 @@ prefix=
custom_name=
depth=
-# The function takes at most 2 arguments. The first argument is the
-# URL that navigates to the submodule origin repo. When relative, this URL
-# is relative to the superproject origin URL repo. The second up_path
-# argument, if specified, is the relative path that navigates
-# from the submodule working tree to the superproject working tree.
-#
-# The output of the function is the origin URL of the submodule.
-#
-# The output will either be an absolute URL or filesystem path (if the
-# superproject origin URL is an absolute URL or filesystem path,
-# respectively) or a relative file system path (if the superproject
-# origin URL is a relative file system path).
-#
-# When the output is a relative file system path, the path is either
-# relative to the submodule working tree, if up_path is specified, or to
-# the superproject working tree otherwise.
-resolve_relative_url ()
-{
- remote=$(get_default_remote)
- remoteurl=$(git config "remote.$remote.url") ||
- remoteurl=$(pwd) # the repository is its own authoritative upstream
- url="$1"
- remoteurl=${remoteurl%/}
- sep=/
- up_path="$2"
-
- case "$remoteurl" in
- *:*|/*)
- is_relative=
- ;;
- ./*|../*)
- is_relative=t
- ;;
- *)
- is_relative=t
- remoteurl="./$remoteurl"
- ;;
- esac
-
- while test -n "$url"
- do
- case "$url" in
- ../*)
- url="${url#../}"
- case "$remoteurl" in
- */*)
- remoteurl="${remoteurl%/*}"
- ;;
- *:*)
- remoteurl="${remoteurl%:*}"
- sep=:
- ;;
- *)
- if test -z "$is_relative" || test "." = "$remoteurl"
- then
- die "$(eval_gettext "cannot strip one component off url '\$remoteurl'")"
- else
- remoteurl=.
- fi
- ;;
- esac
- ;;
- ./*)
- url="${url#./}"
- ;;
- *)
- break;;
- esac
- done
- remoteurl="$remoteurl$sep${url%/}"
- echo "${is_relative:+${up_path}}${remoteurl#./}"
-}
-
# Resolve a path to be relative to another path. This is intended for
# converting submodule paths when git-submodule is run in a subdirectory
# and only handles paths where the directory separator is '/'.
@@ -192,6 +119,17 @@ isnumber()
n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
}
+# Sanitize the local git environment for use within a submodule. We
+# can't simply use clear_local_git_env since we want to preserve some
+# of the settings from GIT_CONFIG_PARAMETERS.
+sanitize_submodule_env()
+{
+ save_config=$GIT_CONFIG_PARAMETERS
+ clear_local_git_env
+ GIT_CONFIG_PARAMETERS=$save_config
+ export GIT_CONFIG_PARAMETERS
+}
+
#
# Add a new submodule to the working tree, .gitmodules and the index
#
@@ -281,7 +219,7 @@ cmd_add()
die "$(gettext "Relative path can only be used from the toplevel of the working tree")"
# dereference source url relative to parent's url
- realrepo=$(resolve_relative_url "$repo") || exit
+ realrepo=$(git submodule--helper resolve-relative-url "$repo") || exit
;;
*:*|/*)
# absolute url
@@ -347,9 +285,9 @@ Use -f if you really want to add it." >&2
echo "$(eval_gettext "Reactivating local git directory for submodule '\$sm_name'.")"
fi
fi
- git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" "$reference" "$depth" || exit
+ git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" ${reference:+"$reference"} ${depth:+"$depth"} || exit
(
- clear_local_git_env
+ sanitize_submodule_env
cd "$sm_path" &&
# ash fails to wordsplit ${branch:+-b "$branch"...}
case "$branch" in
@@ -418,7 +356,7 @@ cmd_foreach()
name=$(git submodule--helper name "$sm_path")
(
prefix="$prefix$sm_path/"
- clear_local_git_env
+ sanitize_submodule_env
cd "$sm_path" &&
sm_path=$(relative_path "$sm_path") &&
# we make $path available to scripts ...
@@ -467,50 +405,7 @@ cmd_init()
shift
done
- git submodule--helper list --prefix "$wt_prefix" "$@" |
- while read mode sha1 stage sm_path
- do
- die_if_unmatched "$mode"
- name=$(git submodule--helper name "$sm_path") || exit
-
- displaypath=$(relative_path "$prefix$sm_path")
-
- # Copy url setting when it is not set yet
- if test -z "$(git config "submodule.$name.url")"
- then
- url=$(git config -f .gitmodules submodule."$name".url)
- test -z "$url" &&
- die "$(eval_gettext "No url found for submodule path '\$displaypath' in .gitmodules")"
-
- # Possibly a url relative to parent
- case "$url" in
- ./*|../*)
- url=$(resolve_relative_url "$url") || exit
- ;;
- esac
- git config submodule."$name".url "$url" ||
- die "$(eval_gettext "Failed to register url for submodule path '\$displaypath'")"
-
- say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$displaypath'")"
- fi
-
- # Copy "update" setting when it is not set yet
- if upd="$(git config -f .gitmodules submodule."$name".update)" &&
- test -n "$upd" &&
- test -z "$(git config submodule."$name".update)"
- then
- case "$upd" in
- checkout | rebase | merge | none)
- ;; # known modes of updating
- *)
- echo >&2 "warning: unknown update mode '$upd' suggested for submodule '$name'"
- upd=none
- ;;
- esac
- git config submodule."$name".update "$upd" ||
- die "$(eval_gettext "Failed to register update mode for submodule path '\$displaypath'")"
- fi
- done
+ git ${wt_prefix:+-C "$wt_prefix"} submodule--helper init ${GIT_QUIET:+--quiet} ${prefix:+--prefix "$prefix"} "$@"
}
#
@@ -521,6 +416,7 @@ cmd_init()
cmd_deinit()
{
# parse $args after "submodule ... deinit".
+ deinit_all=
while test $# -ne 0
do
case "$1" in
@@ -530,6 +426,9 @@ cmd_deinit()
-q|--quiet)
GIT_QUIET=1
;;
+ --all)
+ deinit_all=t
+ ;;
--)
shift
break
@@ -544,9 +443,14 @@ cmd_deinit()
shift
done
- if test $# = 0
+ if test -n "$deinit_all" && test "$#" -ne 0
+ then
+ echo >&2 "$(eval_gettext "pathspec and --all are incompatible")"
+ usage
+ fi
+ if test $# = 0 && test -z "$deinit_all"
then
- die "$(eval_gettext "Use '.' if you really want to deinitialize all submodules")"
+ die "$(eval_gettext "Use '--all' if you really want to deinitialize all submodules")"
fi
git submodule--helper list --prefix "$wt_prefix" "$@" |
@@ -592,14 +496,14 @@ cmd_deinit()
}
is_tip_reachable () (
- clear_local_git_env
+ sanitize_submodule_env &&
cd "$1" &&
rev=$(git rev-list -n 1 "$2" --not --all 2>/dev/null) &&
test -z "$rev"
)
fetch_in_submodule () (
- clear_local_git_env
+ sanitize_submodule_env &&
cd "$1" &&
case "$2" in
'')
@@ -663,6 +567,14 @@ cmd_update()
--depth=*)
depth=$1
;;
+ -j|--jobs)
+ case "$2" in '') usage ;; esac
+ jobs="--jobs=$2"
+ shift
+ ;;
+ --jobs=*)
+ jobs=$1
+ ;;
--)
shift
break
@@ -682,17 +594,21 @@ cmd_update()
cmd_init "--" "$@" || return
fi
- cloned_modules=
- git submodule--helper list --prefix "$wt_prefix" "$@" | {
+ {
+ git submodule--helper update-clone ${GIT_QUIET:+--quiet} \
+ ${wt_prefix:+--prefix "$wt_prefix"} \
+ ${prefix:+--recursive-prefix "$prefix"} \
+ ${update:+--update "$update"} \
+ ${reference:+--reference "$reference"} \
+ ${depth:+--depth "$depth"} \
+ ${jobs:+$jobs} \
+ "$@" || echo "#unmatched"
+ } | {
err=
- while read mode sha1 stage sm_path
+ while read mode sha1 stage just_cloned sm_path
do
die_if_unmatched "$mode"
- if test "$stage" = U
- then
- echo >&2 "Skipping unmerged submodule $prefix$sm_path"
- continue
- fi
+
name=$(git submodule--helper name "$sm_path") || exit
url=$(git config submodule."$name".url)
branch=$(get_submodule_config "$name" branch master)
@@ -709,29 +625,12 @@ cmd_update()
displaypath=$(relative_path "$prefix$sm_path")
- if test "$update_module" = "none"
- then
- echo "Skipping submodule '$displaypath'"
- continue
- fi
-
- if test -z "$url"
- then
- # Only mention uninitialized submodules when its
- # path have been specified
- test "$#" != "0" &&
- say "$(eval_gettext "Submodule path '\$displaypath' not initialized
-Maybe you want to use 'update --init'?")"
- continue
- fi
-
- if ! test -d "$sm_path"/.git && ! test -f "$sm_path"/.git
+ if test $just_cloned -eq 1
then
- git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$prefix" --path "$sm_path" --name "$name" --url "$url" "$reference" "$depth" || exit
- cloned_modules="$cloned_modules;$name"
subsha1=
+ update_module=checkout
else
- subsha1=$(clear_local_git_env; cd "$sm_path" &&
+ subsha1=$(sanitize_submodule_env; cd "$sm_path" &&
git rev-parse --verify HEAD) ||
die "$(eval_gettext "Unable to find current revision in submodule path '\$displaypath'")"
fi
@@ -741,11 +640,11 @@ Maybe you want to use 'update --init'?")"
if test -z "$nofetch"
then
# Fetch remote before determining tracking $sha1
- (clear_local_git_env; cd "$sm_path" && git-fetch) ||
+ (sanitize_submodule_env; cd "$sm_path" && git-fetch) ||
die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")"
fi
- remote_name=$(clear_local_git_env; cd "$sm_path" && get_default_remote)
- sha1=$(clear_local_git_env; cd "$sm_path" &&
+ remote_name=$(sanitize_submodule_env; cd "$sm_path" && get_default_remote)
+ sha1=$(sanitize_submodule_env; cd "$sm_path" &&
git rev-parse --verify "${remote_name}/${branch}") ||
die "$(eval_gettext "Unable to find current ${remote_name}/${branch} revision in submodule path '\$sm_path'")"
fi
@@ -774,13 +673,6 @@ Maybe you want to use 'update --init'?")"
die "$(eval_gettext "Fetched in submodule path '\$displaypath', but it did not contain $sha1. Direct fetching of that commit failed.")"
fi
- # Is this something we just cloned?
- case ";$cloned_modules;" in
- *";$name;"*)
- # then there is no local change to integrate
- update_module=checkout ;;
- esac
-
must_die_on_failure=
case "$update_module" in
checkout)
@@ -810,7 +702,7 @@ Maybe you want to use 'update --init'?")"
die "$(eval_gettext "Invalid update mode '$update_module' for submodule '$name'")"
esac
- if (clear_local_git_env; cd "$sm_path" && $command "$sha1")
+ if (sanitize_submodule_env; cd "$sm_path" && $command "$sha1")
then
say "$say_msg"
elif test -n "$must_die_on_failure"
@@ -825,8 +717,9 @@ Maybe you want to use 'update --init'?")"
if test -n "$recursive"
then
(
- prefix="$prefix$sm_path/"
- clear_local_git_env
+ prefix=$(relative_path "$prefix$sm_path/")
+ wt_prefix=
+ sanitize_submodule_env
cd "$sm_path" &&
eval cmd_update
)
@@ -864,7 +757,7 @@ Maybe you want to use 'update --init'?")"
set_name_rev () {
revname=$( (
- clear_local_git_env
+ sanitize_submodule_env
cd "$1" && {
git describe "$2" 2>/dev/null ||
git describe --tags "$2" 2>/dev/null ||
@@ -1148,7 +1041,7 @@ cmd_status()
else
if test -z "$cached"
then
- sha1=$(clear_local_git_env; cd "$sm_path" && git rev-parse --verify HEAD)
+ sha1=$(sanitize_submodule_env; cd "$sm_path" && git rev-parse --verify HEAD)
fi
set_name_rev "$sm_path" "$sha1"
say "+$sha1 $displaypath$revname"
@@ -1158,7 +1051,7 @@ cmd_status()
then
(
prefix="$displaypath/"
- clear_local_git_env
+ sanitize_submodule_env
wt_prefix=
cd "$sm_path" &&
eval cmd_status
@@ -1214,9 +1107,9 @@ cmd_sync()
# guarantee a trailing /
up_path=${up_path%/}/ &&
# path from submodule work tree to submodule origin repo
- sub_origin_url=$(resolve_relative_url "$url" "$up_path") &&
+ sub_origin_url=$(git submodule--helper resolve-relative-url "$url" "$up_path") &&
# path from superproject work tree to submodule origin repo
- super_config_url=$(resolve_relative_url "$url") || exit
+ super_config_url=$(git submodule--helper resolve-relative-url "$url") || exit
;;
*)
sub_origin_url="$url"
@@ -1233,7 +1126,7 @@ cmd_sync()
if test -e "$sm_path"/.git
then
(
- clear_local_git_env
+ sanitize_submodule_env
cd "$sm_path"
remote=$(get_default_remote)
git config remote."$remote".url "$sub_origin_url"
diff --git a/git.spec.in b/git.spec.in
deleted file mode 100644
index bfd1cfb63f..0000000000
--- a/git.spec.in
+++ /dev/null
@@ -1,330 +0,0 @@
-# Pass --without docs to rpmbuild if you don't want the documentation
-
-Name: git
-Version: @@VERSION@@
-Release: 1%{?dist}
-Summary: Core git tools
-License: GPL
-Group: Development/Tools
-URL: http://kernel.org/pub/software/scm/git/
-Source: http://kernel.org/pub/software/scm/git/%{name}-%{version}.tar.gz
-BuildRequires: zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel, gettext %{!?_without_docs:, xmlto, asciidoc > 6.0.3}
-BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-
-Requires: perl-Git = %{version}-%{release}
-Requires: zlib >= 1.2, rsync, less, openssh-clients, expat
-Provides: git-core = %{version}-%{release}
-Obsoletes: git-core <= 1.5.4.2
-Obsoletes: git-p4
-
-%description
-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.
-
-The git rpm installs the core tools with minimal dependencies. To
-install all git packages, including tools for integrating with other
-SCMs, install the git-all meta-package.
-
-%package all
-Summary: Meta-package to pull in all git tools
-Group: Development/Tools
-Requires: git = %{version}-%{release}
-Requires: git-svn = %{version}-%{release}
-Requires: git-cvs = %{version}-%{release}
-Requires: git-arch = %{version}-%{release}
-Requires: git-email = %{version}-%{release}
-Requires: gitk = %{version}-%{release}
-Requires: gitweb = %{version}-%{release}
-Requires: git-gui = %{version}-%{release}
-Obsoletes: git <= 1.5.4.2
-
-%description all
-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.
-
-This is a dummy package which brings in all subpackages.
-
-%package svn
-Summary: Git tools for importing Subversion repositories
-Group: Development/Tools
-Requires: git = %{version}-%{release}, subversion
-%description svn
-Git tools for importing Subversion repositories.
-
-%package cvs
-Summary: Git tools for importing CVS repositories
-Group: Development/Tools
-Requires: git = %{version}-%{release}, cvs, cvsps
-%description cvs
-Git tools for importing CVS repositories.
-
-%package arch
-Summary: Git tools for importing Arch repositories
-Group: Development/Tools
-Requires: git = %{version}-%{release}, tla
-%description arch
-Git tools for importing Arch repositories.
-
-%package email
-Summary: Git tools for sending email
-Group: Development/Tools
-Requires: git = %{version}-%{release}
-%description email
-Git tools for sending email.
-
-%package gui
-Summary: Git GUI tool
-Group: Development/Tools
-Requires: git = %{version}-%{release}, tk >= 8.4
-%description gui
-Git GUI tool
-
-%package -n gitk
-Summary: Git revision tree visualiser ('gitk')
-Group: Development/Tools
-Requires: git = %{version}-%{release}, tk >= 8.4
-%description -n gitk
-Git revision tree visualiser ('gitk')
-
-%package -n gitweb
-Summary: Git web interface
-Group: Development/Tools
-Requires: git = %{version}-%{release}
-%description -n gitweb
-Browsing git repository on the web
-
-%package -n perl-Git
-Summary: Perl interface to Git
-Group: Development/Libraries
-Requires: git = %{version}-%{release}
-Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
-BuildRequires: perl(Error)
-BuildRequires: perl(ExtUtils::MakeMaker)
-
-%description -n perl-Git
-Perl interface to Git
-
-%define path_settings ETC_GITCONFIG=/etc/gitconfig prefix=%{_prefix} mandir=%{_mandir} htmldir=%{_docdir}/%{name}-%{version}
-%{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
-
-%prep
-%setup -q
-
-%build
-make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" \
- %{path_settings} \
- all %{!?_without_docs: doc}
-
-%install
-rm -rf $RPM_BUILD_ROOT
-make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" DESTDIR=$RPM_BUILD_ROOT \
- %{path_settings} \
- INSTALLDIRS=vendor install %{!?_without_docs: install-doc}
-test ! -d $RPM_BUILD_ROOT%{python_sitelib} || rm -fr $RPM_BUILD_ROOT%{python_sitelib}
-find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} ';'
-find $RPM_BUILD_ROOT -type f -name '*.bs' -empty -exec rm -f {} ';'
-find $RPM_BUILD_ROOT -type f -name perllocal.pod -exec rm -f {} ';'
-
-(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "archimport|svn|cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@) > bin-man-doc-files
-(find $RPM_BUILD_ROOT%{_libexecdir}/git-core -type f | grep -vE "archimport|svn|cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@) >> bin-man-doc-files
-(find $RPM_BUILD_ROOT%{perl_vendorlib} -type f | sed -e s@^$RPM_BUILD_ROOT@@) >> perl-files
-%if %{!?_without_docs:1}0
-(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "archimport|svn|git-cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
-%else
-rm -rf $RPM_BUILD_ROOT%{_mandir}
-%endif
-rm -rf $RPM_BUILD_ROOT%{_datadir}/locale
-
-mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d
-install -m 644 -T contrib/completion/git-completion.bash $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/git
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-
-%files -f bin-man-doc-files
-%defattr(-,root,root)
-%{_datadir}/git-core/
-%doc README.md COPYING Documentation/*.txt
-%{!?_without_docs: %doc Documentation/*.html Documentation/howto}
-%{!?_without_docs: %doc Documentation/technical}
-%{_sysconfdir}/bash_completion.d
-
-%files svn
-%defattr(-,root,root)
-%{_libexecdir}/git-core/*svn*
-%doc Documentation/*svn*.txt
-%{!?_without_docs: %{_mandir}/man1/*svn*.1*}
-%{!?_without_docs: %doc Documentation/*svn*.html }
-
-%files cvs
-%defattr(-,root,root)
-%doc Documentation/*git-cvs*.txt
-%{_bindir}/git-cvsserver
-%{_libexecdir}/git-core/*cvs*
-%{!?_without_docs: %{_mandir}/man1/*cvs*.1*}
-%{!?_without_docs: %doc Documentation/*git-cvs*.html }
-
-%files arch
-%defattr(-,root,root)
-%doc Documentation/git-archimport.txt
-%{_libexecdir}/git-core/git-archimport
-%{!?_without_docs: %{_mandir}/man1/git-archimport.1*}
-%{!?_without_docs: %doc Documentation/git-archimport.html }
-
-%files email
-%defattr(-,root,root)
-%doc Documentation/*email*.txt
-%{_libexecdir}/git-core/*email*
-%{!?_without_docs: %{_mandir}/man1/*email*.1*}
-%{!?_without_docs: %doc Documentation/*email*.html }
-
-%files gui
-%defattr(-,root,root)
-%{_libexecdir}/git-core/git-gui
-%{_libexecdir}/git-core/git-citool
-%{_libexecdir}/git-core/git-gui--askpass
-%{_datadir}/git-gui/
-%{!?_without_docs: %{_mandir}/man1/git-gui.1*}
-%{!?_without_docs: %doc Documentation/git-gui.html}
-%{!?_without_docs: %{_mandir}/man1/git-citool.1*}
-%{!?_without_docs: %doc Documentation/git-citool.html}
-
-%files -n gitk
-%defattr(-,root,root)
-%doc Documentation/*gitk*.txt
-%{_bindir}/*gitk*
-%{_datadir}/gitk/
-%{!?_without_docs: %{_mandir}/man1/*gitk*.1*}
-%{!?_without_docs: %doc Documentation/*gitk*.html }
-
-%files -n gitweb
-%defattr(-,root,root)
-%doc gitweb/README gitweb/INSTALL Documentation/*gitweb*.txt
-%{_datadir}/gitweb
-%{!?_without_docs: %{_mandir}/man1/*gitweb*.1*}
-%{!?_without_docs: %{_mandir}/man5/*gitweb*.5*}
-%{!?_without_docs: %doc Documentation/*gitweb*.html }
-
-%files -n perl-Git -f perl-files
-%defattr(-,root,root)
-
-%files all
-# No files for you!
-
-%changelog
-* Sun Sep 18 2011 Jakub Narebski <jnareb@gmail.com>
-- Add gitweb manpages to 'gitweb' subpackage
-
-* Wed Jun 30 2010 Junio C Hamano <gitster@pobox.com>
-- Add 'gitweb' subpackage.
-
-* Fri Mar 26 2010 Ian Ward Comfort <icomfort@stanford.edu>
-- Ship bash completion support from contrib/ in the core package.
-
-* Sun Jan 31 2010 Junio C Hamano <gitster@pobox.com>
-- Do not use %define inside %{!?...} construct.
-
-* Sat Jan 30 2010 Junio C Hamano <gitster@pobox.com>
-- We don't ship Python bits until a real foreign scm interface comes.
-
-* Mon Feb 04 2009 David J. Mellor <dmellor@whistlingcat.com>
-- fixed broken git help -w after renaming the git-core package to git.
-
-* Fri Sep 12 2008 Quy Tonthat <qtonthat@gmail.com>
-- move git-cvsserver to bindir.
-
-* Sun Jun 15 2008 Junio C Hamano <gitster@pobox.com>
-- Remove curl from Requires list.
-
-* Fri Feb 15 2008 Kristian HΓΈgsberg <krh@redhat.com>
-- Rename git-core to just git and rename meta package from git to git-all.
-
-* Sun Feb 03 2008 James Bowes <jbowes@dangerouslyinc.com>
-- Add a BuildRequires for gettext
-
-* Fri Jan 11 2008 Junio C Hamano <gitster@pobox.com>
-- Include gitk message files
-
-* Sun Jan 06 2008 James Bowes <jbowes@dangerouslyinc.com>
-- Make the metapackage require the same version of the subpackages.
-
-* Wed Dec 12 2007 Junio C Hamano <gitster@pobox.com>
-- Adjust htmldir to point at /usr/share/doc/git-core-$version/
-
-* Sun Jul 15 2007 Sean Estabrooks <seanlkml@sympatico.ca>
-- Removed p4import.
-
-* Tue Jun 26 2007 Quy Tonthat <qtonthat@gmail.com>
-- Fixed problems looking for wrong manpages.
-
-* Thu Jun 21 2007 Shawn O. Pearce <spearce@spearce.org>
-- Added documentation files for git-gui
-
-* Tue May 13 2007 Quy Tonthat <qtonthat@gmail.com>
-- Added lib files for git-gui
-- Added Documentation/technical (As needed by Git Users Manual)
-
-* Tue May 8 2007 Quy Tonthat <qtonthat@gmail.com>
-- Added howto files
-
-* Tue Mar 27 2007 Eygene Ryabinkin <rea-git@codelabs.ru>
-- Added the git-p4 package: Perforce import stuff.
-
-* Mon Feb 13 2007 Nicolas Pitre <nico@fluxnic.net>
-- Update core package description (Git isn't as stupid as it used to be)
-
-* Mon Feb 12 2007 Junio C Hamano <junkio@cox.net>
-- Add git-gui and git-citool.
-
-* Mon Nov 14 2005 H. Peter Anvin <hpa@zytor.com> 0.99.9j-1
-- Change subpackage names to git-<name> instead of git-core-<name>
-- Create empty root package which brings in all subpackages
-- Rename git-tk -> gitk
-
-* Thu Nov 10 2005 Chris Wright <chrisw@osdl.org> 0.99.9g-1
-- zlib dependency fix
-- Minor cleanups from split
-- Move arch import to separate package as well
-
-* Tue Sep 27 2005 Jim Radford <radford@blackbean.org>
-- Move programs with non-standard dependencies (svn, cvs, email)
- into separate packages
-
-* Tue Sep 27 2005 H. Peter Anvin <hpa@zytor.com>
-- parallelize build
-- COPTS -> CFLAGS
-
-* Fri Sep 16 2005 Chris Wright <chrisw@osdl.org> 0.99.6-1
-- update to 0.99.6
-
-* Fri Sep 16 2005 Horst H. von Brand <vonbrand@inf.utfsm.cl>
-- Linus noticed that less is required, added to the dependencies
-
-* Sun Sep 11 2005 Horst H. von Brand <vonbrand@inf.utfsm.cl>
-- Updated dependencies
-- Don't assume manpages are gzipped
-
-* Thu Aug 18 2005 Chris Wright <chrisw@osdl.org> 0.99.4-4
-- drop sh_utils, sh-utils, diffutils, mktemp, and openssl Requires
-- use RPM_OPT_FLAGS in spec file, drop patch0
-
-* Wed Aug 17 2005 Tom "spot" Callaway <tcallawa@redhat.com> 0.99.4-3
-- use dist tag to differentiate between branches
-- use rpm optflags by default (patch0)
-- own %{_datadir}/git-core/
-
-* Mon Aug 15 2005 Chris Wright <chrisw@osdl.org>
-- update spec file to fix Buildroot, Requires, and drop Vendor
-
-* Sun Aug 07 2005 Horst H. von Brand <vonbrand@inf.utfsm.cl>
-- Redid the description
-- Cut overlong make line, loosened changelog a bit
-- I think Junio (or perhaps OSDL?) should be vendor...
-
-* Thu Jul 14 2005 Eric Biederman <ebiederm@xmission.com>
-- Add the man pages, and the --without docs build option
-
-* Wed Jul 7 2005 Chris Wright <chris@osdl.org>
-- initial git spec file
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 05d7910b7c..2fddf750fa 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3935,6 +3935,9 @@ sub run_highlighter {
close $fd;
open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ".
+ quote_command($^X, '-CO', '-MEncode=decode,FB_DEFAULT', '-pse',
+ '$_ = decode($fe, $_, FB_DEFAULT) if !utf8::decode($_);',
+ '--', "-fe=$fallback_encoding")." | ".
quote_command($highlight_bin).
" --replace-tabs=8 --fragment --syntax $syntax |"
or die_error(500, "Couldn't open file or run syntax highlighter");
diff --git a/gpg-interface.c b/gpg-interface.c
index 3dc2fe397e..c4b1e8c78d 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -219,11 +219,9 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
args_gpg[0] = gpg_program;
fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
if (fd < 0)
- return error(_("could not create temporary file '%s': %s"),
- path, strerror(errno));
+ return error_errno(_("could not create temporary file '%s'"), path);
if (write_in_full(fd, signature, signature_size) < 0)
- return error(_("failed writing detached signature to '%s': %s"),
- path, strerror(errno));
+ return error_errno(_("failed writing detached signature to '%s'"), path);
close(fd);
gpg.argv = args_gpg;
@@ -237,6 +235,7 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
return error(_("could not run gpg."));
}
+ sigchain_push(SIGPIPE, SIG_IGN);
write_in_full(gpg.in, payload, payload_size);
close(gpg.in);
@@ -250,6 +249,7 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
close(gpg.out);
ret = finish_command(&gpg);
+ sigchain_pop(SIGPIPE);
unlink_or_warn(path);
diff --git a/grep.c b/grep.c
index 528b652f71..ec6f7ffa19 100644
--- a/grep.c
+++ b/grep.c
@@ -1732,7 +1732,7 @@ static int grep_source_load_file(struct grep_source *gs)
if (lstat(filename, &st) < 0) {
err_ret:
if (errno != ENOENT)
- error(_("'%s': %s"), filename, strerror(errno));
+ error_errno(_("failed to stat '%s'"), filename);
return -1;
}
if (!S_ISREG(st.st_mode))
@@ -1743,7 +1743,7 @@ static int grep_source_load_file(struct grep_source *gs)
goto err_ret;
data = xmallocz(size);
if (st.st_size != read_in_full(i, data, size)) {
- error(_("'%s': short read %s"), filename, strerror(errno));
+ error_errno(_("'%s': short read"), filename);
close(i);
free(data);
return -1;
diff --git a/http-backend.c b/http-backend.c
index 8870a2681e..214881459d 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -484,9 +484,9 @@ static int show_head_ref(const char *refname, const struct object_id *oid,
const char *target = resolve_ref_unsafe(refname,
RESOLVE_REF_READING,
unused.hash, NULL);
- const char *target_nons = strip_namespace(target);
- strbuf_addf(buf, "ref: %s\n", target_nons);
+ if (target)
+ strbuf_addf(buf, "ref: %s\n", strip_namespace(target));
} else {
strbuf_addf(buf, "%s\n", oid_to_hex(oid));
}
diff --git a/http-push.c b/http-push.c
index bd60668707..a092f0288b 100644
--- a/http-push.c
+++ b/http-push.c
@@ -211,7 +211,7 @@ static void curl_setup_http(CURL *curl, const char *url,
static struct curl_slist *get_dav_token_headers(struct remote_lock *lock, enum dav_header_flag options)
{
struct strbuf buf = STRBUF_INIT;
- struct curl_slist *dav_headers = NULL;
+ struct curl_slist *dav_headers = http_copy_default_headers();
if (options & DAV_HEADER_IF) {
strbuf_addf(&buf, "If: (<%s>)", lock->token);
@@ -417,7 +417,7 @@ static void start_put(struct transfer_request *request)
static void start_move(struct transfer_request *request)
{
struct active_request_slot *slot;
- struct curl_slist *dav_headers = NULL;
+ struct curl_slist *dav_headers = http_copy_default_headers();
slot = get_active_slot();
slot->callback_func = process_response;
@@ -845,7 +845,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
char *ep;
char timeout_header[25];
struct remote_lock *lock = NULL;
- struct curl_slist *dav_headers = NULL;
+ struct curl_slist *dav_headers = http_copy_default_headers();
struct xml_ctx ctx;
char *escaped;
@@ -1126,7 +1126,7 @@ static void remote_ls(const char *path, int flags,
struct slot_results results;
struct strbuf in_buffer = STRBUF_INIT;
struct buffer out_buffer = { STRBUF_INIT, 0 };
- struct curl_slist *dav_headers = NULL;
+ struct curl_slist *dav_headers = http_copy_default_headers();
struct xml_ctx ctx;
struct remote_ls_ctx ls;
@@ -1204,7 +1204,7 @@ static int locking_available(void)
struct slot_results results;
struct strbuf in_buffer = STRBUF_INIT;
struct buffer out_buffer = { STRBUF_INIT, 0 };
- struct curl_slist *dav_headers = NULL;
+ struct curl_slist *dav_headers = http_copy_default_headers();
struct xml_ctx ctx;
int lock_flags = 0;
char *escaped;
@@ -1312,10 +1312,10 @@ static struct object_list **process_tree(struct tree *tree,
while (tree_entry(&desc, &entry))
switch (object_type(entry.mode)) {
case OBJ_TREE:
- p = process_tree(lookup_tree(entry.sha1), p);
+ p = process_tree(lookup_tree(entry.oid->hash), p);
break;
case OBJ_BLOB:
- p = process_blob(lookup_blob(entry.sha1), p);
+ p = process_blob(lookup_blob(entry.oid->hash), p);
break;
default:
/* Subproject commit - not in this repository */
diff --git a/http.c b/http.c
index 4304b80ad3..df6dd01594 100644
--- a/http.c
+++ b/http.c
@@ -114,6 +114,7 @@ static unsigned long http_auth_methods = CURLAUTH_ANY;
static struct curl_slist *pragma_header;
static struct curl_slist *no_pragma_header;
+static struct curl_slist *extra_http_headers;
static struct active_request_slot *active_queue_head;
@@ -293,7 +294,7 @@ static int http_options(const char *var, const char *value, void *cb)
return git_config_string(&http_proxy_authmethod, var, value);
if (!strcmp("http.cookiefile", var))
- return git_config_string(&curl_cookie_file, var, value);
+ return git_config_pathname(&curl_cookie_file, var, value);
if (!strcmp("http.savecookies", var)) {
curl_save_cookies = git_config_bool(var, value);
return 0;
@@ -323,6 +324,19 @@ static int http_options(const char *var, const char *value, void *cb)
#endif
}
+ if (!strcmp("http.extraheader", var)) {
+ if (!value) {
+ return config_error_nonbool(var);
+ } else if (!*value) {
+ curl_slist_free_all(extra_http_headers);
+ extra_http_headers = NULL;
+ } else {
+ extra_http_headers =
+ curl_slist_append(extra_http_headers, value);
+ }
+ return 0;
+ }
+
/* Fall back on the default ones */
return git_default_config(var, value, cb);
}
@@ -446,8 +460,7 @@ static int sockopt_callback(void *client, curl_socket_t fd, curlsocktype type)
rc = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&ka, len);
if (rc < 0)
- warning("unable to set SO_KEEPALIVE on socket %s",
- strerror(errno));
+ warning_errno("unable to set SO_KEEPALIVE on socket");
return 0; /* CURL_SOCKOPT_OK only exists since curl 7.21.5 */
}
@@ -678,8 +691,10 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
if (remote)
var_override(&http_proxy_authmethod, remote->http_proxy_authmethod);
- pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
- no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
+ pragma_header = curl_slist_append(http_copy_default_headers(),
+ "Pragma: no-cache");
+ no_pragma_header = curl_slist_append(http_copy_default_headers(),
+ "Pragma:");
#ifdef USE_CURL_MULTI
{
@@ -765,6 +780,9 @@ void http_cleanup(void)
#endif
curl_global_cleanup();
+ curl_slist_free_all(extra_http_headers);
+ extra_http_headers = NULL;
+
curl_slist_free_all(pragma_header);
pragma_header = NULL;
@@ -1163,6 +1181,16 @@ int run_one_slot(struct active_request_slot *slot,
return handle_curl_result(results);
}
+struct curl_slist *http_copy_default_headers(void)
+{
+ struct curl_slist *headers = NULL, *h;
+
+ for (h = extra_http_headers; h; h = h->next)
+ headers = curl_slist_append(headers, h->data);
+
+ return headers;
+}
+
static CURLcode curlinfo_strbuf(CURL *curl, CURLINFO info, struct strbuf *buf)
{
char *ptr;
@@ -1380,7 +1408,7 @@ static int http_request(const char *url,
{
struct active_request_slot *slot;
struct slot_results results;
- struct curl_slist *headers = NULL;
+ struct curl_slist *headers = http_copy_default_headers();
struct strbuf buf = STRBUF_INIT;
const char *accept_language;
int ret;
@@ -1894,8 +1922,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
}
if (freq->localfile < 0) {
- error("Couldn't create temporary file %s: %s",
- freq->tmpfile, strerror(errno));
+ error_errno("Couldn't create temporary file %s", freq->tmpfile);
goto abort;
}
@@ -1940,8 +1967,8 @@ struct http_object_request *new_http_object_request(const char *base_url,
prev_posn = 0;
lseek(freq->localfile, 0, SEEK_SET);
if (ftruncate(freq->localfile, 0) < 0) {
- error("Couldn't truncate temporary file %s: %s",
- freq->tmpfile, strerror(errno));
+ error_errno("Couldn't truncate temporary file %s",
+ freq->tmpfile);
goto abort;
}
}
diff --git a/http.h b/http.h
index 4ef4bbda7d..36f558bfb3 100644
--- a/http.h
+++ b/http.h
@@ -106,6 +106,7 @@ extern void step_active_slots(void);
extern void http_init(struct remote *remote, const char *url,
int proactive_auth);
extern void http_cleanup(void);
+extern struct curl_slist *http_copy_default_headers(void);
extern long int git_curl_ipresolve;
extern int active_requests;
diff --git a/ident.c b/ident.c
index 6e125821f0..139c5289d0 100644
--- a/ident.c
+++ b/ident.c
@@ -75,14 +75,12 @@ static int add_mailname_host(struct strbuf *buf)
mailname = fopen("/etc/mailname", "r");
if (!mailname) {
if (errno != ENOENT)
- warning("cannot open /etc/mailname: %s",
- strerror(errno));
+ warning_errno("cannot open /etc/mailname");
return -1;
}
if (strbuf_getline(&mailnamebuf, mailname) == EOF) {
if (ferror(mailname))
- warning("cannot read /etc/mailname: %s",
- strerror(errno));
+ warning_errno("cannot read /etc/mailname");
strbuf_release(&mailnamebuf);
fclose(mailname);
return -1;
@@ -125,7 +123,7 @@ static void add_domainname(struct strbuf *out, int *is_bogus)
char buf[1024];
if (gethostname(buf, sizeof(buf))) {
- warning("cannot get host name: %s", strerror(errno));
+ warning_errno("cannot get host name");
strbuf_addstr(out, "(none)");
*is_bogus = 1;
return;
@@ -351,15 +349,17 @@ const char *fmt_ident(const char *name, const char *email,
if (want_name) {
int using_default = 0;
if (!name) {
+ if (strict && ident_use_config_only
+ && !(ident_config_given & IDENT_NAME_GIVEN)) {
+ fputs(env_hint, stderr);
+ die("no name was given and auto-detection is disabled");
+ }
name = ident_default_name();
using_default = 1;
if (strict && default_name_is_bogus) {
fputs(env_hint, stderr);
die("unable to auto-detect name (got '%s')", name);
}
- if (strict && ident_use_config_only
- && !(ident_config_given & IDENT_NAME_GIVEN))
- die("user.useConfigOnly set but no name given");
}
if (!*name) {
struct passwd *pw;
@@ -374,14 +374,16 @@ const char *fmt_ident(const char *name, const char *email,
}
if (!email) {
+ if (strict && ident_use_config_only
+ && !(ident_config_given & IDENT_MAIL_GIVEN)) {
+ fputs(env_hint, stderr);
+ die("no email was given and auto-detection is disabled");
+ }
email = ident_default_email();
if (strict && default_email_is_bogus) {
fputs(env_hint, stderr);
die("unable to auto-detect email address (got '%s')", email);
}
- if (strict && ident_use_config_only
- && !(ident_config_given & IDENT_MAIL_GIVEN))
- die("user.useConfigOnly set but no mail given");
}
strbuf_reset(&ident);
diff --git a/list-objects.c b/list-objects.c
index 917cc5d7c9..f3ca6aafb7 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -110,16 +110,16 @@ static void process_tree(struct rev_info *revs,
if (S_ISDIR(entry.mode))
process_tree(revs,
- lookup_tree(entry.sha1),
+ lookup_tree(entry.oid->hash),
show, base, entry.path,
cb_data);
else if (S_ISGITLINK(entry.mode))
- process_gitlink(revs, entry.sha1,
+ process_gitlink(revs, entry.oid->hash,
show, base, entry.path,
cb_data);
else
process_blob(revs,
- lookup_blob(entry.sha1),
+ lookup_blob(entry.oid->hash),
show, base, entry.path,
cb_data);
}
diff --git a/ll-merge.c b/ll-merge.c
index ff4a43a982..ad8be42f91 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -47,7 +47,9 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
assert(opts);
/*
- * The tentative merge result is the or common ancestor for an internal merge.
+ * The tentative merge result is the common ancestor for an
+ * internal merge. For the final merge, it is "ours" by
+ * default but -Xours/-Xtheirs can tweak the choice.
*/
if (opts->virtual_ancestor) {
stolen = orig;
@@ -383,8 +385,12 @@ int ll_merge(mmbuffer_t *result_buf,
}
}
driver = find_ll_merge_driver(ll_driver_name);
- if (opts->virtual_ancestor && driver->recursive)
- driver = find_ll_merge_driver(driver->recursive);
+
+ if (opts->virtual_ancestor) {
+ if (driver->recursive)
+ driver = find_ll_merge_driver(driver->recursive);
+ marker_size += 2;
+ }
return driver->fn(driver, result_buf, path, ancestor, ancestor_label,
ours, our_label, theirs, their_label,
opts, marker_size);
diff --git a/log-tree.c b/log-tree.c
index 60f983934d..78a5381d0e 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -683,6 +683,7 @@ void show_log(struct rev_info *opt)
ctx.fmt = opt->commit_format;
ctx.mailmap = opt->mailmap;
ctx.color = opt->diffopt.use_color;
+ ctx.expand_tabs_in_log = opt->expand_tabs_in_log;
ctx.output_encoding = get_log_output_encoding();
if (opt->from_ident.mail_begin && opt->from_ident.name_begin)
ctx.from_ident = &opt->from_ident;
diff --git a/mailmap.c b/mailmap.c
index 972623709f..b5c521fdea 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -189,8 +189,7 @@ static int read_mailmap_file(struct string_list *map, const char *filename,
if (!f) {
if (errno == ENOENT)
return 0;
- return error("unable to open mailmap at %s: %s",
- filename, strerror(errno));
+ return error_errno("unable to open mailmap at %s", filename);
}
while (fgets(buffer, sizeof(buffer), f) != NULL)
diff --git a/match-trees.c b/match-trees.c
index 1ce0954a3e..396b7338df 100644
--- a/match-trees.c
+++ b/match-trees.c
@@ -48,17 +48,17 @@ static int score_matches(unsigned mode1, unsigned mode2, const char *path)
}
static void *fill_tree_desc_strict(struct tree_desc *desc,
- const unsigned char *hash)
+ const struct object_id *hash)
{
void *buffer;
enum object_type type;
unsigned long size;
- buffer = read_sha1_file(hash, &type, &size);
+ buffer = read_sha1_file(hash->hash, &type, &size);
if (!buffer)
- die("unable to read tree (%s)", sha1_to_hex(hash));
+ die("unable to read tree (%s)", oid_to_hex(hash));
if (type != OBJ_TREE)
- die("%s is not a tree", sha1_to_hex(hash));
+ die("%s is not a tree", oid_to_hex(hash));
init_tree_desc(desc, buffer, size);
return buffer;
}
@@ -73,7 +73,7 @@ static int base_name_entries_compare(const struct name_entry *a,
/*
* Inspect two trees, and give a score that tells how similar they are.
*/
-static int score_trees(const unsigned char *hash1, const unsigned char *hash2)
+static int score_trees(const struct object_id *hash1, const struct object_id *hash2)
{
struct tree_desc one;
struct tree_desc two;
@@ -104,7 +104,7 @@ static int score_trees(const unsigned char *hash1, const unsigned char *hash2)
else if (cmp > 0)
/* path2 does not appear in one */
score += score_missing(e2.mode, e2.path);
- else if (hashcmp(e1.sha1, e2.sha1))
+ else if (oidcmp(e1.oid, e2.oid))
/* they are different */
score += score_differs(e1.mode, e2.mode, e1.path);
else
@@ -119,8 +119,8 @@ static int score_trees(const unsigned char *hash1, const unsigned char *hash2)
/*
* Match one itself and its subtrees with two and pick the best match.
*/
-static void match_trees(const unsigned char *hash1,
- const unsigned char *hash2,
+static void match_trees(const struct object_id *hash1,
+ const struct object_id *hash2,
int *best_score,
char **best_match,
const char *base,
@@ -131,7 +131,7 @@ static void match_trees(const unsigned char *hash1,
while (one.size) {
const char *path;
- const unsigned char *elem;
+ const struct object_id *elem;
unsigned mode;
int score;
@@ -191,15 +191,15 @@ static int splice_tree(const unsigned char *hash1,
while (desc.size) {
const char *name;
unsigned mode;
- const unsigned char *sha1;
+ const struct object_id *oid;
- sha1 = tree_entry_extract(&desc, &name, &mode);
+ oid = tree_entry_extract(&desc, &name, &mode);
if (strlen(name) == toplen &&
!memcmp(name, prefix, toplen)) {
if (!S_ISDIR(mode))
die("entry %s in tree %s is not a tree",
name, sha1_to_hex(hash1));
- rewrite_here = (unsigned char *) sha1;
+ rewrite_here = (unsigned char *) oid->hash;
break;
}
update_tree_entry(&desc);
@@ -229,9 +229,9 @@ static int splice_tree(const unsigned char *hash1,
* other hand, it could cover tree one and we might need to pick a
* subtree of it.
*/
-void shift_tree(const unsigned char *hash1,
- const unsigned char *hash2,
- unsigned char *shifted,
+void shift_tree(const struct object_id *hash1,
+ const struct object_id *hash2,
+ struct object_id *shifted,
int depth_limit)
{
char *add_prefix;
@@ -262,7 +262,7 @@ void shift_tree(const unsigned char *hash1,
match_trees(hash2, hash1, &del_score, &del_prefix, "", depth_limit);
/* Assume we do not have to do any shifting */
- hashcpy(shifted, hash2);
+ oidcpy(shifted, hash2);
if (add_score < del_score) {
/* We need to pick a subtree of two */
@@ -271,16 +271,16 @@ void shift_tree(const unsigned char *hash1,
if (!*del_prefix)
return;
- if (get_tree_entry(hash2, del_prefix, shifted, &mode))
+ if (get_tree_entry(hash2->hash, del_prefix, shifted->hash, &mode))
die("cannot find path %s in tree %s",
- del_prefix, sha1_to_hex(hash2));
+ del_prefix, oid_to_hex(hash2));
return;
}
if (!*add_prefix)
return;
- splice_tree(hash1, add_prefix, hash2, shifted);
+ splice_tree(hash1->hash, add_prefix, hash2->hash, shifted->hash);
}
/*
@@ -288,22 +288,22 @@ void shift_tree(const unsigned char *hash1,
* Unfortunately we cannot fundamentally tell which one to
* be prefixed, as recursive merge can work in either direction.
*/
-void shift_tree_by(const unsigned char *hash1,
- const unsigned char *hash2,
- unsigned char *shifted,
+void shift_tree_by(const struct object_id *hash1,
+ const struct object_id *hash2,
+ struct object_id *shifted,
const char *shift_prefix)
{
- unsigned char sub1[20], sub2[20];
+ struct object_id sub1, sub2;
unsigned mode1, mode2;
unsigned candidate = 0;
/* Can hash2 be a tree at shift_prefix in tree hash1? */
- if (!get_tree_entry(hash1, shift_prefix, sub1, &mode1) &&
+ if (!get_tree_entry(hash1->hash, shift_prefix, sub1.hash, &mode1) &&
S_ISDIR(mode1))
candidate |= 1;
/* Can hash1 be a tree at shift_prefix in tree hash2? */
- if (!get_tree_entry(hash2, shift_prefix, sub2, &mode2) &&
+ if (!get_tree_entry(hash2->hash, shift_prefix, sub2.hash, &mode2) &&
S_ISDIR(mode2))
candidate |= 2;
@@ -313,19 +313,19 @@ void shift_tree_by(const unsigned char *hash1,
int score;
candidate = 0;
- score = score_trees(sub1, hash2);
+ score = score_trees(&sub1, hash2);
if (score > best_score) {
candidate = 1;
best_score = score;
}
- score = score_trees(sub2, hash1);
+ score = score_trees(&sub2, hash1);
if (score > best_score)
candidate = 2;
}
if (!candidate) {
/* Neither is plausible -- do not shift */
- hashcpy(shifted, hash2);
+ oidcpy(shifted, hash2);
return;
}
@@ -334,11 +334,11 @@ void shift_tree_by(const unsigned char *hash1,
* shift tree2 down by adding shift_prefix above it
* to match tree1.
*/
- splice_tree(hash1, shift_prefix, hash2, shifted);
+ splice_tree(hash1->hash, shift_prefix, hash2->hash, shifted->hash);
else
/*
* shift tree2 up by removing shift_prefix from it
* to match tree1.
*/
- hashcpy(shifted, sub2);
+ oidcpy(shifted, &sub2);
}
diff --git a/merge-recursive.c b/merge-recursive.c
index b880ae50e7..65cb5d6c1f 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -29,9 +29,9 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two,
struct object_id shifted;
if (!*subtree_shift) {
- shift_tree(one->object.oid.hash, two->object.oid.hash, shifted.hash, 0);
+ shift_tree(&one->object.oid, &two->object.oid, &shifted, 0);
} else {
- shift_tree_by(one->object.oid.hash, two->object.oid.hash, shifted.hash,
+ shift_tree_by(&one->object.oid, &two->object.oid, &shifted,
subtree_shift);
}
if (!oidcmp(&two->object.oid, &shifted))
@@ -622,7 +622,7 @@ static char *unique_path(struct merge_options *o, const char *path, const char *
base_len = newpath.len;
while (string_list_has_string(&o->current_file_set, newpath.buf) ||
string_list_has_string(&o->current_directory_set, newpath.buf) ||
- file_exists(newpath.buf)) {
+ (!o->call_depth && file_exists(newpath.buf))) {
strbuf_setlen(&newpath, base_len);
strbuf_addf(&newpath, "_%d", suffix++);
}
@@ -1234,8 +1234,8 @@ static void conflict_rename_rename_2to1(struct merge_options *o,
a->path, c1->path, ci->branch1,
b->path, c2->path, ci->branch2);
- remove_file(o, 1, a->path, would_lose_untracked(a->path));
- remove_file(o, 1, b->path, would_lose_untracked(b->path));
+ remove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));
+ remove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));
mfi_c1 = merge_file_special_markers(o, a, c1, &ci->ren1_other,
o->branch1, c1->path,
@@ -1773,8 +1773,6 @@ static int process_entry(struct merge_options *o,
output(o, 1, _("CONFLICT (%s): There is a directory with name %s in %s. "
"Adding %s as %s"),
conf, path, other_branch, path, new_path);
- if (o->call_depth)
- remove_file_from_cache(path);
update_file(o, 0, sha, mode, new_path);
if (o->call_depth)
remove_file_from_cache(path);
diff --git a/mergetools/examdiff b/mergetools/examdiff
new file mode 100644
index 0000000000..7b524d4088
--- /dev/null
+++ b/mergetools/examdiff
@@ -0,0 +1,18 @@
+diff_cmd () {
+ "$merge_tool_path" "$LOCAL" "$REMOTE" -nh
+}
+
+merge_cmd () {
+ touch "$BACKUP"
+ if $base_present
+ then
+ "$merge_tool_path" -merge "$LOCAL" "$BASE" "$REMOTE" -o:"$MERGED" -nh
+ else
+ "$merge_tool_path" -merge "$LOCAL" "$REMOTE" -o:"$MERGED" -nh
+ fi
+ check_unchanged
+}
+
+translate_merge_tool_path() {
+ mergetool_find_win32_cmd "ExamDiff.com" "ExamDiff Pro"
+}
diff --git a/mergetools/winmerge b/mergetools/winmerge
index 74a66d4e8d..f3819d316a 100644
--- a/mergetools/winmerge
+++ b/mergetools/winmerge
@@ -13,24 +13,5 @@ merge_cmd () {
}
translate_merge_tool_path() {
- # Use WinMergeU.exe if it exists in $PATH
- if type -p WinMergeU.exe >/dev/null 2>&1
- then
- printf WinMergeU.exe
- return
- fi
-
- # Look for WinMergeU.exe in the typical locations
- winmerge_exe="WinMerge/WinMergeU.exe"
- for directory in $(env | grep -Ei '^PROGRAM(FILES(\(X86\))?|W6432)=' |
- cut -d '=' -f 2- | sort -u)
- do
- if test -n "$directory" && test -x "$directory/$winmerge_exe"
- then
- printf '%s' "$directory/$winmerge_exe"
- return
- fi
- done
-
- printf WinMergeU.exe
+ mergetool_find_win32_cmd "WinMergeU.exe" "WinMerge"
}
diff --git a/notes.c b/notes.c
index 88cf4747c1..e4e4854d69 100644
--- a/notes.c
+++ b/notes.c
@@ -446,7 +446,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
l = (struct leaf_node *)
xcalloc(1, sizeof(struct leaf_node));
hashcpy(l->key_sha1, object_sha1);
- hashcpy(l->val_sha1, entry.sha1);
+ hashcpy(l->val_sha1, entry.oid->hash);
if (len < 20) {
if (!S_ISDIR(entry.mode) || path_len != 2)
goto handle_non_note; /* not subtree */
@@ -493,7 +493,7 @@ handle_non_note:
}
strbuf_addstr(&non_note_path, entry.path);
add_non_note(t, strbuf_detach(&non_note_path, NULL),
- entry.mode, entry.sha1);
+ entry.mode, entry.oid->hash);
}
}
free(buf);
diff --git a/parse-options.c b/parse-options.c
index 47a9192060..312a85dbde 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -110,6 +110,8 @@ static int get_value(struct parse_opt_ctx_t *p,
return 0;
case OPTION_COUNTUP:
+ if (*(int *)opt->value < 0)
+ *(int *)opt->value = 0;
*(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
return 0;
diff --git a/patch-ids.c b/patch-ids.c
index b7b3e5a1a7..a4d0016664 100644
--- a/patch-ids.c
+++ b/patch-ids.c
@@ -4,7 +4,7 @@
#include "sha1-lookup.h"
#include "patch-ids.h"
-static int commit_patch_id(struct commit *commit, struct diff_options *options,
+int commit_patch_id(struct commit *commit, struct diff_options *options,
unsigned char *sha1)
{
if (commit->parents)
diff --git a/patch-ids.h b/patch-ids.h
index c8c7ca110a..eeb56b307f 100644
--- a/patch-ids.h
+++ b/patch-ids.h
@@ -13,6 +13,8 @@ struct patch_ids {
struct patch_id_bucket *patches;
};
+int commit_patch_id(struct commit *commit, struct diff_options *options,
+ unsigned char *sha1);
int init_patch_ids(struct patch_ids *);
int free_patch_ids(struct patch_ids *);
struct patch_id *add_commit_patch_id(struct commit *, struct patch_ids *);
diff --git a/path.c b/path.c
index bbaea5ab0b..259aeed846 100644
--- a/path.c
+++ b/path.c
@@ -5,6 +5,7 @@
#include "strbuf.h"
#include "string-list.h"
#include "dir.h"
+#include "worktree.h"
static int get_st_mode_bits(const char *path, int *mode)
{
@@ -134,7 +135,7 @@ static struct common_dir common_list[] = {
* definite
* definition
*
- * The trie would look look like:
+ * The trie would look like:
* root: len = 0, children a and d non-NULL, value = NULL.
* a: len = 2, contents = bc, value = (data for "abc")
* d: len = 2, contents = ef, children i non-NULL, value = (data for "def")
@@ -383,10 +384,11 @@ static void adjust_git_path(struct strbuf *buf, int git_dir_len)
update_common_dir(buf, git_dir_len, NULL);
}
-static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
+static void do_git_path(const struct worktree *wt, struct strbuf *buf,
+ const char *fmt, va_list args)
{
int gitdir_len;
- strbuf_addstr(buf, get_git_dir());
+ strbuf_addstr(buf, get_worktree_git_dir(wt));
if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
strbuf_addch(buf, '/');
gitdir_len = buf->len;
@@ -400,7 +402,7 @@ char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
va_list args;
strbuf_reset(buf);
va_start(args, fmt);
- do_git_path(buf, fmt, args);
+ do_git_path(NULL, buf, fmt, args);
va_end(args);
return buf->buf;
}
@@ -409,7 +411,7 @@ void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
- do_git_path(sb, fmt, args);
+ do_git_path(NULL, sb, fmt, args);
va_end(args);
}
@@ -418,7 +420,7 @@ const char *git_path(const char *fmt, ...)
struct strbuf *pathname = get_pathname();
va_list args;
va_start(args, fmt);
- do_git_path(pathname, fmt, args);
+ do_git_path(NULL, pathname, fmt, args);
va_end(args);
return pathname->buf;
}
@@ -428,7 +430,7 @@ char *git_pathdup(const char *fmt, ...)
struct strbuf path = STRBUF_INIT;
va_list args;
va_start(args, fmt);
- do_git_path(&path, fmt, args);
+ do_git_path(NULL, &path, fmt, args);
va_end(args);
return strbuf_detach(&path, NULL);
}
@@ -454,6 +456,16 @@ const char *mkpath(const char *fmt, ...)
return cleanup_path(pathname->buf);
}
+const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)
+{
+ struct strbuf *pathname = get_pathname();
+ va_list args;
+ va_start(args, fmt);
+ do_git_path(wt, pathname, fmt, args);
+ va_end(args);
+ return pathname->buf;
+}
+
static void do_submodule_path(struct strbuf *buf, const char *path,
const char *fmt, va_list args)
{
@@ -503,6 +515,35 @@ void strbuf_git_path_submodule(struct strbuf *buf, const char *path,
va_end(args);
}
+static void do_git_common_path(struct strbuf *buf,
+ const char *fmt,
+ va_list args)
+{
+ strbuf_addstr(buf, get_git_common_dir());
+ if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
+ strbuf_addch(buf, '/');
+ strbuf_vaddf(buf, fmt, args);
+ strbuf_cleanup_path(buf);
+}
+
+const char *git_common_path(const char *fmt, ...)
+{
+ struct strbuf *pathname = get_pathname();
+ va_list args;
+ va_start(args, fmt);
+ do_git_common_path(pathname, fmt, args);
+ va_end(args);
+ return pathname->buf;
+}
+
+void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ do_git_common_path(sb, fmt, args);
+ va_end(args);
+}
+
int validate_headref(const char *path)
{
struct stat st;
diff --git a/perl/Git.pm b/perl/Git.pm
index 49eb88af8d..ce7e4e8da3 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -393,7 +393,7 @@ sub command_close_pipe {
Execute the given C<COMMAND> in the same way as command_output_pipe()
does but return both an input pipe filehandle and an output pipe filehandle.
-The function will return return C<($pid, $pipe_in, $pipe_out, $ctx)>.
+The function will return C<($pid, $pipe_in, $pipe_out, $ctx)>.
See C<command_close_bidi_pipe()> for details.
=cut
diff --git a/perl/Git/SVN.pm b/perl/Git/SVN.pm
index b2c14e2ff5..d94d01cfdc 100644
--- a/perl/Git/SVN.pm
+++ b/perl/Git/SVN.pm
@@ -97,7 +97,8 @@ sub resolve_local_globs {
"existing: $existing\n",
" globbed: $refname\n";
}
- my $u = (::cmt_metadata("$refname"))[0];
+ my $u = (::cmt_metadata("$refname"))[0] or die
+ "$refname: no associated commit metadata\n";
$u =~ s!^\Q$url\E(/|$)!! or die
"$refname: '$url' not found in '$u'\n";
if ($pathname ne $u) {
diff --git a/pretty.c b/pretty.c
index 92b2870a7e..87c44971a1 100644
--- a/pretty.c
+++ b/pretty.c
@@ -16,6 +16,7 @@ static struct cmt_fmt_map {
const char *name;
enum cmit_fmt format;
int is_tformat;
+ int expand_tabs_in_log;
int is_alias;
const char *user_format;
} *commit_formats;
@@ -87,13 +88,13 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c
static void setup_commit_formats(void)
{
struct cmt_fmt_map builtin_formats[] = {
- { "raw", CMIT_FMT_RAW, 0 },
- { "medium", CMIT_FMT_MEDIUM, 0 },
- { "short", CMIT_FMT_SHORT, 0 },
- { "email", CMIT_FMT_EMAIL, 0 },
- { "fuller", CMIT_FMT_FULLER, 0 },
- { "full", CMIT_FMT_FULL, 0 },
- { "oneline", CMIT_FMT_ONELINE, 1 }
+ { "raw", CMIT_FMT_RAW, 0, 0 },
+ { "medium", CMIT_FMT_MEDIUM, 0, 8 },
+ { "short", CMIT_FMT_SHORT, 0, 0 },
+ { "email", CMIT_FMT_EMAIL, 0, 0 },
+ { "fuller", CMIT_FMT_FULLER, 0, 8 },
+ { "full", CMIT_FMT_FULL, 0, 8 },
+ { "oneline", CMIT_FMT_ONELINE, 1, 0 }
};
commit_formats_len = ARRAY_SIZE(builtin_formats);
builtin_formats_len = commit_formats_len;
@@ -172,6 +173,7 @@ void get_commit_format(const char *arg, struct rev_info *rev)
rev->commit_format = commit_format->format;
rev->use_terminator = commit_format->is_tformat;
+ rev->expand_tabs_in_log_default = commit_format->expand_tabs_in_log;
if (commit_format->format == CMIT_FMT_USERFORMAT) {
save_user_format(rev, commit_format->user_format,
commit_format->is_tformat);
@@ -1629,6 +1631,72 @@ void pp_title_line(struct pretty_print_context *pp,
strbuf_release(&title);
}
+static int pp_utf8_width(const char *start, const char *end)
+{
+ int width = 0;
+ size_t remain = end - start;
+
+ while (remain) {
+ int n = utf8_width(&start, &remain);
+ if (n < 0 || !start)
+ return -1;
+ width += n;
+ }
+ return width;
+}
+
+static void strbuf_add_tabexpand(struct strbuf *sb, int tabwidth,
+ const char *line, int linelen)
+{
+ const char *tab;
+
+ while ((tab = memchr(line, '\t', linelen)) != NULL) {
+ int width = pp_utf8_width(line, tab);
+
+ /*
+ * If it wasn't well-formed utf8, or it
+ * had characters with badly defined
+ * width (control characters etc), just
+ * give up on trying to align things.
+ */
+ if (width < 0)
+ break;
+
+ /* Output the data .. */
+ strbuf_add(sb, line, tab - line);
+
+ /* .. and the de-tabified tab */
+ strbuf_addchars(sb, ' ', tabwidth - (width % tabwidth));
+
+ /* Skip over the printed part .. */
+ linelen -= tab + 1 - line;
+ line = tab + 1;
+ }
+
+ /*
+ * Print out everything after the last tab without
+ * worrying about width - there's nothing more to
+ * align.
+ */
+ strbuf_add(sb, line, linelen);
+}
+
+/*
+ * pp_handle_indent() prints out the intendation, and
+ * the whole line (without the final newline), after
+ * de-tabifying.
+ */
+static void pp_handle_indent(struct pretty_print_context *pp,
+ struct strbuf *sb, int indent,
+ const char *line, int linelen)
+{
+ strbuf_addchars(sb, ' ', indent);
+ if (pp->expand_tabs_in_log)
+ strbuf_add_tabexpand(sb, pp->expand_tabs_in_log, line, linelen);
+ else
+ strbuf_add(sb, line, linelen);
+}
+
void pp_remainder(struct pretty_print_context *pp,
const char **msg_p,
struct strbuf *sb,
@@ -1653,8 +1721,12 @@ void pp_remainder(struct pretty_print_context *pp,
strbuf_grow(sb, linelen + indent + 20);
if (indent)
- strbuf_addchars(sb, ' ', indent);
- strbuf_add(sb, line, linelen);
+ pp_handle_indent(pp, sb, indent, line, linelen);
+ else if (pp->expand_tabs_in_log)
+ strbuf_add_tabexpand(sb, pp->expand_tabs_in_log,
+ line, linelen);
+ else
+ strbuf_add(sb, line, linelen);
strbuf_addch(sb, '\n');
}
}
diff --git a/quote.c b/quote.c
index fe884d2452..b281a8fe45 100644
--- a/quote.c
+++ b/quote.c
@@ -43,6 +43,19 @@ void sq_quote_buf(struct strbuf *dst, const char *src)
free(to_free);
}
+void sq_quotef(struct strbuf *dst, const char *fmt, ...)
+{
+ struct strbuf src = STRBUF_INIT;
+
+ va_list ap;
+ va_start(ap, fmt);
+ strbuf_vaddf(&src, fmt, ap);
+ va_end(ap);
+
+ sq_quote_buf(dst, src.buf);
+ strbuf_release(&src);
+}
+
void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
{
int i;
diff --git a/quote.h b/quote.h
index 99e04d34bf..6c53a2cc66 100644
--- a/quote.h
+++ b/quote.h
@@ -25,10 +25,13 @@ struct strbuf;
* sq_quote_buf() writes to an existing buffer of specified size; it
* will return the number of characters that would have been written
* excluding the final null regardless of the buffer size.
+ *
+ * sq_quotef() quotes the entire formatted string as a single result.
*/
extern void sq_quote_buf(struct strbuf *, const char *src);
extern void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen);
+extern void sq_quotef(struct strbuf *, const char *fmt, ...);
/* This unwraps what sq_quote() produces in place, but returns
* NULL if the input does not look like what sq_quote would have
diff --git a/reachable.c b/reachable.c
index ed35201896..d0199cace4 100644
--- a/reachable.c
+++ b/reachable.c
@@ -119,8 +119,7 @@ static int add_recent_loose(const unsigned char *sha1,
*/
if (errno == ENOENT)
return 0;
- return error("unable to stat %s: %s",
- sha1_to_hex(sha1), strerror(errno));
+ return error_errno("unable to stat %s", sha1_to_hex(sha1));
}
add_recent_object(sha1, st.st_mtime, data);
diff --git a/refs.c b/refs.c
index b0e6ece6f4..87dc82f1d8 100644
--- a/refs.c
+++ b/refs.c
@@ -1080,3 +1080,152 @@ int rename_ref_available(const char *oldname, const char *newname)
strbuf_release(&err);
return ret;
}
+
+int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+ struct object_id oid;
+ int flag;
+
+ if (submodule) {
+ if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0)
+ return fn("HEAD", &oid, 0, cb_data);
+
+ return 0;
+ }
+
+ if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag))
+ return fn("HEAD", &oid, flag, cb_data);
+
+ return 0;
+}
+
+int head_ref(each_ref_fn fn, void *cb_data)
+{
+ return head_ref_submodule(NULL, fn, cb_data);
+}
+
+int for_each_ref(each_ref_fn fn, void *cb_data)
+{
+ return do_for_each_ref(NULL, "", fn, 0, 0, cb_data);
+}
+
+int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+ return do_for_each_ref(submodule, "", fn, 0, 0, cb_data);
+}
+
+int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
+{
+ return do_for_each_ref(NULL, prefix, fn, strlen(prefix), 0, cb_data);
+}
+
+int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)
+{
+ unsigned int flag = 0;
+
+ if (broken)
+ flag = DO_FOR_EACH_INCLUDE_BROKEN;
+ return do_for_each_ref(NULL, prefix, fn, 0, flag, cb_data);
+}
+
+int for_each_ref_in_submodule(const char *submodule, const char *prefix,
+ each_ref_fn fn, void *cb_data)
+{
+ return do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data);
+}
+
+int for_each_replace_ref(each_ref_fn fn, void *cb_data)
+{
+ return do_for_each_ref(NULL, git_replace_ref_base, fn,
+ strlen(git_replace_ref_base), 0, cb_data);
+}
+
+int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
+{
+ struct strbuf buf = STRBUF_INIT;
+ int ret;
+ strbuf_addf(&buf, "%srefs/", get_git_namespace());
+ ret = do_for_each_ref(NULL, buf.buf, fn, 0, 0, cb_data);
+ strbuf_release(&buf);
+ return ret;
+}
+
+int for_each_rawref(each_ref_fn fn, void *cb_data)
+{
+ return do_for_each_ref(NULL, "", fn, 0,
+ DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
+}
+
+/* This function needs to return a meaningful errno on failure */
+const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
+ unsigned char *sha1, int *flags)
+{
+ static struct strbuf sb_refname = STRBUF_INIT;
+ int unused_flags;
+ int symref_count;
+
+ if (!flags)
+ flags = &unused_flags;
+
+ *flags = 0;
+
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
+ !refname_is_safe(refname)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * dwim_ref() uses REF_ISBROKEN to distinguish between
+ * missing refs and refs that were present but invalid,
+ * to complain about the latter to stderr.
+ *
+ * We don't know whether the ref exists, so don't set
+ * REF_ISBROKEN yet.
+ */
+ *flags |= REF_BAD_NAME;
+ }
+
+ for (symref_count = 0; symref_count < SYMREF_MAXDEPTH; symref_count++) {
+ unsigned int read_flags = 0;
+
+ if (read_raw_ref(refname, sha1, &sb_refname, &read_flags)) {
+ *flags |= read_flags;
+ if (errno != ENOENT || (resolve_flags & RESOLVE_REF_READING))
+ return NULL;
+ hashclr(sha1);
+ if (*flags & REF_BAD_NAME)
+ *flags |= REF_ISBROKEN;
+ return refname;
+ }
+
+ *flags |= read_flags;
+
+ if (!(read_flags & REF_ISSYMREF)) {
+ if (*flags & REF_BAD_NAME) {
+ hashclr(sha1);
+ *flags |= REF_ISBROKEN;
+ }
+ return refname;
+ }
+
+ refname = sb_refname.buf;
+ if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
+ hashclr(sha1);
+ return refname;
+ }
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
+ !refname_is_safe(refname)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ *flags |= REF_ISBROKEN | REF_BAD_NAME;
+ }
+ }
+
+ errno = ELOOP;
+ return NULL;
+}
diff --git a/refs/files-backend.c b/refs/files-backend.c
index ea78ce9d90..1f38076411 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -513,9 +513,6 @@ static void sort_ref_dir(struct ref_dir *dir)
dir->sorted = dir->nr = i;
}
-/* Include broken references in a do_for_each_ref*() iteration: */
-#define DO_FOR_EACH_INCLUDE_BROKEN 0x01
-
/*
* Return true iff the reference described by entry can be resolved to
* an object in the database. Emit a warning if the referred-to
@@ -1272,8 +1269,6 @@ static struct ref_dir *get_loose_refs(struct ref_cache *refs)
return get_ref_dir(refs->loose);
}
-/* We allow "recursive" symbolic refs. Only within reason, though */
-#define MAXDEPTH 5
#define MAXREFLEN (1024)
/*
@@ -1303,7 +1298,7 @@ static int resolve_gitlink_ref_recursive(struct ref_cache *refs,
char buffer[128], *p;
char *path;
- if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
+ if (recursion > SYMREF_MAXDEPTH || strlen(refname) > MAXREFLEN)
return -1;
path = *refs->name
? git_pathdup_submodule(refs->name, "%s", refname)
@@ -1371,13 +1366,11 @@ static struct ref_entry *get_packed_ref(const char *refname)
}
/*
- * A loose ref file doesn't exist; check for a packed ref. The
- * options are forwarded from resolve_safe_unsafe().
+ * A loose ref file doesn't exist; check for a packed ref.
*/
static int resolve_missing_loose_ref(const char *refname,
- int resolve_flags,
unsigned char *sha1,
- int *flags)
+ unsigned int *flags)
{
struct ref_entry *entry;
@@ -1388,205 +1381,158 @@ static int resolve_missing_loose_ref(const char *refname,
entry = get_packed_ref(refname);
if (entry) {
hashcpy(sha1, entry->u.value.oid.hash);
- if (flags)
- *flags |= REF_ISPACKED;
- return 0;
- }
- /* The reference is not a packed reference, either. */
- if (resolve_flags & RESOLVE_REF_READING) {
- errno = ENOENT;
- return -1;
- } else {
- hashclr(sha1);
+ *flags |= REF_ISPACKED;
return 0;
}
+ /* refname is not a packed reference. */
+ return -1;
}
-/* This function needs to return a meaningful errno on failure */
-static const char *resolve_ref_1(const char *refname,
- int resolve_flags,
- unsigned char *sha1,
- int *flags,
- struct strbuf *sb_refname,
- struct strbuf *sb_path,
- struct strbuf *sb_contents)
+/*
+ * Read a raw ref from the filesystem or packed refs file.
+ *
+ * If the ref is a sha1, fill in sha1 and return 0.
+ *
+ * If the ref is symbolic, fill in *symref with the referrent
+ * (e.g. "refs/heads/master") and return 0. The caller is responsible
+ * for validating the referrent. Set REF_ISSYMREF in flags.
+ *
+ * If the ref doesn't exist, set errno to ENOENT and return -1.
+ *
+ * If the ref exists but is neither a symbolic ref nor a sha1, it is
+ * broken. Set REF_ISBROKEN in flags, set errno to EINVAL, and return
+ * -1.
+ *
+ * If there is another error reading the ref, set errno appropriately and
+ * return -1.
+ *
+ * Backend-specific flags might be set in flags as well, regardless of
+ * outcome.
+ *
+ * sb_path is workspace: the caller should allocate and free it.
+ *
+ * It is OK for refname to point into symref. In this case:
+ * - if the function succeeds with REF_ISSYMREF, symref will be
+ * overwritten and the memory pointed to by refname might be changed
+ * or even freed.
+ * - in all other cases, symref will be untouched, and therefore
+ * refname will still be valid and unchanged.
+ */
+int read_raw_ref(const char *refname, unsigned char *sha1,
+ struct strbuf *symref, unsigned int *flags)
{
- int depth = MAXDEPTH;
- int bad_name = 0;
+ struct strbuf sb_contents = STRBUF_INIT;
+ struct strbuf sb_path = STRBUF_INIT;
+ const char *path;
+ const char *buf;
+ struct stat st;
+ int fd;
+ int ret = -1;
+ int save_errno;
- if (flags)
- *flags = 0;
+ strbuf_reset(&sb_path);
+ strbuf_git_path(&sb_path, "%s", refname);
+ path = sb_path.buf;
- if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
- if (flags)
- *flags |= REF_BAD_NAME;
+stat_ref:
+ /*
+ * We might have to loop back here to avoid a race
+ * condition: first we lstat() the file, then we try
+ * to read it as a link or as a file. But if somebody
+ * changes the type of the file (file <-> directory
+ * <-> symlink) between the lstat() and reading, then
+ * we don't want to report that as an error but rather
+ * try again starting with the lstat().
+ */
- if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
- !refname_is_safe(refname)) {
- errno = EINVAL;
- return NULL;
+ if (lstat(path, &st) < 0) {
+ if (errno != ENOENT)
+ goto out;
+ if (resolve_missing_loose_ref(refname, sha1, flags)) {
+ errno = ENOENT;
+ goto out;
}
- /*
- * dwim_ref() uses REF_ISBROKEN to distinguish between
- * missing refs and refs that were present but invalid,
- * to complain about the latter to stderr.
- *
- * We don't know whether the ref exists, so don't set
- * REF_ISBROKEN yet.
- */
- bad_name = 1;
+ ret = 0;
+ goto out;
}
- for (;;) {
- const char *path;
- struct stat st;
- char *buf;
- int fd;
-
- if (--depth < 0) {
- errno = ELOOP;
- return NULL;
- }
-
- strbuf_reset(sb_path);
- strbuf_git_path(sb_path, "%s", refname);
- path = sb_path->buf;
-
- /*
- * We might have to loop back here to avoid a race
- * condition: first we lstat() the file, then we try
- * to read it as a link or as a file. But if somebody
- * changes the type of the file (file <-> directory
- * <-> symlink) between the lstat() and reading, then
- * we don't want to report that as an error but rather
- * try again starting with the lstat().
- */
- stat_ref:
- if (lstat(path, &st) < 0) {
- if (errno != ENOENT)
- return NULL;
- if (resolve_missing_loose_ref(refname, resolve_flags,
- sha1, flags))
- return NULL;
- if (bad_name) {
- hashclr(sha1);
- if (flags)
- *flags |= REF_ISBROKEN;
- }
- return refname;
- }
-
- /* Follow "normalized" - ie "refs/.." symlinks by hand */
- if (S_ISLNK(st.st_mode)) {
- strbuf_reset(sb_contents);
- if (strbuf_readlink(sb_contents, path, 0) < 0) {
- if (errno == ENOENT || errno == EINVAL)
- /* inconsistent with lstat; retry */
- goto stat_ref;
- else
- return NULL;
- }
- if (starts_with(sb_contents->buf, "refs/") &&
- !check_refname_format(sb_contents->buf, 0)) {
- strbuf_swap(sb_refname, sb_contents);
- refname = sb_refname->buf;
- if (flags)
- *flags |= REF_ISSYMREF;
- if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
- hashclr(sha1);
- return refname;
- }
- continue;
- }
- }
- /* Is it a directory? */
- if (S_ISDIR(st.st_mode)) {
- errno = EISDIR;
- return NULL;
- }
-
- /*
- * Anything else, just open it and try to use it as
- * a ref
- */
- fd = open(path, O_RDONLY);
- if (fd < 0) {
- if (errno == ENOENT)
+ /* Follow "normalized" - ie "refs/.." symlinks by hand */
+ if (S_ISLNK(st.st_mode)) {
+ strbuf_reset(&sb_contents);
+ if (strbuf_readlink(&sb_contents, path, 0) < 0) {
+ if (errno == ENOENT || errno == EINVAL)
/* inconsistent with lstat; retry */
goto stat_ref;
else
- return NULL;
+ goto out;
}
- strbuf_reset(sb_contents);
- if (strbuf_read(sb_contents, fd, 256) < 0) {
- int save_errno = errno;
- close(fd);
- errno = save_errno;
- return NULL;
+ if (starts_with(sb_contents.buf, "refs/") &&
+ !check_refname_format(sb_contents.buf, 0)) {
+ strbuf_swap(&sb_contents, symref);
+ *flags |= REF_ISSYMREF;
+ ret = 0;
+ goto out;
}
- close(fd);
- strbuf_rtrim(sb_contents);
+ }
- /*
- * Is it a symbolic ref?
- */
- if (!starts_with(sb_contents->buf, "ref:")) {
- /*
- * Please note that FETCH_HEAD has a second
- * line containing other data.
- */
- if (get_sha1_hex(sb_contents->buf, sha1) ||
- (sb_contents->buf[40] != '\0' && !isspace(sb_contents->buf[40]))) {
- if (flags)
- *flags |= REF_ISBROKEN;
- errno = EINVAL;
- return NULL;
- }
- if (bad_name) {
- hashclr(sha1);
- if (flags)
- *flags |= REF_ISBROKEN;
- }
- return refname;
- }
- if (flags)
- *flags |= REF_ISSYMREF;
- buf = sb_contents->buf + 4;
+ /* Is it a directory? */
+ if (S_ISDIR(st.st_mode)) {
+ errno = EISDIR;
+ goto out;
+ }
+
+ /*
+ * Anything else, just open it and try to use it as
+ * a ref
+ */
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ /* inconsistent with lstat; retry */
+ goto stat_ref;
+ else
+ goto out;
+ }
+ strbuf_reset(&sb_contents);
+ if (strbuf_read(&sb_contents, fd, 256) < 0) {
+ int save_errno = errno;
+ close(fd);
+ errno = save_errno;
+ goto out;
+ }
+ close(fd);
+ strbuf_rtrim(&sb_contents);
+ buf = sb_contents.buf;
+ if (starts_with(buf, "ref:")) {
+ buf += 4;
while (isspace(*buf))
buf++;
- strbuf_reset(sb_refname);
- strbuf_addstr(sb_refname, buf);
- refname = sb_refname->buf;
- if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
- hashclr(sha1);
- return refname;
- }
- if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
- if (flags)
- *flags |= REF_ISBROKEN;
-
- if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
- !refname_is_safe(buf)) {
- errno = EINVAL;
- return NULL;
- }
- bad_name = 1;
- }
+
+ strbuf_reset(symref);
+ strbuf_addstr(symref, buf);
+ *flags |= REF_ISSYMREF;
+ ret = 0;
+ goto out;
}
-}
-const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
- unsigned char *sha1, int *flags)
-{
- static struct strbuf sb_refname = STRBUF_INIT;
- struct strbuf sb_contents = STRBUF_INIT;
- struct strbuf sb_path = STRBUF_INIT;
- const char *ret;
+ /*
+ * Please note that FETCH_HEAD has additional
+ * data after the sha.
+ */
+ if (get_sha1_hex(buf, sha1) ||
+ (buf[40] != '\0' && !isspace(buf[40]))) {
+ *flags |= REF_ISBROKEN;
+ errno = EINVAL;
+ goto out;
+ }
+
+ ret = 0;
- ret = resolve_ref_1(refname, resolve_flags, sha1, flags,
- &sb_refname, &sb_path, &sb_contents);
+out:
+ save_errno = errno;
strbuf_release(&sb_path);
strbuf_release(&sb_contents);
+ errno = save_errno;
return ret;
}
@@ -1727,10 +1673,13 @@ static int do_for_each_entry(struct ref_cache *refs, const char *base,
* value, stop the iteration and return that value; otherwise, return
* 0.
*/
-static int do_for_each_ref(struct ref_cache *refs, const char *base,
- each_ref_fn fn, int trim, int flags, void *cb_data)
+int do_for_each_ref(const char *submodule, const char *base,
+ each_ref_fn fn, int trim, int flags, void *cb_data)
{
struct ref_entry_cb data;
+ struct ref_cache *refs;
+
+ refs = get_ref_cache(submodule);
data.base = base;
data.trim = trim;
data.flags = flags;
@@ -1745,86 +1694,6 @@ static int do_for_each_ref(struct ref_cache *refs, const char *base,
return do_for_each_entry(refs, base, do_one_ref, &data);
}
-static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
-{
- struct object_id oid;
- int flag;
-
- if (submodule) {
- if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0)
- return fn("HEAD", &oid, 0, cb_data);
-
- return 0;
- }
-
- if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag))
- return fn("HEAD", &oid, flag, cb_data);
-
- return 0;
-}
-
-int head_ref(each_ref_fn fn, void *cb_data)
-{
- return do_head_ref(NULL, fn, cb_data);
-}
-
-int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
- return do_head_ref(submodule, fn, cb_data);
-}
-
-int for_each_ref(each_ref_fn fn, void *cb_data)
-{
- return do_for_each_ref(&ref_cache, "", fn, 0, 0, cb_data);
-}
-
-int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
- return do_for_each_ref(get_ref_cache(submodule), "", fn, 0, 0, cb_data);
-}
-
-int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
-{
- return do_for_each_ref(&ref_cache, prefix, fn, strlen(prefix), 0, cb_data);
-}
-
-int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)
-{
- unsigned int flag = 0;
-
- if (broken)
- flag = DO_FOR_EACH_INCLUDE_BROKEN;
- return do_for_each_ref(&ref_cache, prefix, fn, 0, flag, cb_data);
-}
-
-int for_each_ref_in_submodule(const char *submodule, const char *prefix,
- each_ref_fn fn, void *cb_data)
-{
- return do_for_each_ref(get_ref_cache(submodule), prefix, fn, strlen(prefix), 0, cb_data);
-}
-
-int for_each_replace_ref(each_ref_fn fn, void *cb_data)
-{
- return do_for_each_ref(&ref_cache, git_replace_ref_base, fn,
- strlen(git_replace_ref_base), 0, cb_data);
-}
-
-int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
-{
- struct strbuf buf = STRBUF_INIT;
- int ret;
- strbuf_addf(&buf, "%srefs/", get_git_namespace());
- ret = do_for_each_ref(&ref_cache, buf.buf, fn, 0, 0, cb_data);
- strbuf_release(&buf);
- return ret;
-}
-
-int for_each_rawref(each_ref_fn fn, void *cb_data)
-{
- return do_for_each_ref(&ref_cache, "", fn, 0,
- DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
-}
-
static void unlock_ref(struct ref_lock *lock)
{
/* Do not free lock->lk -- atexit() still looks at them */
@@ -3481,7 +3350,8 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
* reference itself, plus we might need to update the
* reference if --updateref was specified:
*/
- lock = lock_ref_sha1_basic(refname, sha1, NULL, NULL, 0, &type, &err);
+ lock = lock_ref_sha1_basic(refname, sha1, NULL, NULL, REF_NODEREF,
+ &type, &err);
if (!lock) {
error("cannot lock ref '%s': %s", refname, err.buf);
strbuf_release(&err);
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index c7dded35f4..3a4f634cb4 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -197,4 +197,19 @@ const char *find_descendant_ref(const char *dirname,
int rename_ref_available(const char *oldname, const char *newname);
+/* We allow "recursive" symbolic refs. Only within reason, though */
+#define SYMREF_MAXDEPTH 5
+
+/* Include broken references in a do_for_each_ref*() iteration: */
+#define DO_FOR_EACH_INCLUDE_BROKEN 0x01
+
+/*
+ * The common backend for the for_each_*ref* functions
+ */
+int do_for_each_ref(const char *submodule, const char *base,
+ each_ref_fn fn, int trim, int flags, void *cb_data);
+
+int read_raw_ref(const char *refname, unsigned char *sha1,
+ struct strbuf *symref, unsigned int *flags);
+
#endif /* REFS_REFS_INTERNAL_H */
diff --git a/remote-curl.c b/remote-curl.c
index 15e48e25fb..672b382e5a 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -474,7 +474,7 @@ static int run_slot(struct active_request_slot *slot,
static int probe_rpc(struct rpc_state *rpc, struct slot_results *results)
{
struct active_request_slot *slot;
- struct curl_slist *headers = NULL;
+ struct curl_slist *headers = http_copy_default_headers();
struct strbuf buf = STRBUF_INIT;
int err;
@@ -503,7 +503,7 @@ static int probe_rpc(struct rpc_state *rpc, struct slot_results *results)
static int post_rpc(struct rpc_state *rpc)
{
struct active_request_slot *slot;
- struct curl_slist *headers = NULL;
+ struct curl_slist *headers = http_copy_default_headers();
int use_gzip = rpc->gzip_request;
char *gzip_body = NULL;
size_t gzip_size = 0;
diff --git a/remote.c b/remote.c
index 28fd676acb..a326e4e251 100644
--- a/remote.c
+++ b/remote.c
@@ -1660,7 +1660,7 @@ int branch_merge_matches(struct branch *branch,
return refname_match(branch->merge[i]->src, refname);
}
-__attribute((format (printf,2,3)))
+__attribute__((format (printf,2,3)))
static const char *error_buf(struct strbuf *err, const char *fmt, ...)
{
if (err) {
@@ -2108,7 +2108,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
"Your branch and '%s' have diverged,\n"
"and have %d and %d different commits each, "
"respectively.\n",
- theirs),
+ ours + theirs),
base, ours, theirs);
if (advice_status_hints)
strbuf_addf(sb,
diff --git a/rerere.c b/rerere.c
index 587b7e2717..aaadec17d8 100644
--- a/rerere.c
+++ b/rerere.c
@@ -8,6 +8,7 @@
#include "ll-merge.h"
#include "attr.h"
#include "pathspec.h"
+#include "sha1-lookup.h"
#define RESOLVED 0
#define PUNTED 1
@@ -20,6 +21,29 @@ static int rerere_enabled = -1;
/* automatically update cleanly resolved paths to the index */
static int rerere_autoupdate;
+static int rerere_dir_nr;
+static int rerere_dir_alloc;
+
+#define RR_HAS_POSTIMAGE 1
+#define RR_HAS_PREIMAGE 2
+static struct rerere_dir {
+ unsigned char sha1[20];
+ int status_alloc, status_nr;
+ unsigned char *status;
+} **rerere_dir;
+
+static void free_rerere_dirs(void)
+{
+ int i;
+ for (i = 0; i < rerere_dir_nr; i++) {
+ free(rerere_dir[i]->status);
+ free(rerere_dir[i]);
+ }
+ free(rerere_dir);
+ rerere_dir_nr = rerere_dir_alloc = 0;
+ rerere_dir = NULL;
+}
+
static void free_rerere_id(struct string_list_item *item)
{
free(item->util);
@@ -27,7 +51,33 @@ static void free_rerere_id(struct string_list_item *item)
static const char *rerere_id_hex(const struct rerere_id *id)
{
- return id->hex;
+ return sha1_to_hex(id->collection->sha1);
+}
+
+static void fit_variant(struct rerere_dir *rr_dir, int variant)
+{
+ variant++;
+ ALLOC_GROW(rr_dir->status, variant, rr_dir->status_alloc);
+ if (rr_dir->status_nr < variant) {
+ memset(rr_dir->status + rr_dir->status_nr,
+ '\0', variant - rr_dir->status_nr);
+ rr_dir->status_nr = variant;
+ }
+}
+
+static void assign_variant(struct rerere_id *id)
+{
+ int variant;
+ struct rerere_dir *rr_dir = id->collection;
+
+ variant = id->variant;
+ if (variant < 0) {
+ for (variant = 0; variant < rr_dir->status_nr; variant++)
+ if (!rr_dir->status[variant])
+ break;
+ }
+ fit_variant(rr_dir, variant);
+ id->variant = variant;
}
const char *rerere_path(const struct rerere_id *id, const char *file)
@@ -35,20 +85,103 @@ const char *rerere_path(const struct rerere_id *id, const char *file)
if (!file)
return git_path("rr-cache/%s", rerere_id_hex(id));
- return git_path("rr-cache/%s/%s", rerere_id_hex(id), file);
+ if (id->variant <= 0)
+ return git_path("rr-cache/%s/%s", rerere_id_hex(id), file);
+
+ return git_path("rr-cache/%s/%s.%d",
+ rerere_id_hex(id), file, id->variant);
+}
+
+static int is_rr_file(const char *name, const char *filename, int *variant)
+{
+ const char *suffix;
+ char *ep;
+
+ if (!strcmp(name, filename)) {
+ *variant = 0;
+ return 1;
+ }
+ if (!skip_prefix(name, filename, &suffix) || *suffix != '.')
+ return 0;
+
+ errno = 0;
+ *variant = strtol(suffix + 1, &ep, 10);
+ if (errno || *ep)
+ return 0;
+ return 1;
+}
+
+static void scan_rerere_dir(struct rerere_dir *rr_dir)
+{
+ struct dirent *de;
+ DIR *dir = opendir(git_path("rr-cache/%s", sha1_to_hex(rr_dir->sha1)));
+
+ if (!dir)
+ return;
+ while ((de = readdir(dir)) != NULL) {
+ int variant;
+
+ if (is_rr_file(de->d_name, "postimage", &variant)) {
+ fit_variant(rr_dir, variant);
+ rr_dir->status[variant] |= RR_HAS_POSTIMAGE;
+ } else if (is_rr_file(de->d_name, "preimage", &variant)) {
+ fit_variant(rr_dir, variant);
+ rr_dir->status[variant] |= RR_HAS_PREIMAGE;
+ }
+ }
+ closedir(dir);
+}
+
+static const unsigned char *rerere_dir_sha1(size_t i, void *table)
+{
+ struct rerere_dir **rr_dir = table;
+ return rr_dir[i]->sha1;
+}
+
+static struct rerere_dir *find_rerere_dir(const char *hex)
+{
+ unsigned char sha1[20];
+ struct rerere_dir *rr_dir;
+ int pos;
+
+ if (get_sha1_hex(hex, sha1))
+ return NULL; /* BUG */
+ pos = sha1_pos(sha1, rerere_dir, rerere_dir_nr, rerere_dir_sha1);
+ if (pos < 0) {
+ rr_dir = xmalloc(sizeof(*rr_dir));
+ hashcpy(rr_dir->sha1, sha1);
+ rr_dir->status = NULL;
+ rr_dir->status_nr = 0;
+ rr_dir->status_alloc = 0;
+ pos = -1 - pos;
+
+ /* Make sure the array is big enough ... */
+ ALLOC_GROW(rerere_dir, rerere_dir_nr + 1, rerere_dir_alloc);
+ /* ... and add it in. */
+ rerere_dir_nr++;
+ memmove(rerere_dir + pos + 1, rerere_dir + pos,
+ (rerere_dir_nr - pos - 1) * sizeof(*rerere_dir));
+ rerere_dir[pos] = rr_dir;
+ scan_rerere_dir(rr_dir);
+ }
+ return rerere_dir[pos];
}
static int has_rerere_resolution(const struct rerere_id *id)
{
- struct stat st;
+ const int both = RR_HAS_POSTIMAGE|RR_HAS_PREIMAGE;
+ int variant = id->variant;
- return !stat(rerere_path(id, "postimage"), &st);
+ if (variant < 0)
+ return 0;
+ return ((id->collection->status[variant] & both) == both);
}
static struct rerere_id *new_rerere_id_hex(char *hex)
{
struct rerere_id *id = xmalloc(sizeof(*id));
- xsnprintf(id->hex, sizeof(id->hex), "%s", hex);
+ id->collection = find_rerere_dir(hex);
+ id->variant = -1; /* not known yet */
return id;
}
@@ -75,16 +208,26 @@ static void read_rr(struct string_list *rr)
char *path;
unsigned char sha1[20];
struct rerere_id *id;
+ int variant;
/* There has to be the hash, tab, path and then NUL */
if (buf.len < 42 || get_sha1_hex(buf.buf, sha1))
die("corrupt MERGE_RR");
- if (buf.buf[40] != '\t')
+ if (buf.buf[40] != '.') {
+ variant = 0;
+ path = buf.buf + 40;
+ } else {
+ errno = 0;
+ variant = strtol(buf.buf + 41, &path, 10);
+ if (errno)
+ die("corrupt MERGE_RR");
+ }
+ if (*(path++) != '\t')
die("corrupt MERGE_RR");
buf.buf[40] = '\0';
- path = buf.buf + 41;
id = new_rerere_id_hex(buf.buf);
+ id->variant = variant;
string_list_insert(rr, path)->util = id;
}
strbuf_release(&buf);
@@ -105,9 +248,16 @@ static int write_rr(struct string_list *rr, int out_fd)
id = rr->items[i].util;
if (!id)
continue;
- strbuf_addf(&buf, "%s\t%s%c",
- rerere_id_hex(id),
- rr->items[i].string, 0);
+ assert(id->variant >= 0);
+ if (0 < id->variant)
+ strbuf_addf(&buf, "%s.%d\t%s%c",
+ rerere_id_hex(id), id->variant,
+ rr->items[i].string, 0);
+ else
+ strbuf_addf(&buf, "%s\t%s%c",
+ rerere_id_hex(id),
+ rr->items[i].string, 0);
+
if (write_in_full(out_fd, buf.buf, buf.len) != buf.len)
die("unable to write rerere record");
@@ -351,8 +501,7 @@ static int handle_file(const char *path, unsigned char *sha1, const char *output
error("There were errors while writing %s (%s)",
path, strerror(io.io.wrerror));
if (io.io.output && fclose(io.io.output))
- io.io.wrerror = error("Failed to flush %s: %s",
- path, strerror(errno));
+ io.io.wrerror = error_errno("Failed to flush %s", path);
if (hunk_no < 0) {
if (output)
@@ -365,103 +514,6 @@ static int handle_file(const char *path, unsigned char *sha1, const char *output
}
/*
- * Subclass of rerere_io that reads from an in-core buffer that is a
- * strbuf
- */
-struct rerere_io_mem {
- struct rerere_io io;
- struct strbuf input;
-};
-
-/*
- * ... and its getline() method implementation
- */
-static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)
-{
- struct rerere_io_mem *io = (struct rerere_io_mem *)io_;
- char *ep;
- size_t len;
-
- strbuf_release(sb);
- if (!io->input.len)
- return -1;
- ep = memchr(io->input.buf, '\n', io->input.len);
- if (!ep)
- ep = io->input.buf + io->input.len;
- else if (*ep == '\n')
- ep++;
- len = ep - io->input.buf;
- strbuf_add(sb, io->input.buf, len);
- strbuf_remove(&io->input, 0, len);
- return 0;
-}
-
-static int handle_cache(const char *path, unsigned char *sha1, const char *output)
-{
- mmfile_t mmfile[3] = {{NULL}};
- mmbuffer_t result = {NULL, 0};
- const struct cache_entry *ce;
- int pos, len, i, hunk_no;
- struct rerere_io_mem io;
- int marker_size = ll_merge_marker_size(path);
-
- /*
- * Reproduce the conflicted merge in-core
- */
- len = strlen(path);
- pos = cache_name_pos(path, len);
- if (0 <= pos)
- return -1;
- pos = -pos - 1;
-
- while (pos < active_nr) {
- enum object_type type;
- unsigned long size;
-
- ce = active_cache[pos++];
- if (ce_namelen(ce) != len || memcmp(ce->name, path, len))
- break;
- i = ce_stage(ce) - 1;
- if (!mmfile[i].ptr) {
- mmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size);
- mmfile[i].size = size;
- }
- }
- for (i = 0; i < 3; i++)
- if (!mmfile[i].ptr && !mmfile[i].size)
- mmfile[i].ptr = xstrdup("");
-
- /*
- * NEEDSWORK: handle conflicts from merges with
- * merge.renormalize set, too
- */
- ll_merge(&result, path, &mmfile[0], NULL,
- &mmfile[1], "ours",
- &mmfile[2], "theirs", NULL);
- for (i = 0; i < 3; i++)
- free(mmfile[i].ptr);
-
- memset(&io, 0, sizeof(io));
- io.io.getline = rerere_mem_getline;
- if (output)
- io.io.output = fopen(output, "w");
- else
- io.io.output = NULL;
- strbuf_init(&io.input, 0);
- strbuf_attach(&io.input, result.ptr, result.size, result.size);
-
- /*
- * Grab the conflict ID and optionally write the original
- * contents with conflict markers out.
- */
- hunk_no = handle_path(sha1, (struct rerere_io *)&io, marker_size);
- strbuf_release(&io.input);
- if (io.io.output)
- fclose(io.io.output);
- return hunk_no;
-}
-
-/*
* Look at a cache entry at "i" and see if it is not conflicting,
* conflicting and we are willing to handle, or conflicting and
* we are unable to handle, and return the determination in *type.
@@ -569,6 +621,33 @@ int rerere_remaining(struct string_list *merge_rr)
}
/*
+ * Try using the given conflict resolution "ID" to see
+ * if that recorded conflict resolves cleanly what we
+ * got in the "cur".
+ */
+static int try_merge(const struct rerere_id *id, const char *path,
+ mmfile_t *cur, mmbuffer_t *result)
+{
+ int ret;
+ mmfile_t base = {NULL, 0}, other = {NULL, 0};
+
+ if (read_mmfile(&base, rerere_path(id, "preimage")) ||
+ read_mmfile(&other, rerere_path(id, "postimage")))
+ ret = 1;
+ else
+ /*
+ * A three-way merge. Note that this honors user-customizable
+ * low-level merge driver settings.
+ */
+ ret = ll_merge(result, path, &base, NULL, cur, "", &other, "", NULL);
+
+ free(base.ptr);
+ free(other.ptr);
+
+ return ret;
+}
+
+/*
* Find the conflict identified by "id"; the change between its
* "preimage" (i.e. a previous contents with conflict markers) and its
* "postimage" (i.e. the corresponding contents with conflicts
@@ -582,30 +661,20 @@ static int merge(const struct rerere_id *id, const char *path)
{
FILE *f;
int ret;
- mmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0};
+ mmfile_t cur = {NULL, 0};
mmbuffer_t result = {NULL, 0};
/*
* Normalize the conflicts in path and write it out to
* "thisimage" temporary file.
*/
- if (handle_file(path, NULL, rerere_path(id, "thisimage")) < 0) {
- ret = 1;
- goto out;
- }
-
- if (read_mmfile(&cur, rerere_path(id, "thisimage")) ||
- read_mmfile(&base, rerere_path(id, "preimage")) ||
- read_mmfile(&other, rerere_path(id, "postimage"))) {
+ if ((handle_file(path, NULL, rerere_path(id, "thisimage")) < 0) ||
+ read_mmfile(&cur, rerere_path(id, "thisimage"))) {
ret = 1;
goto out;
}
- /*
- * A three-way merge. Note that this honors user-customizable
- * low-level merge driver settings.
- */
- ret = ll_merge(&result, path, &base, NULL, &cur, "", &other, "", NULL);
+ ret = try_merge(id, path, &cur, &result);
if (ret)
goto out;
@@ -614,25 +683,20 @@ static int merge(const struct rerere_id *id, const char *path)
* Mark that "postimage" was used to help gc.
*/
if (utime(rerere_path(id, "postimage"), NULL) < 0)
- warning("failed utime() on %s: %s",
- rerere_path(id, "postimage"),
- strerror(errno));
+ warning_errno("failed utime() on %s",
+ rerere_path(id, "postimage"));
/* Update "path" with the resolution */
f = fopen(path, "w");
if (!f)
- return error("Could not open %s: %s", path,
- strerror(errno));
+ return error_errno("Could not open %s", path);
if (fwrite(result.ptr, result.size, 1, f) != 1)
- error("Could not write %s: %s", path, strerror(errno));
+ error_errno("Could not write %s", path);
if (fclose(f))
- return error("Writing %s failed: %s", path,
- strerror(errno));
+ return error_errno("Writing %s failed", path);
out:
free(cur.ptr);
- free(base.ptr);
- free(other.ptr);
free(result.ptr);
return ret;
@@ -661,6 +725,13 @@ static void update_paths(struct string_list *update)
rollback_lock_file(&index_lock);
}
+static void remove_variant(struct rerere_id *id)
+{
+ unlink_or_warn(rerere_path(id, "postimage"));
+ unlink_or_warn(rerere_path(id, "preimage"));
+ id->collection->status[id->variant] = 0;
+}
+
/*
* The path indicated by rr_item may still have conflict for which we
* have a recorded resolution, in which case replay it and optionally
@@ -672,12 +743,47 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
struct string_list *update)
{
const char *path = rr_item->string;
- const struct rerere_id *id = rr_item->util;
+ struct rerere_id *id = rr_item->util;
+ struct rerere_dir *rr_dir = id->collection;
+ int variant;
+
+ variant = id->variant;
+
+ /* Has the user resolved it already? */
+ if (variant >= 0) {
+ if (!handle_file(path, NULL, NULL)) {
+ copy_file(rerere_path(id, "postimage"), path, 0666);
+ id->collection->status[variant] |= RR_HAS_POSTIMAGE;
+ fprintf(stderr, "Recorded resolution for '%s'.\n", path);
+ free_rerere_id(rr_item);
+ rr_item->util = NULL;
+ return;
+ }
+ /*
+ * There may be other variants that can cleanly
+ * replay. Try them and update the variant number for
+ * this one.
+ */
+ }
+
+ /* Does any existing resolution apply cleanly? */
+ for (variant = 0; variant < rr_dir->status_nr; variant++) {
+ const int both = RR_HAS_PREIMAGE | RR_HAS_POSTIMAGE;
+ struct rerere_id vid = *id;
+
+ if ((rr_dir->status[variant] & both) != both)
+ continue;
+
+ vid.variant = variant;
+ if (merge(&vid, path))
+ continue; /* failed to replay */
- /* Is there a recorded resolution we could attempt to apply? */
- if (has_rerere_resolution(id)) {
- if (merge(id, path))
- return; /* failed to replay */
+ /*
+ * If there already is a different variant that applies
+ * cleanly, there is no point maintaining our own variant.
+ */
+ if (0 <= id->variant && id->variant != variant)
+ remove_variant(id);
if (rerere_autoupdate)
string_list_insert(update, path);
@@ -685,15 +791,24 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
fprintf(stderr,
"Resolved '%s' using previous resolution.\n",
path);
- } else if (!handle_file(path, NULL, NULL)) {
- /* The user has resolved it. */
- copy_file(rerere_path(id, "postimage"), path, 0666);
- fprintf(stderr, "Recorded resolution for '%s'.\n", path);
- } else {
+ free_rerere_id(rr_item);
+ rr_item->util = NULL;
return;
}
- free_rerere_id(rr_item);
- rr_item->util = NULL;
+
+ /* None of the existing one applies; we need a new variant */
+ assign_variant(id);
+
+ variant = id->variant;
+ handle_file(path, NULL, rerere_path(id, "preimage"));
+ if (id->collection->status[variant] & RR_HAS_POSTIMAGE) {
+ const char *path = rerere_path(id, "postimage");
+ if (unlink(path))
+ die_errno("cannot unlink stray '%s'", path);
+ id->collection->status[variant] &= ~RR_HAS_POSTIMAGE;
+ }
+ id->collection->status[variant] |= RR_HAS_PREIMAGE;
+ fprintf(stderr, "Recorded preimage for '%s'\n", path);
}
static int do_plain_rerere(struct string_list *rr, int fd)
@@ -731,24 +846,8 @@ static int do_plain_rerere(struct string_list *rr, int fd)
id = new_rerere_id(sha1);
string_list_insert(rr, path)->util = id;
- /*
- * If the directory does not exist, create
- * it. mkdir_in_gitdir() will fail with
- * EEXIST if there already is one.
- *
- * NEEDSWORK: make sure "gc" does not remove
- * preimage without removing the directory.
- */
- if (mkdir_in_gitdir(rerere_path(id, NULL)))
- continue;
-
- /*
- * We are the first to encounter this
- * conflict. Ask handle_file() to write the
- * normalized contents to the "preimage" file.
- */
- handle_file(path, NULL, rerere_path(id, "preimage"));
- fprintf(stderr, "Recorded preimage for '%s'\n", path);
+ /* Ensure that the directory exists. */
+ mkdir_in_gitdir(rerere_path(id, NULL));
}
for (i = 0; i < rr->nr; i++)
@@ -812,12 +911,111 @@ int setup_rerere(struct string_list *merge_rr, int flags)
int rerere(int flags)
{
struct string_list merge_rr = STRING_LIST_INIT_DUP;
- int fd;
+ int fd, status;
fd = setup_rerere(&merge_rr, flags);
if (fd < 0)
return 0;
- return do_plain_rerere(&merge_rr, fd);
+ status = do_plain_rerere(&merge_rr, fd);
+ free_rerere_dirs();
+ return status;
+}
+
+/*
+ * Subclass of rerere_io that reads from an in-core buffer that is a
+ * strbuf
+ */
+struct rerere_io_mem {
+ struct rerere_io io;
+ struct strbuf input;
+};
+
+/*
+ * ... and its getline() method implementation
+ */
+static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)
+{
+ struct rerere_io_mem *io = (struct rerere_io_mem *)io_;
+ char *ep;
+ size_t len;
+
+ strbuf_release(sb);
+ if (!io->input.len)
+ return -1;
+ ep = memchr(io->input.buf, '\n', io->input.len);
+ if (!ep)
+ ep = io->input.buf + io->input.len;
+ else if (*ep == '\n')
+ ep++;
+ len = ep - io->input.buf;
+ strbuf_add(sb, io->input.buf, len);
+ strbuf_remove(&io->input, 0, len);
+ return 0;
+}
+
+static int handle_cache(const char *path, unsigned char *sha1, const char *output)
+{
+ mmfile_t mmfile[3] = {{NULL}};
+ mmbuffer_t result = {NULL, 0};
+ const struct cache_entry *ce;
+ int pos, len, i, hunk_no;
+ struct rerere_io_mem io;
+ int marker_size = ll_merge_marker_size(path);
+
+ /*
+ * Reproduce the conflicted merge in-core
+ */
+ len = strlen(path);
+ pos = cache_name_pos(path, len);
+ if (0 <= pos)
+ return -1;
+ pos = -pos - 1;
+
+ while (pos < active_nr) {
+ enum object_type type;
+ unsigned long size;
+
+ ce = active_cache[pos++];
+ if (ce_namelen(ce) != len || memcmp(ce->name, path, len))
+ break;
+ i = ce_stage(ce) - 1;
+ if (!mmfile[i].ptr) {
+ mmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size);
+ mmfile[i].size = size;
+ }
+ }
+ for (i = 0; i < 3; i++)
+ if (!mmfile[i].ptr && !mmfile[i].size)
+ mmfile[i].ptr = xstrdup("");
+
+ /*
+ * NEEDSWORK: handle conflicts from merges with
+ * merge.renormalize set, too?
+ */
+ ll_merge(&result, path, &mmfile[0], NULL,
+ &mmfile[1], "ours",
+ &mmfile[2], "theirs", NULL);
+ for (i = 0; i < 3; i++)
+ free(mmfile[i].ptr);
+
+ memset(&io, 0, sizeof(io));
+ io.io.getline = rerere_mem_getline;
+ if (output)
+ io.io.output = fopen(output, "w");
+ else
+ io.io.output = NULL;
+ strbuf_init(&io.input, 0);
+ strbuf_attach(&io.input, result.ptr, result.size, result.size);
+
+ /*
+ * Grab the conflict ID and optionally write the original
+ * contents with conflict markers out.
+ */
+ hunk_no = handle_path(sha1, (struct rerere_io *)&io, marker_size);
+ strbuf_release(&io.input);
+ if (io.io.output)
+ fclose(io.io.output);
+ return hunk_no;
}
static int rerere_forget_one_path(const char *path, struct string_list *rr)
@@ -838,11 +1036,43 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
/* Nuke the recorded resolution for the conflict */
id = new_rerere_id(sha1);
+
+ for (id->variant = 0;
+ id->variant < id->collection->status_nr;
+ id->variant++) {
+ mmfile_t cur = { NULL, 0 };
+ mmbuffer_t result = {NULL, 0};
+ int cleanly_resolved;
+
+ if (!has_rerere_resolution(id))
+ continue;
+
+ handle_cache(path, sha1, rerere_path(id, "thisimage"));
+ if (read_mmfile(&cur, rerere_path(id, "thisimage"))) {
+ free(cur.ptr);
+ error("Failed to update conflicted state in '%s'", path);
+ goto fail_exit;
+ }
+ cleanly_resolved = !try_merge(id, path, &cur, &result);
+ free(result.ptr);
+ free(cur.ptr);
+ if (cleanly_resolved)
+ break;
+ }
+
+ if (id->collection->status_nr <= id->variant) {
+ error("no remembered resolution for '%s'", path);
+ goto fail_exit;
+ }
+
filename = rerere_path(id, "postimage");
- if (unlink(filename))
- return (errno == ENOENT
- ? error("no remembered resolution for %s", path)
- : error("cannot unlink %s: %s", filename, strerror(errno)));
+ if (unlink(filename)) {
+ if (errno == ENOENT)
+ error("no remembered resolution for %s", path);
+ else
+ error_errno("cannot unlink %s", filename);
+ goto fail_exit;
+ }
/*
* Update the preimage so that the user can resolve the
@@ -861,6 +1091,10 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
item->util = id;
fprintf(stderr, "Forgot resolution for %s\n", path);
return 0;
+
+fail_exit:
+ free(id);
+ return -1;
}
int rerere_forget(struct pathspec *pathspec)
@@ -897,29 +1131,16 @@ int rerere_forget(struct pathspec *pathspec)
* Garbage collection support
*/
-/*
- * Note that this is not reentrant but is used only one-at-a-time
- * so it does not matter right now.
- */
-static struct rerere_id *dirname_to_id(const char *name)
-{
- static struct rerere_id id;
- xsnprintf(id.hex, sizeof(id.hex), "%s", name);
- return &id;
-}
-
-static time_t rerere_created_at(const char *dir_name)
+static time_t rerere_created_at(struct rerere_id *id)
{
struct stat st;
- struct rerere_id *id = dirname_to_id(dir_name);
return stat(rerere_path(id, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
}
-static time_t rerere_last_used_at(const char *dir_name)
+static time_t rerere_last_used_at(struct rerere_id *id)
{
struct stat st;
- struct rerere_id *id = dirname_to_id(dir_name);
return stat(rerere_path(id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
}
@@ -929,15 +1150,28 @@ static time_t rerere_last_used_at(const char *dir_name)
*/
static void unlink_rr_item(struct rerere_id *id)
{
- unlink(rerere_path(id, "thisimage"));
- unlink(rerere_path(id, "preimage"));
- unlink(rerere_path(id, "postimage"));
- /*
- * NEEDSWORK: what if this rmdir() fails? Wouldn't we then
- * assume that we already have preimage recorded in
- * do_plain_rerere()?
- */
- rmdir(rerere_path(id, NULL));
+ unlink_or_warn(rerere_path(id, "thisimage"));
+ remove_variant(id);
+ id->collection->status[id->variant] = 0;
+}
+
+static void prune_one(struct rerere_id *id, time_t now,
+ int cutoff_resolve, int cutoff_noresolve)
+{
+ time_t then;
+ int cutoff;
+
+ then = rerere_last_used_at(id);
+ if (then)
+ cutoff = cutoff_resolve;
+ else {
+ then = rerere_created_at(id);
+ if (!then)
+ return;
+ cutoff = cutoff_noresolve;
+ }
+ if (then < now - cutoff * 86400)
+ unlink_rr_item(id);
}
void rerere_gc(struct string_list *rr)
@@ -945,8 +1179,8 @@ void rerere_gc(struct string_list *rr)
struct string_list to_remove = STRING_LIST_INIT_DUP;
DIR *dir;
struct dirent *e;
- int i, cutoff;
- time_t now = time(NULL), then;
+ int i;
+ time_t now = time(NULL);
int cutoff_noresolve = 15;
int cutoff_resolve = 60;
@@ -961,25 +1195,32 @@ void rerere_gc(struct string_list *rr)
die_errno("unable to open rr-cache directory");
/* Collect stale conflict IDs ... */
while ((e = readdir(dir))) {
+ struct rerere_dir *rr_dir;
+ struct rerere_id id;
+ int now_empty;
+
if (is_dot_or_dotdot(e->d_name))
continue;
-
- then = rerere_last_used_at(e->d_name);
- if (then) {
- cutoff = cutoff_resolve;
- } else {
- then = rerere_created_at(e->d_name);
- if (!then)
- continue;
- cutoff = cutoff_noresolve;
+ rr_dir = find_rerere_dir(e->d_name);
+ if (!rr_dir)
+ continue; /* or should we remove e->d_name? */
+
+ now_empty = 1;
+ for (id.variant = 0, id.collection = rr_dir;
+ id.variant < id.collection->status_nr;
+ id.variant++) {
+ prune_one(&id, now, cutoff_resolve, cutoff_noresolve);
+ if (id.collection->status[id.variant])
+ now_empty = 0;
}
- if (then < now - cutoff * 86400)
+ if (now_empty)
string_list_append(&to_remove, e->d_name);
}
closedir(dir);
- /* ... and then remove them one-by-one */
+
+ /* ... and then remove the empty directories */
for (i = 0; i < to_remove.nr; i++)
- unlink_rr_item(dirname_to_id(to_remove.items[i].string));
+ rmdir(git_path("rr-cache/%s", to_remove.items[i].string));
string_list_clear(&to_remove, 0);
rollback_lock_file(&write_lock);
}
@@ -1000,8 +1241,10 @@ void rerere_clear(struct string_list *merge_rr)
for (i = 0; i < merge_rr->nr; i++) {
struct rerere_id *id = merge_rr->items[i].util;
- if (!has_rerere_resolution(id))
+ if (!has_rerere_resolution(id)) {
unlink_rr_item(id);
+ rmdir(rerere_path(id, NULL));
+ }
}
unlink_or_warn(git_path_merge_rr());
rollback_lock_file(&write_lock);
diff --git a/rerere.h b/rerere.h
index 1222e91921..c2961feaaa 100644
--- a/rerere.h
+++ b/rerere.h
@@ -16,8 +16,10 @@ struct pathspec;
*/
extern void *RERERE_RESOLVED;
+struct rerere_dir;
struct rerere_id {
- char hex[41];
+ struct rerere_dir *collection;
+ int variant;
};
extern int setup_rerere(struct string_list *, int);
diff --git a/revision.c b/revision.c
index 8b2dfe3160..d30d1c4f80 100644
--- a/revision.c
+++ b/revision.c
@@ -59,10 +59,10 @@ static void mark_tree_contents_uninteresting(struct tree *tree)
while (tree_entry(&desc, &entry)) {
switch (object_type(entry.mode)) {
case OBJ_TREE:
- mark_tree_uninteresting(lookup_tree(entry.sha1));
+ mark_tree_uninteresting(lookup_tree(entry.oid->hash));
break;
case OBJ_BLOB:
- mark_blob_uninteresting(lookup_blob(entry.sha1));
+ mark_blob_uninteresting(lookup_blob(entry.oid->hash));
break;
default:
/* Subproject commit - not in this repository */
@@ -1356,8 +1356,10 @@ void init_revisions(struct rev_info *revs, const char *prefix)
revs->skip_count = -1;
revs->max_count = -1;
revs->max_parents = -1;
+ revs->expand_tabs_in_log = -1;
revs->commit_format = CMIT_FMT_DEFAULT;
+ revs->expand_tabs_in_log_default = 8;
init_grep_defaults();
grep_init(&revs->grep_filter, prefix);
@@ -1854,6 +1856,15 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->verbose_header = 1;
revs->pretty_given = 1;
get_commit_format(arg+9, revs);
+ } else if (!strcmp(arg, "--expand-tabs")) {
+ revs->expand_tabs_in_log = 8;
+ } else if (!strcmp(arg, "--no-expand-tabs")) {
+ revs->expand_tabs_in_log = 0;
+ } else if (skip_prefix(arg, "--expand-tabs=", &arg)) {
+ int val;
+ if (strtol_i(arg, 10, &val) < 0 || val < 0)
+ die("'%s': not a non-negative integer", arg);
+ revs->expand_tabs_in_log = val;
} else if (!strcmp(arg, "--show-notes") || !strcmp(arg, "--notes")) {
revs->show_notes = 1;
revs->show_notes_given = 1;
@@ -2327,6 +2338,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
if (revs->first_parent_only && revs->bisect)
die(_("--first-parent is incompatible with --bisect"));
+ if (revs->expand_tabs_in_log < 0)
+ revs->expand_tabs_in_log = revs->expand_tabs_in_log_default;
+
return left;
}
diff --git a/revision.h b/revision.h
index dca0d38171..9fac1a607d 100644
--- a/revision.h
+++ b/revision.h
@@ -148,6 +148,8 @@ struct rev_info {
linear:1;
struct date_mode date_mode;
+ int expand_tabs_in_log; /* unset if negative */
+ int expand_tabs_in_log_default;
unsigned int abbrev;
enum cmit_fmt commit_format;
diff --git a/run-command.c b/run-command.c
index c72601056c..af0c8a10df 100644
--- a/run-command.c
+++ b/run-command.c
@@ -233,7 +233,7 @@ static int wait_or_whine(pid_t pid, const char *argv0, int in_signal)
if (waiting < 0) {
failed_errno = errno;
- error("waitpid for %s failed: %s", argv0, strerror(errno));
+ error_errno("waitpid for %s failed", argv0);
} else if (waiting != pid) {
error("waitpid is confused (%s)", argv0);
} else if (WIFSIGNALED(status)) {
@@ -420,8 +420,7 @@ fail_pipe:
}
}
if (cmd->pid < 0)
- error("cannot fork() for %s: %s", cmd->argv[0],
- strerror(errno));
+ error_errno("cannot fork() for %s", cmd->argv[0]);
else if (cmd->clean_on_exit)
mark_child_for_cleanup(cmd->pid);
@@ -482,7 +481,7 @@ fail_pipe:
cmd->dir, fhin, fhout, fherr);
failed_errno = errno;
if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
- error("cannot spawn %s: %s", cmd->argv[0], strerror(errno));
+ error_errno("cannot spawn %s", cmd->argv[0]);
if (cmd->clean_on_exit && cmd->pid >= 0)
mark_child_for_cleanup(cmd->pid);
@@ -590,6 +589,16 @@ static void *run_thread(void *data)
struct async *async = data;
intptr_t ret;
+ if (async->isolate_sigpipe) {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGPIPE);
+ if (pthread_sigmask(SIG_BLOCK, &mask, NULL) < 0) {
+ ret = error("unable to block SIGPIPE in async thread");
+ return (void *)ret;
+ }
+ }
+
pthread_setspecific(async_key, async);
ret = async->proc(async->proc_in, async->proc_out, async->data);
return (void *)ret;
@@ -693,7 +702,7 @@ int start_async(struct async *async)
if (pipe(fdin) < 0) {
if (async->out > 0)
close(async->out);
- return error("cannot create pipe: %s", strerror(errno));
+ return error_errno("cannot create pipe");
}
async->in = fdin[1];
}
@@ -705,7 +714,7 @@ int start_async(struct async *async)
close_pair(fdin);
else if (async->in)
close(async->in);
- return error("cannot create pipe: %s", strerror(errno));
+ return error_errno("cannot create pipe");
}
async->out = fdout[0];
}
@@ -730,7 +739,7 @@ int start_async(struct async *async)
async->pid = fork();
if (async->pid < 0) {
- error("fork (async) failed: %s", strerror(errno));
+ error_errno("fork (async) failed");
goto error;
}
if (!async->pid) {
@@ -777,7 +786,7 @@ int start_async(struct async *async)
{
int err = pthread_create(&async->tid, NULL, run_thread, async);
if (err) {
- error("cannot create thread: %s", strerror(err));
+ error_errno("cannot create thread");
goto error;
}
}
@@ -815,7 +824,10 @@ const char *find_hook(const char *name)
static struct strbuf path = STRBUF_INIT;
strbuf_reset(&path);
- strbuf_git_path(&path, "hooks/%s", name);
+ if (git_hooks_path)
+ strbuf_addf(&path, "%s/%s", git_hooks_path, name);
+ else
+ strbuf_git_path(&path, "hooks/%s", name);
if (access(path.buf, X_OK) < 0)
return NULL;
return path.buf;
@@ -902,7 +914,7 @@ struct parallel_processes {
struct strbuf buffered_output; /* of finished children */
};
-static int default_start_failure(struct strbuf *err,
+static int default_start_failure(struct strbuf *out,
void *pp_cb,
void *pp_task_cb)
{
@@ -910,7 +922,7 @@ static int default_start_failure(struct strbuf *err,
}
static int default_task_finished(int result,
- struct strbuf *err,
+ struct strbuf *out,
void *pp_cb,
void *pp_task_cb)
{
@@ -994,7 +1006,7 @@ static void pp_cleanup(struct parallel_processes *pp)
* When get_next_task added messages to the buffer in its last
* iteration, the buffered output is non empty.
*/
- fputs(pp->buffered_output.buf, stderr);
+ strbuf_write(&pp->buffered_output, stderr);
strbuf_release(&pp->buffered_output);
sigchain_pop_common();
@@ -1079,7 +1091,7 @@ static void pp_output(struct parallel_processes *pp)
int i = pp->output_owner;
if (pp->children[i].state == GIT_CP_WORKING &&
pp->children[i].err.len) {
- fputs(pp->children[i].err.buf, stderr);
+ strbuf_write(&pp->children[i].err, stderr);
strbuf_reset(&pp->children[i].err);
}
}
@@ -1117,11 +1129,11 @@ static int pp_collect_finished(struct parallel_processes *pp)
strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
strbuf_reset(&pp->children[i].err);
} else {
- fputs(pp->children[i].err.buf, stderr);
+ strbuf_write(&pp->children[i].err, stderr);
strbuf_reset(&pp->children[i].err);
/* Output all other finished child processes */
- fputs(pp->buffered_output.buf, stderr);
+ strbuf_write(&pp->buffered_output, stderr);
strbuf_reset(&pp->buffered_output);
/*
diff --git a/run-command.h b/run-command.h
index 3d1e59e26e..11f76b04ed 100644
--- a/run-command.h
+++ b/run-command.h
@@ -116,6 +116,7 @@ struct async {
int proc_in;
int proc_out;
#endif
+ int isolate_sigpipe;
};
int start_async(struct async *async);
@@ -140,7 +141,7 @@ void NORETURN async_exit(int code);
* return the negative signal number.
*/
typedef int (*get_next_task_fn)(struct child_process *cp,
- struct strbuf *err,
+ struct strbuf *out,
void *pp_cb,
void **pp_task_cb);
@@ -149,7 +150,7 @@ typedef int (*get_next_task_fn)(struct child_process *cp,
* a new process.
*
* You must not write to stdout or stderr in this function. Add your
- * message to the strbuf err instead, which will be printed without
+ * message to the strbuf out instead, which will be printed without
* messing up the output of the other parallel processes.
*
* pp_cb is the callback cookie as passed into run_processes_parallel,
@@ -159,7 +160,7 @@ typedef int (*get_next_task_fn)(struct child_process *cp,
* To send a signal to other child processes for abortion, return
* the negative signal number.
*/
-typedef int (*start_failure_fn)(struct strbuf *err,
+typedef int (*start_failure_fn)(struct strbuf *out,
void *pp_cb,
void *pp_task_cb);
@@ -167,7 +168,7 @@ typedef int (*start_failure_fn)(struct strbuf *err,
* This callback is called on every child process that finished processing.
*
* You must not write to stdout or stderr in this function. Add your
- * message to the strbuf err instead, which will be printed without
+ * message to the strbuf out instead, which will be printed without
* messing up the output of the other parallel processes.
*
* pp_cb is the callback cookie as passed into run_processes_parallel,
@@ -178,7 +179,7 @@ typedef int (*start_failure_fn)(struct strbuf *err,
* the negative signal number.
*/
typedef int (*task_finished_fn)(int result,
- struct strbuf *err,
+ struct strbuf *out,
void *pp_cb,
void *pp_task_cb);
diff --git a/send-pack.c b/send-pack.c
index 047bd18fde..37ee04ea3b 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -518,6 +518,7 @@ int send_pack(struct send_pack_args *args,
demux.proc = sideband_demux;
demux.data = fd;
demux.out = -1;
+ demux.isolate_sigpipe = 1;
if (start_async(&demux))
die("send-pack: unable to fork off sideband demultiplexer");
in = demux.out;
@@ -531,8 +532,10 @@ int send_pack(struct send_pack_args *args,
close(out);
if (git_connection_is_socket(conn))
shutdown(fd[0], SHUT_WR);
- if (use_sideband)
+ if (use_sideband) {
+ close(demux.out);
finish_async(&demux);
+ }
fd[1] = -1;
return -1;
}
@@ -551,11 +554,11 @@ int send_pack(struct send_pack_args *args,
packet_flush(out);
if (use_sideband && cmds_sent) {
+ close(demux.out);
if (finish_async(&demux)) {
error("error in sideband demultiplexer");
ret = -1;
}
- close(demux.out);
}
if (ret < 0)
diff --git a/sequencer.c b/sequencer.c
index e66f2fe0f0..4687ad4b80 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -875,8 +875,7 @@ static int sequencer_rollback(struct replay_opts *opts)
return rollback_single_pick();
}
if (!f)
- return error(_("cannot open %s: %s"), git_path_head_file(),
- strerror(errno));
+ return error_errno(_("cannot open %s"), git_path_head_file());
if (strbuf_getline_lf(&buf, f)) {
error(_("cannot read %s: %s"), git_path_head_file(),
ferror(f) ? strerror(errno) : _("unexpected end of file"));
diff --git a/server-info.c b/server-info.c
index 5a86e297b5..75dd677413 100644
--- a/server-info.c
+++ b/server-info.c
@@ -36,7 +36,7 @@ static int update_info_file(char *path, int (*generate)(FILE *))
out:
if (ret) {
- error("unable to update %s: %s", path, strerror(errno));
+ error_errno("unable to update %s", path);
if (fp)
fclose(fp);
else if (fd >= 0)
diff --git a/sha1_file.c b/sha1_file.c
index d0f2aa029b..d5e11217f5 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -301,7 +301,7 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,
return -1;
}
}
- if (!strcmp_icase(ent->base, normalized_objdir)) {
+ if (!fspathcmp(ent->base, normalized_objdir)) {
free(ent);
return -1;
}
@@ -1107,9 +1107,8 @@ unsigned char *use_pack(struct packed_git *p,
PROT_READ, MAP_PRIVATE,
p->pack_fd, win->offset);
if (win->base == MAP_FAILED)
- die("packfile %s cannot be mapped: %s",
- p->pack_name,
- strerror(errno));
+ die_errno("packfile %s cannot be mapped",
+ p->pack_name);
if (!win->offset && win->len == p->pack_size
&& !p->do_not_close)
close_pack_fd(p);
@@ -1279,8 +1278,8 @@ static void prepare_packed_git_one(char *objdir, int local)
dir = opendir(path.buf);
if (!dir) {
if (errno != ENOENT)
- error("unable to open object pack directory: %s: %s",
- path.buf, strerror(errno));
+ error_errno("unable to open object pack directory: %s",
+ path.buf);
strbuf_release(&path);
return;
}
@@ -2984,7 +2983,7 @@ int finalize_object_file(const char *tmpfile, const char *filename)
unlink_or_warn(tmpfile);
if (ret) {
if (ret != EEXIST) {
- return error("unable to write sha1 filename %s: %s", filename, strerror(ret));
+ return error_errno("unable to write sha1 filename %s", filename);
}
/* FIXME!!! Collision check here ? */
}
@@ -2998,7 +2997,7 @@ out:
static int write_buffer(int fd, const void *buf, size_t len)
{
if (write_in_full(fd, buf, len) < 0)
- return error("file write error (%s)", strerror(errno));
+ return error_errno("file write error");
return 0;
}
@@ -3081,7 +3080,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
if (errno == EACCES)
return error("insufficient permission for adding an object to repository database %s", get_object_directory());
else
- return error("unable to create temporary file: %s", strerror(errno));
+ return error_errno("unable to create temporary file");
}
/* Set it up */
@@ -3126,8 +3125,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
utb.actime = mtime;
utb.modtime = mtime;
if (utime(tmp_file.buf, &utb) < 0)
- warning("failed utime() on %s: %s",
- tmp_file.buf, strerror(errno));
+ warning_errno("failed utime() on %s", tmp_file.buf);
}
return finalize_object_file(tmp_file.buf, filename);
@@ -3360,7 +3358,7 @@ static int index_core(unsigned char *sha1, int fd, size_t size,
if (size == read_in_full(fd, buf, size))
ret = index_mem(sha1, buf, size, type, path, flags);
else
- ret = error("short read %s", strerror(errno));
+ ret = error_errno("short read");
free(buf);
} else {
void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
@@ -3425,18 +3423,14 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned
case S_IFREG:
fd = open(path, O_RDONLY);
if (fd < 0)
- return error("open(\"%s\"): %s", path,
- strerror(errno));
+ return error_errno("open(\"%s\")", path);
if (index_fd(sha1, fd, st, OBJ_BLOB, path, flags) < 0)
return error("%s: failed to insert into database",
path);
break;
case S_IFLNK:
- if (strbuf_readlink(&sb, path, st->st_size)) {
- char *errstr = strerror(errno);
- return error("readlink(\"%s\"): %s", path,
- errstr);
- }
+ if (strbuf_readlink(&sb, path, st->st_size))
+ return error_errno("readlink(\"%s\")", path);
if (!(flags & HASH_WRITE_OBJECT))
hash_sha1_file(sb.buf, sb.len, blob_type, sha1);
else if (write_sha1_file(sb.buf, sb.len, blob_type, sha1))
@@ -3492,7 +3486,7 @@ static int for_each_file_in_obj_subdir(int subdir_nr,
if (!dir) {
if (errno == ENOENT)
return 0;
- return error("unable to open %s: %s", path->buf, strerror(errno));
+ return error_errno("unable to open %s", path->buf);
}
while ((de = readdir(dir))) {
diff --git a/sha1_name.c b/sha1_name.c
index 776101e8d7..ca7ddd6f2c 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -1215,6 +1215,15 @@ int get_sha1(const char *name, unsigned char *sha1)
}
/*
+ * This is like "get_sha1()", but for struct object_id.
+ */
+int get_oid(const char *name, struct object_id *oid)
+{
+ return get_sha1(name, oid->hash);
+}
+
+
+/*
* Many callers know that the user meant to name a commit-ish by
* syntactical positions where the object name appears. Calling this
* function allows the machinery to disambiguate shorter-than-unique
diff --git a/split-index.c b/split-index.c
index 968b780a06..3c75d4b9ce 100644
--- a/split-index.c
+++ b/split-index.c
@@ -60,7 +60,7 @@ static void mark_base_index_entries(struct index_state *base)
* To keep track of the shared entries between
* istate->base->cache[] and istate->cache[], base entry
* position is stored in each base entry. All positions start
- * from 1 instead of 0, which is resrved to say "this is a new
+ * from 1 instead of 0, which is reserved to say "this is a new
* entry".
*/
for (i = 0; i < base->cache_nr; i++)
diff --git a/strbuf.c b/strbuf.c
index 2c08dbb153..1ba600bd78 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -395,6 +395,12 @@ ssize_t strbuf_read_once(struct strbuf *sb, int fd, size_t hint)
return cnt;
}
+ssize_t strbuf_write(struct strbuf *sb, FILE *f)
+{
+ return sb->len ? fwrite(sb->buf, 1, sb->len, f) : 0;
+}
+
+
#define STRBUF_MAXLINK (2*PATH_MAX)
int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
diff --git a/strbuf.h b/strbuf.h
index f72fd14c2e..7987405313 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -387,6 +387,12 @@ extern ssize_t strbuf_read_file(struct strbuf *sb, const char *path, size_t hint
extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
/**
+ * Write the whole content of the strbuf to the stream not stopping at
+ * NUL bytes.
+ */
+extern ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
+
+/**
* Read a line from a FILE *, overwriting the existing contents of
* the strbuf. The strbuf_getline*() family of functions share
* this signature, but have different line termination conventions.
diff --git a/string-list.c b/string-list.c
index 2a32a3f1f5..62d20846cb 100644
--- a/string-list.c
+++ b/string-list.c
@@ -231,12 +231,12 @@ void string_list_sort(struct string_list *list)
struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
const char *string)
{
- int i;
+ struct string_list_item *item;
compare_strings_fn cmp = list->cmp ? list->cmp : strcmp;
- for (i = 0; i < list->nr; i++)
- if (!cmp(string, list->items[i].string))
- return list->items + i;
+ for_each_string_list_item(item, list)
+ if (!cmp(string, item->string))
+ return item;
return NULL;
}
diff --git a/submodule-config.c b/submodule-config.c
index 8476e0fa0f..debab294d4 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -30,7 +30,7 @@ enum lookup_type {
lookup_path
};
-static struct submodule_cache cache;
+static struct submodule_cache the_submodule_cache;
static int is_cache_init;
static int config_path_cmp(const struct submodule_entry *a,
@@ -59,6 +59,7 @@ static void free_one_config(struct submodule_entry *entry)
{
free((void *) entry->config->path);
free((void *) entry->config->name);
+ free((void *) entry->config->update_strategy.command);
free(entry->config);
}
@@ -194,6 +195,8 @@ static struct submodule *lookup_or_create_by_name(struct submodule_cache *cache,
submodule->path = NULL;
submodule->url = NULL;
+ submodule->update_strategy.type = SM_UPDATE_UNSPECIFIED;
+ submodule->update_strategy.command = NULL;
submodule->fetch_recurse = RECURSE_SUBMODULES_NONE;
submodule->ignore = NULL;
@@ -293,7 +296,7 @@ static int parse_config(const char *var, const char *value, void *data)
if (!strcmp(item.buf, "path")) {
if (!value)
ret = config_error_nonbool(var);
- else if (!me->overwrite && submodule->path != NULL)
+ else if (!me->overwrite && submodule->path)
warn_multiple_config(me->commit_sha1, submodule->name,
"path");
else {
@@ -317,7 +320,7 @@ static int parse_config(const char *var, const char *value, void *data)
} else if (!strcmp(item.buf, "ignore")) {
if (!value)
ret = config_error_nonbool(var);
- else if (!me->overwrite && submodule->ignore != NULL)
+ else if (!me->overwrite && submodule->ignore)
warn_multiple_config(me->commit_sha1, submodule->name,
"ignore");
else if (strcmp(value, "untracked") &&
@@ -333,13 +336,23 @@ static int parse_config(const char *var, const char *value, void *data)
} else if (!strcmp(item.buf, "url")) {
if (!value) {
ret = config_error_nonbool(var);
- } else if (!me->overwrite && submodule->url != NULL) {
+ } else if (!me->overwrite && submodule->url) {
warn_multiple_config(me->commit_sha1, submodule->name,
"url");
} else {
free((void *) submodule->url);
submodule->url = xstrdup(value);
}
+ } else if (!strcmp(item.buf, "update")) {
+ if (!value)
+ ret = config_error_nonbool(var);
+ else if (!me->overwrite &&
+ submodule->update_strategy.type != SM_UPDATE_UNSPECIFIED)
+ warn_multiple_config(me->commit_sha1, submodule->name,
+ "update");
+ else if (parse_submodule_update_strategy(value,
+ &submodule->update_strategy) < 0)
+ die(_("invalid value for %s"), var);
}
strbuf_release(&name);
@@ -457,14 +470,14 @@ static void ensure_cache_init(void)
if (is_cache_init)
return;
- cache_init(&cache);
+ cache_init(&the_submodule_cache);
is_cache_init = 1;
}
int parse_submodule_config_option(const char *var, const char *value)
{
struct parse_config_parameter parameter;
- parameter.cache = &cache;
+ parameter.cache = &the_submodule_cache;
parameter.commit_sha1 = NULL;
parameter.gitmodules_sha1 = null_sha1;
parameter.overwrite = 1;
@@ -477,18 +490,18 @@ const struct submodule *submodule_from_name(const unsigned char *commit_sha1,
const char *name)
{
ensure_cache_init();
- return config_from_name(&cache, commit_sha1, name);
+ return config_from_name(&the_submodule_cache, commit_sha1, name);
}
const struct submodule *submodule_from_path(const unsigned char *commit_sha1,
const char *path)
{
ensure_cache_init();
- return config_from_path(&cache, commit_sha1, path);
+ return config_from_path(&the_submodule_cache, commit_sha1, path);
}
void submodule_free(void)
{
- cache_free(&cache);
+ cache_free(&the_submodule_cache);
is_cache_init = 0;
}
diff --git a/submodule-config.h b/submodule-config.h
index 9bfa65af03..e4857f53a8 100644
--- a/submodule-config.h
+++ b/submodule-config.h
@@ -2,6 +2,7 @@
#define SUBMODULE_CONFIG_CACHE_H
#include "hashmap.h"
+#include "submodule.h"
#include "strbuf.h"
/*
@@ -14,6 +15,7 @@ struct submodule {
const char *url;
int fetch_recurse;
const char *ignore;
+ struct submodule_update_strategy update_strategy;
/* the sha1 blob id of the responsible .gitmodules file */
unsigned char gitmodules_sha1[20];
};
diff --git a/submodule.c b/submodule.c
index 62c4356c50..4532b11d66 100644
--- a/submodule.c
+++ b/submodule.c
@@ -13,8 +13,10 @@
#include "argv-array.h"
#include "blob.h"
#include "thread-utils.h"
+#include "quote.h"
static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
+static int parallel_jobs = 1;
static struct string_list changed_submodule_paths;
static int initialized_fetch_ref_tips;
static struct sha1_array ref_tips_before_fetch;
@@ -169,7 +171,12 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
int submodule_config(const char *var, const char *value, void *cb)
{
- if (starts_with(var, "submodule."))
+ if (!strcmp(var, "submodule.fetchjobs")) {
+ parallel_jobs = git_config_int(var, value);
+ if (parallel_jobs < 0)
+ die(_("negative values not allowed for submodule.fetchJobs"));
+ return 0;
+ } else if (starts_with(var, "submodule."))
return parse_submodule_config_option(var, value);
else if (!strcmp(var, "fetch.recursesubmodules")) {
config_fetch_recurse_submodules = parse_fetch_recurse_submodules_arg(var, value);
@@ -210,6 +217,48 @@ void gitmodules_config(void)
}
}
+int parse_submodule_update_strategy(const char *value,
+ struct submodule_update_strategy *dst)
+{
+ free((void*)dst->command);
+ dst->command = NULL;
+ if (!strcmp(value, "none"))
+ dst->type = SM_UPDATE_NONE;
+ else if (!strcmp(value, "checkout"))
+ dst->type = SM_UPDATE_CHECKOUT;
+ else if (!strcmp(value, "rebase"))
+ dst->type = SM_UPDATE_REBASE;
+ else if (!strcmp(value, "merge"))
+ dst->type = SM_UPDATE_MERGE;
+ else if (skip_prefix(value, "!", &value)) {
+ dst->type = SM_UPDATE_COMMAND;
+ dst->command = xstrdup(value);
+ } else
+ return -1;
+ return 0;
+}
+
+const char *submodule_strategy_to_string(const struct submodule_update_strategy *s)
+{
+ struct strbuf sb = STRBUF_INIT;
+ switch (s->type) {
+ case SM_UPDATE_CHECKOUT:
+ return "checkout";
+ case SM_UPDATE_MERGE:
+ return "merge";
+ case SM_UPDATE_REBASE:
+ return "rebase";
+ case SM_UPDATE_NONE:
+ return "none";
+ case SM_UPDATE_UNSPECIFIED:
+ return NULL;
+ case SM_UPDATE_COMMAND:
+ strbuf_addf(&sb, "!%s", s->command);
+ return strbuf_detach(&sb, NULL);
+ }
+ return NULL;
+}
+
void handle_ignore_submodules_arg(struct diff_options *diffopt,
const char *arg)
{
@@ -366,7 +415,7 @@ static int submodule_needs_pushing(const char *path, const unsigned char sha1[20
argv[1] = sha1_to_hex(sha1);
cp.argv = argv;
- cp.env = local_repo_env;
+ prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
cp.no_stdin = 1;
cp.out = -1;
@@ -453,7 +502,7 @@ static int push_submodule(const char *path)
const char *argv[] = {"push", NULL};
cp.argv = argv;
- cp.env = local_repo_env;
+ prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
cp.no_stdin = 1;
cp.dir = path;
@@ -499,7 +548,7 @@ static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
argv[3] = sha1_to_hex(sha1);
cp.argv = argv;
- cp.env = local_repo_env;
+ prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
cp.no_stdin = 1;
cp.dir = path;
@@ -682,7 +731,7 @@ static int get_next_submodule(struct child_process *cp,
if (is_directory(git_dir)) {
child_process_init(cp);
cp->dir = strbuf_detach(&submodule_path, NULL);
- cp->env = local_repo_env;
+ prepare_submodule_repo_env(&cp->env_array);
cp->git_cmd = 1;
if (!spf->quiet)
strbuf_addf(err, "Fetching submodule %s%s\n",
@@ -750,6 +799,9 @@ int fetch_populated_submodules(const struct argv_array *options,
argv_array_push(&spf.args, "--recurse-submodules-default");
/* default value, "--submodule-prefix" and its value are added later */
+ if (max_parallel_jobs < 0)
+ max_parallel_jobs = parallel_jobs;
+
calculate_changed_submodule_paths();
run_processes_parallel(max_parallel_jobs,
get_next_submodule,
@@ -794,7 +846,7 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
argv[2] = "-uno";
cp.argv = argv;
- cp.env = local_repo_env;
+ prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
cp.no_stdin = 1;
cp.out = -1;
@@ -855,7 +907,7 @@ int submodule_uses_gitfile(const char *path)
/* Now test that all nested submodules use a gitfile too */
cp.argv = argv;
- cp.env = local_repo_env;
+ prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
cp.no_stdin = 1;
cp.no_stderr = 1;
@@ -888,7 +940,7 @@ int ok_to_remove_submodule(const char *path)
return 0;
cp.argv = argv;
- cp.env = local_repo_env;
+ prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
cp.no_stdin = 1;
cp.out = -1;
@@ -1094,3 +1146,18 @@ void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)
strbuf_release(&rel_path);
free((void *)real_work_tree);
}
+
+int parallel_submodules(void)
+{
+ return parallel_jobs;
+}
+
+void prepare_submodule_repo_env(struct argv_array *out)
+{
+ const char * const *var;
+
+ for (var = local_repo_env; *var; var++) {
+ if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
+ argv_array_push(out, *var);
+ }
+}
diff --git a/submodule.h b/submodule.h
index e06eaa5ebb..2af9390998 100644
--- a/submodule.h
+++ b/submodule.h
@@ -14,6 +14,21 @@ enum {
RECURSE_SUBMODULES_ON = 2
};
+enum submodule_update_type {
+ SM_UPDATE_UNSPECIFIED = 0,
+ SM_UPDATE_CHECKOUT,
+ SM_UPDATE_REBASE,
+ SM_UPDATE_MERGE,
+ SM_UPDATE_NONE,
+ SM_UPDATE_COMMAND
+};
+
+struct submodule_update_strategy {
+ enum submodule_update_type type;
+ const char *command;
+};
+#define SUBMODULE_UPDATE_STRATEGY_INIT {SM_UPDATE_UNSPECIFIED, NULL}
+
int is_staging_gitmodules_ok(void);
int update_path_in_gitmodules(const char *oldpath, const char *newpath);
int remove_path_from_gitmodules(const char *path);
@@ -22,6 +37,9 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
const char *path);
int submodule_config(const char *var, const char *value, void *cb);
void gitmodules_config(void);
+int parse_submodule_update_strategy(const char *value,
+ struct submodule_update_strategy *dst);
+const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
void show_submodule_summary(FILE *f, const char *path,
const char *line_prefix,
@@ -42,5 +60,13 @@ int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_nam
struct string_list *needs_pushing);
int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name);
void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
+int parallel_submodules(void);
+
+/*
+ * Prepare the "env_array" parameter of a "struct child_process" for executing
+ * a submodule by clearing any repo-specific envirionment variables, but
+ * retaining any config in the environment.
+ */
+void prepare_submodule_repo_env(struct argv_array *out);
#endif
diff --git a/t/README b/t/README
index 1dc908e43a..76a0daa3ac 100644
--- a/t/README
+++ b/t/README
@@ -84,9 +84,9 @@ appropriately before running "make".
-x::
Turn on shell tracing (i.e., `set -x`) during the tests
- themselves. Implies `--verbose`. Note that this can cause
- failures in some tests which redirect and test the
- output of shell functions. Use with caution.
+ 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::
diff --git a/t/helper/.gitignore b/t/helper/.gitignore
new file mode 100644
index 0000000000..d6e8b36798
--- /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/test-chmtime.c b/t/helper/test-chmtime.c
index dfe8a83261..dfe8a83261 100644
--- a/test-chmtime.c
+++ b/t/helper/test-chmtime.c
diff --git a/test-config.c b/t/helper/test-config.c
index 6a77552210..6a77552210 100644
--- a/test-config.c
+++ b/t/helper/test-config.c
diff --git a/test-ctype.c b/t/helper/test-ctype.c
index 707a821f03..707a821f03 100644
--- a/test-ctype.c
+++ b/t/helper/test-ctype.c
diff --git a/test-date.c b/t/helper/test-date.c
index 63f373557e..63f373557e 100644
--- a/test-date.c
+++ b/t/helper/test-date.c
diff --git a/test-delta.c b/t/helper/test-delta.c
index 4595cd6433..4595cd6433 100644
--- a/test-delta.c
+++ b/t/helper/test-delta.c
diff --git a/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c
index bb53c0aa65..bb53c0aa65 100644
--- a/test-dump-cache-tree.c
+++ b/t/helper/test-dump-cache-tree.c
diff --git a/test-dump-split-index.c b/t/helper/test-dump-split-index.c
index 861d28c9b6..861d28c9b6 100644
--- a/test-dump-split-index.c
+++ b/t/helper/test-dump-split-index.c
diff --git a/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c
index 0a1c285246..0a1c285246 100644
--- a/test-dump-untracked-cache.c
+++ b/t/helper/test-dump-untracked-cache.c
diff --git a/test-fake-ssh.c b/t/helper/test-fake-ssh.c
index 980de216e1..980de216e1 100644
--- a/test-fake-ssh.c
+++ b/t/helper/test-fake-ssh.c
diff --git a/test-genrandom.c b/t/helper/test-genrandom.c
index 54824d0754..54824d0754 100644
--- a/test-genrandom.c
+++ b/t/helper/test-genrandom.c
diff --git a/test-hashmap.c b/t/helper/test-hashmap.c
index cc2891dd97..cc2891dd97 100644
--- a/test-hashmap.c
+++ b/t/helper/test-hashmap.c
diff --git a/test-index-version.c b/t/helper/test-index-version.c
index 05d4699c4a..05d4699c4a 100644
--- a/test-index-version.c
+++ b/t/helper/test-index-version.c
diff --git a/test-line-buffer.c b/t/helper/test-line-buffer.c
index 1e58f0476f..1e58f0476f 100644
--- a/test-line-buffer.c
+++ b/t/helper/test-line-buffer.c
diff --git a/test-match-trees.c b/t/helper/test-match-trees.c
index 4dad7095f1..d446b8eaca 100644
--- a/test-match-trees.c
+++ b/t/helper/test-match-trees.c
@@ -3,24 +3,24 @@
int main(int ac, char **av)
{
- unsigned char hash1[20], hash2[20], shifted[20];
+ struct object_id hash1, hash2, shifted;
struct tree *one, *two;
setup_git_directory();
- if (get_sha1(av[1], hash1))
+ if (get_oid(av[1], &hash1))
die("cannot parse %s as an object name", av[1]);
- if (get_sha1(av[2], hash2))
+ if (get_oid(av[2], &hash2))
die("cannot parse %s as an object name", av[2]);
- one = parse_tree_indirect(hash1);
+ one = parse_tree_indirect(hash1.hash);
if (!one)
die("not a tree-ish %s", av[1]);
- two = parse_tree_indirect(hash2);
+ two = parse_tree_indirect(hash2.hash);
if (!two)
die("not a tree-ish %s", av[2]);
- shift_tree(one->object.oid.hash, two->object.oid.hash, shifted, -1);
- printf("shifted: %s\n", sha1_to_hex(shifted));
+ shift_tree(&one->object.oid, &two->object.oid, &shifted, -1);
+ printf("shifted: %s\n", oid_to_hex(&shifted));
exit(0);
}
diff --git a/test-mergesort.c b/t/helper/test-mergesort.c
index ea3b959e94..ea3b959e94 100644
--- a/test-mergesort.c
+++ b/t/helper/test-mergesort.c
diff --git a/test-mktemp.c b/t/helper/test-mktemp.c
index c8c54213a3..c8c54213a3 100644
--- a/test-mktemp.c
+++ b/t/helper/test-mktemp.c
diff --git a/test-parse-options.c b/t/helper/test-parse-options.c
index 2c8c8f18ed..8a1235d03e 100644
--- a/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -7,16 +7,25 @@ static int integer = 0;
static unsigned long magnitude = 0;
static unsigned long timestamp;
static int abbrev = 7;
-static int verbose = 0, dry_run = 0, quiet = 0;
+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)
{
- printf("Callback: \"%s\", %d\n",
- (arg ? arg : "not set"), unset);
+ length_cb.called = 1;
+ length_cb.arg = arg;
+ length_cb.unset = unset;
+
if (unset)
return 1; /* do not support unset */
@@ -30,6 +39,61 @@ static int number_callback(const struct option *opt, const char *arg, int unset)
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/";
@@ -37,6 +101,7 @@ int main(int argc, char **argv)
"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-'"),
@@ -77,28 +142,38 @@ int main(int argc, char **argv)
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);
- printf("boolean: %d\n", boolean);
- printf("integer: %d\n", integer);
- printf("magnitude: %lu\n", magnitude);
- printf("timestamp: %lu\n", timestamp);
- printf("string: %s\n", string ? string : "(not set)");
- printf("abbrev: %d\n", abbrev);
- printf("verbose: %d\n", verbose);
- printf("quiet: %s\n", quiet ? "yes" : "no");
- printf("dry run: %s\n", dry_run ? "yes" : "no");
- printf("file: %s\n", file ? file : "(not set)");
+ 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++)
- printf("list: %s\n", list.items[i].string);
+ show(&expect, &ret, "list: %s", list.items[i].string);
for (i = 0; i < argc; i++)
- printf("arg %02d: %s\n", i, argv[i]);
+ show(&expect, &ret, "arg %02d: %s", i, argv[i]);
- return 0;
+ return ret;
}
diff --git a/test-path-utils.c b/t/helper/test-path-utils.c
index ba805b374c..ba805b374c 100644
--- a/test-path-utils.c
+++ b/t/helper/test-path-utils.c
diff --git a/test-prio-queue.c b/t/helper/test-prio-queue.c
index 7be72f0086..7be72f0086 100644
--- a/test-prio-queue.c
+++ b/t/helper/test-prio-queue.c
diff --git a/test-read-cache.c b/t/helper/test-read-cache.c
index b25bcf139b..b25bcf139b 100644
--- a/test-read-cache.c
+++ b/t/helper/test-read-cache.c
diff --git a/test-regex.c b/t/helper/test-regex.c
index 0dc598ecdc..0dc598ecdc 100644
--- a/test-regex.c
+++ b/t/helper/test-regex.c
diff --git a/test-revision-walking.c b/t/helper/test-revision-walking.c
index 3d0313354b..3d0313354b 100644
--- a/test-revision-walking.c
+++ b/t/helper/test-revision-walking.c
diff --git a/test-run-command.c b/t/helper/test-run-command.c
index 30a64a98dc..30a64a98dc 100644
--- a/test-run-command.c
+++ b/t/helper/test-run-command.c
diff --git a/test-scrap-cache-tree.c b/t/helper/test-scrap-cache-tree.c
index 6efee31a48..6efee31a48 100644
--- a/test-scrap-cache-tree.c
+++ b/t/helper/test-scrap-cache-tree.c
diff --git a/test-sha1-array.c b/t/helper/test-sha1-array.c
index 60ea1d5f14..60ea1d5f14 100644
--- a/test-sha1-array.c
+++ b/t/helper/test-sha1-array.c
diff --git a/test-sha1.c b/t/helper/test-sha1.c
index e57eae10bf..e57eae10bf 100644
--- a/test-sha1.c
+++ b/t/helper/test-sha1.c
diff --git a/test-sha1.sh b/t/helper/test-sha1.sh
index cef4bcc866..750b95a0a1 100755
--- a/test-sha1.sh
+++ b/t/helper/test-sha1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
dd if=/dev/zero bs=1048576 count=100 2>/dev/null |
-/usr/bin/time ./test-sha1 >/dev/null
+/usr/bin/time t/helper/test-sha1 >/dev/null
while read expect cnt pfx
do
@@ -11,7 +11,7 @@ do
test -z "$pfx" || echo "$pfx"
dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null |
perl -pe 'y/\000/g/'
- } | ./test-sha1 $cnt
+ } | ./t/helper/test-sha1 $cnt
)
if test "$expect" = "$actual"
then
diff --git a/test-sigchain.c b/t/helper/test-sigchain.c
index e499fce60f..e499fce60f 100644
--- a/test-sigchain.c
+++ b/t/helper/test-sigchain.c
diff --git a/test-string-list.c b/t/helper/test-string-list.c
index 14bdf9d215..14bdf9d215 100644
--- a/test-string-list.c
+++ b/t/helper/test-string-list.c
diff --git a/test-submodule-config.c b/t/helper/test-submodule-config.c
index dab8c27768..dab8c27768 100644
--- a/test-submodule-config.c
+++ b/t/helper/test-submodule-config.c
diff --git a/test-subprocess.c b/t/helper/test-subprocess.c
index 56881a0324..56881a0324 100644
--- a/test-subprocess.c
+++ b/t/helper/test-subprocess.c
diff --git a/test-svn-fe.c b/t/helper/test-svn-fe.c
index 120ec96b0d..120ec96b0d 100644
--- a/test-svn-fe.c
+++ b/t/helper/test-svn-fe.c
diff --git a/test-urlmatch-normalization.c b/t/helper/test-urlmatch-normalization.c
index 090bf219a7..090bf219a7 100644
--- a/test-urlmatch-normalization.c
+++ b/t/helper/test-urlmatch-normalization.c
diff --git a/test-wildmatch.c b/t/helper/test-wildmatch.c
index 578b164fe6..578b164fe6 100644
--- a/test-wildmatch.c
+++ b/t/helper/test-wildmatch.c
diff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh
index f9ae1d780d..54fd5a6ca0 100644
--- a/t/lib-git-p4.sh
+++ b/t/lib-git-p4.sh
@@ -33,7 +33,7 @@ fi
# 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() {
+native_path () {
path="$1" &&
if test_have_prereq CYGWIN && ! p4 -V | grep -q CYGWIN
then
@@ -49,8 +49,8 @@ native_path() {
# 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() {
- python -c 'import time; print int(time.time())'
+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
@@ -75,7 +75,7 @@ git="$TRASH_DIRECTORY/git"
pidfile="$TRASH_DIRECTORY/p4d.pid"
# Sometimes "prove" seems to hang on exit because p4d is still running
-cleanup() {
+cleanup () {
if test -f "$pidfile"
then
kill -9 $(cat "$pidfile") 2>/dev/null && exit 255
@@ -89,7 +89,7 @@ trap cleanup EXIT
TMPDIR="$TRASH_DIRECTORY"
export TMPDIR
-start_p4d() {
+start_p4d () {
mkdir -p "$db" "$cli" "$git" &&
rm -f "$pidfile" &&
(
@@ -151,7 +151,7 @@ start_p4d() {
return 0
}
-p4_add_user() {
+p4_add_user () {
name=$1 &&
p4 user -f -i <<-EOF
User: $name
@@ -160,7 +160,16 @@ p4_add_user() {
EOF
}
-retry_until_success() {
+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
@@ -168,7 +177,7 @@ retry_until_success() {
done
}
-retry_until_fail() {
+retry_until_fail () {
timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
until ! "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
do
@@ -176,7 +185,7 @@ retry_until_fail() {
done
}
-kill_p4d() {
+kill_p4d () {
pid=$(cat "$pidfile")
retry_until_fail kill $pid
retry_until_fail kill -9 $pid
@@ -186,21 +195,22 @@ kill_p4d() {
retry_until_fail kill -9 $watchdog_pid
}
-cleanup_git() {
+cleanup_git () {
retry_until_success rm -r "$git"
test_must_fail test -d "$git" &&
retry_until_success mkdir "$git"
}
-marshal_dump() {
+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(sys.stdin)
- print d['$what']
+ d = marshal.load(instream)
+ print(d[b'$what'].decode('utf-8'))
EOF
"$PYTHON_PATH" "$TRASH_DIRECTORY/marshal-dump.py"
}
@@ -208,7 +218,7 @@ marshal_dump() {
#
# Construct a client with this list of View lines
#
-client_view() {
+client_view () {
(
cat <<-EOF &&
Client: $P4CLIENT
@@ -222,7 +232,7 @@ client_view() {
) | p4 client -i
}
-is_cli_file_writeable() {
+is_cli_file_writeable () {
# cygwin version of p4 does not set read-only attr,
# will be marked 444 but -w is true
file="$1" &&
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 6a50b8793e..fb8823224e 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'
diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
index db2ef22e8f..ec2aa8f687 100755
--- a/t/lib-gpg.sh
+++ b/t/lib-gpg.sh
@@ -1,9 +1,8 @@
#!/bin/sh
gpg_version=$(gpg --version 2>&1)
-if test $? = 127; then
- say "You do not seem to have gpg installed"
-else
+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.
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 9317ba0858..018a83a5a1 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -102,6 +102,10 @@ Alias /auth/dumb/ www/auth/dumb/
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/
@@ -128,6 +132,18 @@ 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
diff --git a/t/perf/p3404-rebase-interactive.sh b/t/perf/p3404-rebase-interactive.sh
new file mode 100755
index 0000000000..88f47de282
--- /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/perf-lib.sh b/t/perf/perf-lib.sh
index 5cf74eddec..5ef17440c0 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -80,23 +80,26 @@ test_perf_create_repo_from () {
error "bug in the test script: not 2 parameters to test-create-repo"
repo="$1"
source="$2"
- source_git=$source/$(cd "$source" && git rev-parse --git-dir)
+ source_git="$(git -C "$source" rev-parse --git-dir)"
+ objects_dir="$(git -C "$source" rev-parse --git-path objects)"
mkdir -p "$repo/.git"
(
- cd "$repo/.git" &&
- { cp -Rl "$source_git/objects" . 2>/dev/null ||
- cp -R "$source_git/objects" .; } &&
+ { 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)
+ */objects|*/hooks|*/config|*/commondir)
;;
*)
- cp -R "$stuff" . || exit 1
+ cp -R "$stuff" "$repo/.git/" || exit 1
;;
esac
done &&
- cd .. &&
- git init -q &&
+ 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'"
}
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 79b9074172..60811a3a7c 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -98,7 +98,7 @@ check_sub_test_lib_test () {
}
check_sub_test_lib_test_err () {
- name="$1" # stdin is the expected output output from the test
+ name="$1" # stdin is the expected output from the test
# expected error output is in descriptior 3
(
cd "$name" &&
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index a5b9e7a4c7..a6fdd5ef3a 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -354,4 +354,34 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' '
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/t0008-ignores.sh b/t/t0008-ignores.sh
index 89544dd833..b425f3a0d2 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -605,7 +605,7 @@ cat <<-EOF >expected-verbose
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: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
@@ -686,7 +686,7 @@ cat <<-EOF >expected-all
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:8:!on* "b/one\\"three"
a/b/.gitignore:9:!two b/two
:: b/not-ignored
a/.gitignore:1:two* b/twooo
diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh
index f33962b178..93725895a4 100755
--- a/t/t0027-auto-crlf.sh
+++ b/t/t0027-auto-crlf.sh
@@ -12,7 +12,7 @@ fi
compare_files () {
tr '\015\000' QN <"$1" >"$1".expect &&
- tr '\015\000' QN <"$2" >"$2".actual &&
+ tr '\015\000' QN <"$2" | tr -d 'Z' >"$2".actual &&
test_cmp "$1".expect "$2".actual &&
rm "$1".expect "$2".actual
}
@@ -52,14 +52,17 @@ create_gitattributes () {
create_NNO_files () {
for crlf in false true input
do
- for attr in "" auto text -text lf crlf
+ for attr in "" auto text -text
do
- pfx=NNO_${crlf}_attr_${attr} &&
- 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
+ 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
}
@@ -100,20 +103,22 @@ commit_check_warn () {
}
commit_chk_wrnNNO () {
- crlf=$1
- attr=$2
- lfwarn=$3
- crlfwarn=$4
- lfmixcrlf=$5
- lfmixcr=$6
- crlfnul=$7
- pfx=NNO_${crlf}_attr_${attr}
+ 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" &&
+ 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
@@ -121,19 +126,19 @@ commit_chk_wrnNNO () {
test_expect_success "commit NNO files crlf=$crlf attr=$attr LF" '
check_warning "$lfwarn" ${pfx}_LF.err
'
- test_expect_success "commit NNO files crlf=$crlf attr=$attr CRLF" '
+ 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 crlf=$crlf attr=$attr CRLF_mix_LF" '
+ 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 crlf=$crlf attr=$attr LF_mix_cr" '
+ 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 crlf=$crlf attr=$attr CRLF_nul" '
+ test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf CRLF_nul" '
check_warning "$crlfnul" ${pfx}_CRLF_nul.err
'
}
@@ -162,6 +167,7 @@ stats_ascii () {
# 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" ;;
@@ -169,8 +175,8 @@ attr_ascii () {
text,lf) echo "text eol=lf" ;;
text,crlf) echo "text eol=crlf" ;;
auto,) echo "text=auto" ;;
- auto,lf) echo "text=auto eol=lf" ;;
- auto,crlf) echo "text=auto eol=crlf" ;;
+ auto,lf) echo "text eol=lf" ;;
+ auto,crlf) echo "text eol=crlf" ;;
lf,) echo "text eol=lf" ;;
crlf,) echo "text eol=crlf" ;;
,) echo "" ;;
@@ -195,28 +201,29 @@ check_files_in_repo () {
}
check_in_repo_NNO () {
- crlf=$1
- attr=$2
- lfname=$3
- crlfname=$4
- lfmixcrlf=$5
- lfmixcr=$6
- crlfnul=$7
- pfx=NNO_${crlf}_attr_${attr}_
- test_expect_success "compare_files $lfname ${pfx}LF.txt" '
- compare_files $lfname ${pfx}LF.txt
+ 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 $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 $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 $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
+ test_expect_success "compare_files $crlfnul ${pfx}_CRLF_nul.txt" '
+ compare_files $crlfnul ${pfx}_CRLF_nul.txt
'
}
@@ -231,7 +238,7 @@ checkout_files () {
lfmixcrlf=$1 ; shift
lfmixcr=$1 ; shift
crlfnul=$1 ; shift
- create_gitattributes "$attr" "$ident" &&
+ 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
@@ -244,7 +251,7 @@ checkout_files () {
fi
done
- test_expect_success "ls-files --eol attr=$attr $ident $aeol core.autocrlf=$crlf core.eol=$ceol" '
+ 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
@@ -259,19 +266,19 @@ checkout_files () {
sort >actual &&
test_cmp expect actual
'
- test_expect_success "checkout $ident $attr $aeol core.autocrlf=$crlf core.eol=$ceol file=LF" "
+ 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 $ident $attr $aeol core.autocrlf=$crlf core.eol=$ceol file=CRLF" "
+ 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 $ident $attr $aeol core.autocrlf=$crlf core.eol=$ceol file=CRLF_mix_LF" "
+ 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 $ident $attr $aeol core.autocrlf=$crlf core.eol=$ceol file=LF_mix_CR" "
+ 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 $ident $attr $aeol core.autocrlf=$crlf core.eol=$ceol file=LF_nul" "
+ 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
"
}
@@ -385,31 +392,31 @@ test_expect_success 'commit files attr=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 "" "" "" "" "" ""
-
+# 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 false "auto" "$WILC" "$WICL" "$WAMIX" "" ""
-commit_chk_wrnNNO true "auto" "LF_CRLF" "" "LF_CRLF" "" ""
-commit_chk_wrnNNO input "auto" "" "CRLF_LF" "CRLF_LF" "" ""
+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 "" ""
-commit_chk_wrnNNO false "text" "$WILC" "$WICL" "$WAMIX" "$WILC" "$WICL"
-commit_chk_wrnNNO true "text" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" ""
-commit_chk_wrnNNO input "text" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF"
-
-commit_chk_wrnNNO false "-text" "" "" "" "" ""
-commit_chk_wrnNNO true "-text" "" "" "" "" ""
-commit_chk_wrnNNO input "-text" "" "" "" "" ""
-
-commit_chk_wrnNNO false "lf" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF"
-commit_chk_wrnNNO true "lf" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF"
-commit_chk_wrnNNO input "lf" "" "CRLF_LF" "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 false "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" ""
-commit_chk_wrnNNO true "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" ""
-commit_chk_wrnNNO input "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" ""
+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 &&
@@ -440,24 +447,20 @@ test_expect_success 'commit -text' '
check_files_in_repo input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
'
-# attr LF CRLF CRLF_mix_LF LF_mix_CR CRLFNUL
-check_in_repo_NNO false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_in_repo_NNO true "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_in_repo_NNO input "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-
-check_in_repo_NNO false "auto" LF LF LF LF_mix_CR CRLF_nul
-check_in_repo_NNO true "auto" LF LF LF LF_mix_CR CRLF_nul
-check_in_repo_NNO input "auto" LF LF LF LF_mix_CR CRLF_nul
-
-check_in_repo_NNO false "text" LF LF LF LF_mix_CR LF_nul
-check_in_repo_NNO true "text" LF LF LF LF_mix_CR LF_nul
-check_in_repo_NNO input "text" LF LF LF LF_mix_CR LF_nul
-
-check_in_repo_NNO false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_in_repo_NNO true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_in_repo_NNO 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:
@@ -489,89 +492,47 @@ LFNUL=LF_nul
fi
export CRLF_MIX_LF_CR MIX NL
-checkout_files "" "" "" false "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" "" "" false crlf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" "" "" false lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" "" "" false native LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" "" "" input "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" "" "" input lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" "" "" true "" CRLF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" "" "" true crlf CRLF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" "" "" true lf CRLF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" "" "" true native CRLF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" ident "" false "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" ident "" false crlf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" ident "" false lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" ident "" false native LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" ident "" input "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" ident "" input lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" ident "" true "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" ident "" true crlf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" ident "" true lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "" ident "" true native LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "auto" "" "" false "" $NL CRLF $MIX_CRLF_LF LF_mix_CR LF_nul
-checkout_files "auto" "" "" false crlf CRLF CRLF CRLF LF_mix_CR LF_nul
-checkout_files "auto" "" "" false lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "auto" "" "" false native $NL CRLF $MIX_CRLF_LF LF_mix_CR LF_nul
-checkout_files "auto" "" "" input "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "auto" "" "" input lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "auto" "" "" true "" CRLF CRLF CRLF LF_mix_CR LF_nul
-checkout_files "auto" "" "" true crlf CRLF CRLF CRLF LF_mix_CR LF_nul
-checkout_files "auto" "" "" true lf CRLF CRLF CRLF LF_mix_CR LF_nul
-checkout_files "auto" "" "" true native CRLF CRLF CRLF LF_mix_CR LF_nul
-checkout_files "auto" ident "" false "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "auto" ident "" false crlf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "auto" ident "" false lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "auto" ident "" false native LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "auto" ident "" input "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "auto" ident "" input lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "auto" ident "" true "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "auto" ident "" true crlf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "auto" ident "" true lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-checkout_files "auto" ident "" true native LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
-
-for id in "" ident;
+# Same handling with and without ident
+for id in "" ident
do
- checkout_files "crlf" "$id" "" false "" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
- checkout_files "crlf" "$id" "" false crlf CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
- checkout_files "crlf" "$id" "" false lf CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
- checkout_files "crlf" "$id" "" false native CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
- checkout_files "crlf" "$id" "" input "" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
- checkout_files "crlf" "$id" "" input lf CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
- checkout_files "crlf" "$id" "" true "" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
- checkout_files "crlf" "$id" "" true crlf CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
- checkout_files "crlf" "$id" "" true lf CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
- checkout_files "crlf" "$id" "" true native CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
- checkout_files "lf" "$id" "" false "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "lf" "$id" "" false crlf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "lf" "$id" "" false lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "lf" "$id" "" false native LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "lf" "$id" "" input "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "lf" "$id" "" input lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "lf" "$id" "" true "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "lf" "$id" "" true crlf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "lf" "$id" "" true lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "lf" "$id" "" true native LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "text" "$id" "" false "" $NL CRLF $MIX_CRLF_LF $MIX_LF_CR $LFNUL
- 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
- checkout_files "text" "$id" "" false native $NL CRLF $MIX_CRLF_LF $MIX_LF_CR $LFNUL
- checkout_files "text" "$id" "" input "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "text" "$id" "" input lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "text" "$id" "" true "" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
- checkout_files "text" "$id" "" true crlf CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
- checkout_files "text" "$id" "" true lf CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
- checkout_files "text" "$id" "" true native CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
- checkout_files "-text" "$id" "" false "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "-text" "$id" "" false crlf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "-text" "$id" "" false lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "-text" "$id" "" false native LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "-text" "$id" "" input "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "-text" "$id" "" input lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "-text" "$id" "" true "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "-text" "$id" "" true crlf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "-text" "$id" "" true lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
- checkout_files "-text" "$id" "" true native LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ 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
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index 9be6411104..db5f60d0c5 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -7,7 +7,7 @@ test_description='our own option parser'
. ./test-lib.sh
-cat > expect << EOF
+cat >expect <<\EOF
usage: test-parse-options <options>
--yes get a boolean
@@ -45,63 +45,24 @@ 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_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.template <<EOF
-boolean: 0
-integer: 0
-magnitude: 0
-timestamp: 0
-string: (not set)
-abbrev: 7
-verbose: 0
-quiet: no
-dry run: no
-file: (not set)
-EOF
-
-check() {
+check () {
what="$1" &&
shift &&
expect="$1" &&
shift &&
- sed "s/^$what .*/$what $expect/" <expect.template >expect &&
- test-parse-options $* >output 2>output.err &&
- test_must_be_empty output.err &&
- test_cmp expect output
-}
-
-check_i18n() {
- what="$1" &&
- shift &&
- expect="$1" &&
- shift &&
- sed "s/^$what .*/$what $expect/" <expect.template >expect &&
- test-parse-options $* >output 2>output.err &&
- test_must_be_empty output.err &&
- test_i18ncmp expect output
-}
-
-check_unknown() {
- case "$1" in
- --*)
- echo error: unknown option \`${1#--}\' >expect ;;
- -*)
- echo error: unknown switch \`${1#-}\' >expect ;;
- esac &&
- cat expect.err >>expect &&
- test_must_fail test-parse-options $* >output 2>output.err &&
- test_must_be_empty output &&
- test_cmp expect output.err
+ test-parse-options --expect="$what $expect" "$@"
}
check_unknown_i18n() {
@@ -156,7 +117,7 @@ test_expect_success 'OPT_MAGNITUDE() 3giga' '
check magnitude: 3221225472 -m 3g
'
-cat > expect << EOF
+cat >expect <<\EOF
boolean: 2
integer: 1729
magnitude: 16384
@@ -164,7 +125,7 @@ timestamp: 0
string: 123
abbrev: 7
verbose: 2
-quiet: no
+quiet: 0
dry run: yes
file: prefix/my.file
EOF
@@ -176,7 +137,7 @@ test_expect_success 'short options' '
test_must_be_empty output.err
'
-cat > expect << EOF
+cat >expect <<\EOF
boolean: 2
integer: 1729
magnitude: 16384
@@ -184,7 +145,7 @@ timestamp: 0
string: 321
abbrev: 10
verbose: 2
-quiet: no
+quiet: 0
dry run: no
file: prefix/fi.le
EOF
@@ -204,15 +165,15 @@ test_expect_success 'missing required value' '
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
@@ -222,32 +183,32 @@ EOF
test_expect_success 'intermingled arguments' '
test-parse-options a1 --string 123 b1 --boolean -j 13 -- --boolean \
- > output 2> 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-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-parse-options --int=2 >output 2>output.err &&
test_must_be_empty output.err &&
test_cmp expect output
'
@@ -256,74 +217,74 @@ test_expect_success 'ambiguously abbreviated option' '
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-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_must_fail test-parse-options -boolean >output 2>output.err &&
test_must_be_empty output &&
test_cmp typo.err output.err
'
-cat > typo.err << EOF
-error: did you mean \`--ambiguous\` (with two dashes ?)
+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_fail test-parse-options -ambiguous >output 2>output.err &&
test_must_be_empty output &&
test_cmp typo.err output.err
'
-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)
arg 00: --quux
EOF
test_expect_success 'keep some options as arguments' '
- test-parse-options --quux > output 2> output.err &&
+ test-parse-options --quux >output 2>output.err &&
test_must_be_empty output.err &&
- test_cmp expect output
+ test_cmp expect output
'
-cat > expect <<EOF
+cat >expect <<\EOF
boolean: 0
integer: 0
magnitude: 0
timestamp: 1
string: (not set)
abbrev: 7
-verbose: 0
-quiet: yes
+verbose: -1
+quiet: 1
dry run: no
file: (not set)
arg 00: foo
@@ -331,12 +292,12 @@ EOF
test_expect_success 'OPT_DATE() works' '
test-parse-options -t "1970-01-01 00:00:01 +0000" \
- foo -q > output 2> output.err &&
+ 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
@@ -344,112 +305,110 @@ 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-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_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-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-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-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-parse-options -bb --no-neg-or4 >output 2>output.err &&
test_must_be_empty output.err &&
test_cmp expect output
'
test_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' '
- test-parse-options + + + + + + > output 2> output.err &&
+ 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-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
@@ -460,7 +419,7 @@ test_expect_success 'negation of OPT_NONEG flags is not ambiguous' '
test_cmp expect output
'
-cat >>expect <<'EOF'
+cat >>expect <<\EOF
list: foo
list: bar
list: baz
@@ -476,4 +435,118 @@ test_expect_success '--no-list resets list' '
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
+'
+
test_done
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 8532a028e7..bf2deee109 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -19,6 +19,13 @@ relative_path() {
"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 &&
@@ -298,4 +305,43 @@ 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/t0300-credentials.sh b/t/t0300-credentials.sh
index d7ef44b4a2..03bd31e9f2 100755
--- a/t/t0300-credentials.sh
+++ b/t/t0300-credentials.sh
@@ -298,4 +298,15 @@ test_expect_success 'helpers can abort the process' '
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/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 3d6f1db9da..d934a24417 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -1087,6 +1087,20 @@ 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 &&
diff --git a/t/t1350-config-hooks-path.sh b/t/t1350-config-hooks-path.sh
new file mode 100755
index 0000000000..5e3fb3a6af
--- /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/t1410-reflog.sh b/t/t1410-reflog.sh
index c623824b4d..9cf91dc6d2 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -338,4 +338,14 @@ test_expect_failure 'reflog with non-commit entries displays all entries' '
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_done
diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh
index c465abe8e3..25ddab4e98 100755
--- a/t/t1430-bad-ref-name.sh
+++ b/t/t1430-bad-ref-name.sh
@@ -42,7 +42,7 @@ 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 &&
- grep -e "broken\.\.\.ref" error &&
+ test_i18ngrep -e "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
! grep -e "broken\.\.\.ref" output
'
@@ -147,35 +147,145 @@ 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 &&
- git symbolic-ref refs/tags/shadow 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.*refs/tags/shadow" err
+ test_i18ngrep "ignoring dangling symref refs/tags/shadow" err
'
-test_expect_success 'update-ref --no-deref -d can delete reference to broken name' '
- git symbolic-ref refs/heads/badname refs/heads/broken...ref &&
+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" &&
- test_path_is_file .git/refs/heads/badname &&
- git update-ref --no-deref -d refs/heads/badname &&
- test_path_is_missing .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 &&
+ 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 &&
+ 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
'
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index e66b7cb697..7ee8ea004f 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -427,6 +427,24 @@ test_expect_success 'fsck allows .Ε‡it' '
)
'
+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).
diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
index cbfa41ec61..3a22fc55fc 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -4,6 +4,8 @@ test_description='test git worktree add'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
test_expect_success 'setup' '
test_commit init
'
@@ -213,4 +215,73 @@ test_expect_success 'local clone from linked checkout' '
( 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/t3033-merge-toplevel.sh b/t/t3033-merge-toplevel.sh
index 46aadc410b..d314599428 100755
--- a/t/t3033-merge-toplevel.sh
+++ b/t/t3033-merge-toplevel.sh
@@ -19,6 +19,8 @@ test_expect_success setup '
test_commit three &&
git checkout right &&
test_commit four &&
+ git checkout --orphan newroot &&
+ test_commit five &&
git checkout master
'
@@ -133,4 +135,18 @@ test_expect_success 'merge FETCH_HEAD octopus non-fast-forward' '
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/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index 8f64505e4f..488945e007 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -85,6 +85,15 @@ test_expect_success 'rebase -Xtheirs' '
! 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
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index d6d65a3a94..66348f11d1 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -62,7 +62,7 @@ test_expect_success 'setup' '
# "exec" commands are ran 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
+# 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
@@ -770,7 +770,6 @@ test_expect_success 'rebase-i history with funny messages' '
test_cmp expect actual
'
-
test_expect_success 'prepare for rebase -i --exec' '
git checkout master &&
git checkout -b execute &&
@@ -779,7 +778,6 @@ test_expect_success 'prepare for rebase -i --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 &&
@@ -792,7 +790,6 @@ test_expect_success 'running "git rebase -i --exec git show HEAD"' '
test_cmp expected actual
'
-
test_expect_success 'running "git rebase --exec git show HEAD -i"' '
git reset --hard execute &&
set_fake_editor &&
@@ -806,7 +803,6 @@ test_expect_success 'running "git rebase --exec git show HEAD -i"' '
test_cmp expected actual
'
-
test_expect_success 'running "git rebase -ix git show HEAD"' '
git reset --hard execute &&
set_fake_editor &&
@@ -834,7 +830,6 @@ test_expect_success 'rebase -ix with several <CMD>' '
test_cmp expected actual
'
-
test_expect_success 'rebase -ix with several instances of --exec' '
git reset --hard execute &&
set_fake_editor &&
@@ -849,7 +844,6 @@ test_expect_success 'rebase -ix with several instances of --exec' '
test_cmp expected actual
'
-
test_expect_success 'rebase -ix with --autosquash' '
git reset --hard execute &&
git checkout -b autosquash &&
@@ -875,16 +869,15 @@ test_expect_success 'rebase -ix with --autosquash' '
test_cmp expected actual
'
-
-test_expect_success 'rebase --exec without -i shows error message' '
+test_expect_success 'rebase --exec works without -i ' '
git reset --hard execute &&
- set_fake_editor &&
- test_must_fail git rebase --exec "git show HEAD" HEAD~2 2>actual &&
- echo "The --exec option must be used with the --interactive option" >expected &&
- test_i18ncmp expected actual
+ 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 &&
diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh
index 0b52105728..73a39f2923 100755
--- a/t/t3412-rebase-root.sh
+++ b/t/t3412-rebase-root.sh
@@ -133,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'
diff --git a/t/t3419-rebase-patch-id.sh b/t/t3419-rebase-patch-id.sh
index 217dd79b2e..49f548cdb9 100755
--- a/t/t3419-rebase-patch-id.sh
+++ b/t/t3419-rebase-patch-id.sh
@@ -73,17 +73,17 @@ do_tests () {
run git format-patch --stdout --ignore-if-in-upstream master
"
- test_expect_success $pr 'detect upstream patch' "
+ 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 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_must_fail test -n "$(git rev-list master...HEAD~)"
+ '
- test_expect_success $pr 'do not drop patch' "
+ test_expect_success $pr 'do not drop patch' '
git branch -f squashed master &&
git checkout -q -f squashed &&
git reset -q --soft HEAD~2 &&
@@ -91,7 +91,7 @@ do_tests () {
git checkout -q other^{} &&
test_must_fail git rebase squashed &&
rm -rf .git/rebase-apply
- "
+ '
}
do_tests 500
diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh
index 9c55cba198..68fe2003ef 100755
--- a/t/t3421-rebase-topology-linear.sh
+++ b/t/t3421-rebase-topology-linear.sh
@@ -253,7 +253,7 @@ test_run_rebase () {
"
}
test_run_rebase success ''
-test_run_rebase failure -m
+test_run_rebase success -m
test_run_rebase success -i
test_run_rebase success -p
@@ -268,7 +268,7 @@ test_run_rebase () {
"
}
test_run_rebase success ''
-test_run_rebase failure -m
+test_run_rebase success -m
test_run_rebase success -i
test_run_rebase failure -p
diff --git a/t/t3513-revert-submodule.sh b/t/t3513-revert-submodule.sh
index a1c4e0216f..db9378142a 100755
--- a/t/t3513-revert-submodule.sh
+++ b/t/t3513-revert-submodule.sh
@@ -14,11 +14,11 @@ test_description='revert can handle submodules'
git_revert () {
git status -su >expect &&
ls -1pR * >>expect &&
- tar czf "$TRASH_DIRECTORY/tmp.tgz" * &&
+ tar cf "$TRASH_DIRECTORY/tmp.tar" * &&
git checkout "$1" &&
git revert HEAD &&
rm -rf * &&
- tar xzf "$TRASH_DIRECTORY/tmp.tgz" &&
+ tar xf "$TRASH_DIRECTORY/tmp.tar" &&
git status -su >actual &&
ls -1pR * >>actual &&
test_cmp expect actual &&
diff --git a/t/t3910-mac-os-precompose.sh b/t/t3910-mac-os-precompose.sh
index 831935665e..26dd5b7f78 100755
--- a/t/t3910-mac-os-precompose.sh
+++ b/t/t3910-mac-os-precompose.sh
@@ -49,12 +49,54 @@ test_expect_success "setup" '
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 &&
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index ed90c6c6f9..0d1fa45d25 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 &&
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 6ec6072118..94ef5000e7 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -90,6 +90,8 @@ test_expect_success setup '
git commit -m "Rearranged lines in dir/sub" &&
git checkout master &&
+ git config diff.renames false &&
+
git show-branch
'
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 3b99434e3e..8049cad374 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -549,7 +549,7 @@ 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 &&
@@ -703,7 +703,7 @@ test_expect_success 'options no longer allowed for format-patch' '
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 &&
@@ -1460,4 +1460,109 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '
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/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh
index 3b8b7921d6..447a8ffa3a 100755
--- a/t/t4047-diff-dirstat.sh
+++ b/t/t4047-diff-dirstat.sh
@@ -248,7 +248,8 @@ EOF
git rm -r src/move/unchanged &&
git rm -r src/move/changed &&
git rm -r src/move/rearranged &&
- git commit -m "changes"
+ git commit -m "changes" &&
+ git config diff.renames false
'
cat <<EOF >expect_diff_stat
diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh
index ea5ace99a1..9473c2779e 100755
--- a/t/t4151-am-abort.sh
+++ b/t/t4151-am-abort.sh
@@ -82,7 +82,7 @@ test_expect_success 'am -3 --abort removes otherfile-4' '
test 4 = "$(cat otherfile-4)" &&
git am --abort &&
test_cmp_rev initial HEAD &&
- test -z $(git ls-files -u) &&
+ test -z "$(git ls-files -u)" &&
test_path_is_missing otherfile-4
'
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index ed9c91e25b..1a080e7823 100755
--- a/t/t4200-rerere.sh
+++ b/t/t4200-rerere.sh
@@ -184,12 +184,27 @@ test_expect_success 'rerere updates postimage timestamp' '
'
test_expect_success 'rerere clear' '
- rm $rr/postimage &&
+ mv $rr/postimage .git/post-saved &&
echo "$sha1 a1" | perl -pe "y/\012/\000/" >.git/MERGE_RR &&
git rerere clear &&
! test -d $rr
'
+test_expect_success 'leftover directory' '
+ git reset --hard &&
+ mkdir -p $rr &&
+ test_must_fail git merge first &&
+ test -f $rr/preimage
+'
+
+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_expect_success 'set up for garbage collection tests' '
mkdir -p $rr &&
echo Hello >$rr/preimage &&
@@ -391,4 +406,157 @@ test_expect_success 'rerere -h' '
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 f5e63670fa..a9773658f0 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -115,7 +115,7 @@ EOF
'
test_expect_success !MINGW 'shortlog from non-git directory' '
- git log HEAD >log &&
+ git log --no-expand-tabs HEAD >log &&
GIT_DIR=non-existing git shortlog -w <log >out &&
test_cmp expect out
'
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index cb82eb7e66..128ba93537 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -101,8 +101,8 @@ test_expect_success 'oneline' '
test_expect_success 'diff-filter=A' '
- git log --pretty="format:%s" --diff-filter=A HEAD > actual &&
- git log --pretty="format:%s" --diff-filter A HEAD > actual-separate &&
+ 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
@@ -119,7 +119,7 @@ test_expect_success 'diff-filter=M' '
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) &&
verbose test "$actual" = "$expect"
@@ -848,7 +848,7 @@ sanitize_output () {
}
test_expect_success 'log --graph with diff and stats' '
- git log --graph --pretty=short --stat -p >actual &&
+ git log --no-renames --graph --pretty=short --stat -p >actual &&
sanitize_output >actual.sanitized <actual &&
test_i18ncmp expect actual.sanitized
'
diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh
index baa9d3c82e..84a809690e 100755
--- a/t/t4204-patch-id.sh
+++ b/t/t4204-patch-id.sh
@@ -30,11 +30,11 @@ test_expect_success 'patch-id output is well-formed' '
#calculate patch id. Make sure output is not empty.
calc_patch_id () {
- name="$1"
+ patch_name="$1"
shift
git patch-id "$@" |
- sed "s/ .*//" >patch-id_"$name" &&
- test_line_count -gt 0 patch-id_"$name"
+ sed "s/ .*//" >patch-id_"$patch_name" &&
+ test_line_count -gt 0 patch-id_"$patch_name"
}
get_top_diff () {
diff --git a/t/t4213-log-tabexpand.sh b/t/t4213-log-tabexpand.sh
new file mode 100755
index 0000000000..e01a8f6ac9
--- /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/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 9b9bec468a..91a69fc33a 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -259,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
)
'
@@ -279,9 +280,10 @@ test_expect_success 'clone shallow depth count' '
test_expect_success 'clone shallow object count' '
(
cd shallow &&
+ git prune &&
git count-objects -v
) > count.shallow &&
- grep "^count: 55" count.shallow
+ grep "^count: 54" count.shallow
'
test_expect_success 'fetch --no-shallow on full repo' '
diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh
index a3e12d295a..44f3d5fb28 100755
--- a/t/t5504-fetch-receive-strict.sh
+++ b/t/t5504-fetch-receive-strict.sh
@@ -100,11 +100,8 @@ test_expect_success 'push with receive.fsckobjects' '
git config receive.fsckobjects true &&
git config transfer.fsckobjects false
) &&
- test_must_fail ok=sigpipe git push --porcelain dst master:refs/heads/test >act &&
- {
- test_cmp exp act ||
- ! test -s act
- }
+ test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+ test_cmp exp act
'
test_expect_success 'push with transfer.fsckobjects' '
@@ -114,7 +111,8 @@ test_expect_success 'push with transfer.fsckobjects' '
cd dst &&
git config transfer.fsckobjects true
) &&
- test_must_fail ok=sigpipe git push --porcelain dst master:refs/heads/test >act
+ test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+ test_cmp exp act
'
cat >bogus-commit <<\EOF
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 38321d19ef..454d896390 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -682,6 +682,7 @@ test_expect_success 'fetching with auto-gc does not lock up' '
(
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
)
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index c952d5ef5c..739c089d50 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -9,6 +9,24 @@ modify () {
mv "$2.x" "$2"
}
+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 &&
@@ -247,15 +265,47 @@ test_expect_success '--rebase fails with multiple branches' '
test_expect_success 'pull --rebase succeeds with dirty working directory and rebase.autostash set' '
test_config rebase.autostash true &&
- git reset --hard before-rebase &&
- echo dirty >new_file &&
- git add new_file &&
- git pull --rebase . copy &&
- test_cmp_rev HEAD^ copy &&
- test "$(cat new_file)" = dirty &&
- test "$(cat file)" = "modified again"
+ 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 &&
@@ -264,6 +314,16 @@ test_expect_success 'pull.rebase' '
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 &&
test_config branch.to-rebase.rebase true &&
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
index 18372caa15..ded8f98dbe 100755
--- a/t/t5521-pull-options.sh
+++ b/t/t5521-pull-options.sh
@@ -144,4 +144,25 @@ test_expect_success 'git pull --all --dry-run' '
)
'
+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/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
index 1241146227..954d0e43f5 100755
--- a/t/t5526-fetch-submodules.sh
+++ b/t/t5526-fetch-submodules.sh
@@ -471,4 +471,18 @@ test_expect_success "don't fetch submodule when newly recorded commits are alrea
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/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index 64146352ae..3484b6f0f3 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -91,6 +91,55 @@ test_expect_success 'configured username does not override URL' '
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 &&
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index 58207d8825..2f375eb94d 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -282,5 +282,22 @@ test_expect_success EXPENSIVE 'http can handle enormous ref negotiation' '
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/t5601-clone.sh b/t/t5601-clone.sh
index c1efb8e445..a433394200 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -308,7 +308,7 @@ test_expect_success 'clone checking out a tag' '
setup_ssh_wrapper () {
test_expect_success 'setup ssh wrapper' '
- cp "$GIT_BUILD_DIR/test-fake-ssh$X" \
+ 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 &&
@@ -466,7 +466,7 @@ test_expect_success 'clone ssh://host.xz:22/~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 "[]")
+ 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
"
diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh
index 27d730c0a7..e4850b778c 100755
--- a/t/t5611-clone-config.sh
+++ b/t/t5611-clone-config.sh
@@ -37,4 +37,24 @@ test_expect_success 'clone -c config is available during clone' '
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/t5614-clone-submodules.sh b/t/t5614-clone-submodules.sh
new file mode 100755
index 0000000000..62044c5a02
--- /dev/null
+++ b/t/t5614-clone-submodules.sh
@@ -0,0 +1,85 @@
+#!/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 &&
+ (
+ cd super_clone &&
+ git log --oneline >lines &&
+ test_line_count = 3 lines
+ ) &&
+ (
+ cd super_clone/sub &&
+ git log --oneline >lines &&
+ test_line_count = 3 lines
+ )
+'
+
+test_expect_success 'shallow clone implies shallow submodule' '
+ test_when_finished "rm -rf super_clone" &&
+ git clone --recurse-submodules --depth 2 "file://$pwd/." super_clone &&
+ (
+ cd super_clone &&
+ git log --oneline >lines &&
+ test_line_count = 2 lines
+ ) &&
+ (
+ cd super_clone/sub &&
+ git log --oneline >lines &&
+ test_line_count = 1 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 &&
+ (
+ cd super_clone &&
+ git log --oneline >lines &&
+ test_line_count = 2 lines
+ ) &&
+ (
+ cd super_clone/sub &&
+ git 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 &&
+ (
+ cd super_clone &&
+ git log --oneline >lines &&
+ test_line_count = 3 lines
+ ) &&
+ (
+ cd super_clone/sub &&
+ git log --oneline >lines &&
+ test_line_count = 1 lines
+ )
+'
+
+test_done
diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh
index 66cda17ef3..20e3e2554a 100755
--- a/t/t6009-rev-list-parent.sh
+++ b/t/t6009-rev-list-parent.sh
@@ -47,7 +47,9 @@ test_expect_success 'setup roots, merges and octopuses' '
git checkout -b yetanotherbranch four &&
test_commit eight &&
git checkout master &&
- test_merge normalmerge newroot &&
+ test_tick &&
+ git merge --allow-unrelated-histories -m normalmerge newroot &&
+ git tag normalmerge &&
test_tick &&
git merge -m tripus sidebranch anotherbranch &&
git tag tripus &&
diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh
index 39b3238da2..e0c5f44cac 100755
--- a/t/t6010-merge-base.sh
+++ b/t/t6010-merge-base.sh
@@ -215,11 +215,13 @@ test_expect_success 'criss-cross merge-base for octopus-step' '
git reset --hard E &&
test_commit CC2 &&
test_tick &&
- git merge -s ours CC1 &&
+ # 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 &&
- git merge -s ours CC2 &&
+ # 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 &&
diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh
index b89cd6b07a..2a0fbb87b1 100755
--- a/t/t6012-rev-list-simplify.sh
+++ b/t/t6012-rev-list-simplify.sh
@@ -71,7 +71,7 @@ test_expect_success setup '
note J &&
git checkout master &&
- test_tick && git merge -m "Coolest" unrelated &&
+ test_tick && git merge --allow-unrelated-histories -m "Coolest" unrelated &&
note K &&
echo "Immaterial" >elif &&
diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh
index 755d30ce2a..3f59e58dfb 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
diff --git a/t/t6026-merge-attr.sh b/t/t6026-merge-attr.sh
index 04c0509c47..ef0cbceafe 100755
--- a/t/t6026-merge-attr.sh
+++ b/t/t6026-merge-attr.sh
@@ -176,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/t6029-merge-subtree.sh b/t/t6029-merge-subtree.sh
index 73fc240e85..3e692454a7 100755
--- a/t/t6029-merge-subtree.sh
+++ b/t/t6029-merge-subtree.sh
@@ -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/t6036-recursive-corner-cases.sh b/t/t6036-recursive-corner-cases.sh
index 9d6621c056..18aa88b5c0 100755
--- a/t/t6036-recursive-corner-cases.sh
+++ b/t/t6036-recursive-corner-cases.sh
@@ -212,7 +212,8 @@ test_expect_success 'git detects differently handled merges conflict' '
-L "" \
-L "Temporary merge branch 1" \
merged empty merge-me &&
- test $(git rev-parse :1:new_a) = $(git hash-object merged)
+ sed -e "s/^\([<=>]\)/\1\1\1/" merged >merged-internal &&
+ test $(git rev-parse :1:new_a) = $(git hash-object merged-internal)
'
#
@@ -299,89 +300,6 @@ test_expect_success 'git detects conflict merging criss-cross+modify/delete, rev
'
#
-# criss-cross + modify/modify with very contrived file contents:
-#
-# 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 with contents 'C\n'
-# Commit D: file with contents 'D\n'
-# Commit E: file with contents:
-# <<<<<<< Temporary merge branch 1
-# C
-# =======
-# B
-# >>>>>>> Temporary merge branch 2
-#
-# Now, when we merge commits D & E, does git detect the conflict?
-
-test_expect_success 'setup differently handled merges of content conflict' '
- 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 &&
- echo C >file &&
- git add 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 D >file &&
- git add file &&
- test_tick &&
- git commit -m D &&
- git tag D &&
-
- git checkout C^0 &&
- test_must_fail git merge B &&
- cat <<EOF >file &&
-<<<<<<< Temporary merge branch 1
-C
-=======
-B
->>>>>>> Temporary merge branch 2
-EOF
- git add file &&
- test_tick &&
- git commit -m E &&
- git tag E
-'
-
-test_expect_failure 'git detects conflict w/ criss-cross+contrived resolution' '
- git checkout D^0 &&
-
- test_must_fail git merge -s recursive E^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 :2:file) = $(git rev-parse D:file) &&
- test $(git rev-parse :3:file) = $(git rev-parse E:file)
-'
-
-#
# criss-cross + d/f conflict via add/add:
# Commit A: Neither file 'a' nor directory 'a/' exists.
# Commit B: Introduce 'a'
diff --git a/t/t6041-bisect-submodule.sh b/t/t6041-bisect-submodule.sh
index c6b7aa6977..62b8a2e7bb 100755
--- a/t/t6041-bisect-submodule.sh
+++ b/t/t6041-bisect-submodule.sh
@@ -8,7 +8,7 @@ test_description='bisect can handle submodules'
git_bisect () {
git status -su >expect &&
ls -1pR * >>expect &&
- tar czf "$TRASH_DIRECTORY/tmp.tgz" * &&
+ tar cf "$TRASH_DIRECTORY/tmp.tar" * &&
GOOD=$(git rev-parse --verify HEAD) &&
git checkout "$1" &&
echo "foo" >bar &&
@@ -20,7 +20,7 @@ git_bisect () {
git bisect start &&
git bisect good $GOOD &&
rm -rf * &&
- tar xzf "$TRASH_DIRECTORY/tmp.tgz" &&
+ tar xf "$TRASH_DIRECTORY/tmp.tar" &&
git status -su >actual &&
ls -1pR * >>actual &&
test_cmp expect actual &&
diff --git a/t/t6044-merge-unrelated-index-changes.sh b/t/t6044-merge-unrelated-index-changes.sh
new file mode 100755
index 0000000000..01023486c5
--- /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/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index 10b1452766..1c6952d049 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -19,7 +19,7 @@ test_expect_success 'setup' '
git checkout --orphan tmp &&
test_commit start2 &&
git checkout master &&
- git merge -m next start2 &&
+ git merge -m next --allow-unrelated-histories start2 &&
test_commit final &&
test_seq 40 |
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index bcf472bf51..d0ab09f4bd 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -5,20 +5,27 @@ test_description='test for-each-refs usage of ref-filter APIs'
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-gpg.sh
-if ! test_have_prereq GPG
-then
- skip_all="skipping for-each-ref tests, GPG not available"
- test_done
-fi
-
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 -s -m "A signed tag message" signed-tag &&
- git tag -s -m "Annonated doubly" double-tag signed-tag &&
+ 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
'
@@ -36,6 +43,7 @@ test_expect_success 'filtering with --points-at' '
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
@@ -58,7 +66,9 @@ test_expect_success 'filtering with --merged' '
test_expect_success 'filtering with --no-merged' '
cat >expect <<-\EOF &&
refs/heads/side
- refs/tags/double-tag
+ refs/tags/annotated-tag
+ refs/tags/doubly-annotated-tag
+ refs/tags/doubly-signed-tag
refs/tags/four
refs/tags/signed-tag
EOF
@@ -71,7 +81,9 @@ test_expect_success 'filtering with --contains' '
refs/heads/master
refs/heads/side
refs/odd/spot
- refs/tags/double-tag
+ refs/tags/annotated-tag
+ refs/tags/doubly-annotated-tag
+ refs/tags/doubly-signed-tag
refs/tags/four
refs/tags/signed-tag
refs/tags/three
@@ -90,7 +102,9 @@ test_expect_success 'left alignment is default' '
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/double-tag|refs/tags/double-tag
+ 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
@@ -106,7 +120,9 @@ test_expect_success 'middle alignment' '
| 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/double-tag|refs/tags/double-tag
+ |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
@@ -122,7 +138,9 @@ test_expect_success 'right alignment' '
| 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/double-tag|refs/tags/double-tag
+ |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
@@ -137,7 +155,9 @@ 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/double-tag |refs/tags/double-tag
+| 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
@@ -182,7 +202,9 @@ test_expect_success 'alignment with format quote' "
|' '\''master| A U Thor'\'' '|
|' '\''side| A U Thor'\'' '|
|' '\''odd/spot| A U Thor'\'' '|
- |' '\''double-tag| '\'' '|
+ |' '\''annotated-tag| '\'' '|
+ |' '\''doubly-annotated-tag| '\'' '|
+ |' '\''doubly-signed-tag| '\'' '|
|' '\''four| A U Thor'\'' '|
|' '\''one| A U Thor'\'' '|
|' '\''signed-tag| '\'' '|
@@ -198,7 +220,9 @@ test_expect_success 'nested alignment with quote formatting' "
|' master '|
|' side '|
|' odd/spot '|
- |' double-tag '|
+ |' annotated-tag '|
+ |'doubly-annotated-tag '|
+ |'doubly-signed-tag '|
|' four '|
|' one '|
|' signed-tag '|
@@ -214,10 +238,12 @@ test_expect_success 'check `%(contents:lines=1)`' '
master |three
side |four
odd/spot |three
- double-tag |Annonated doubly
+ annotated-tag |An annotated tag
+ doubly-annotated-tag |Annonated doubly
+ doubly-signed-tag |Signed doubly
four |four
one |one
- signed-tag |A signed tag message
+ signed-tag |A signed tag
three |three
two |two
EOF
@@ -230,7 +256,9 @@ test_expect_success 'check `%(contents:lines=0)`' '
master |
side |
odd/spot |
- double-tag |
+ annotated-tag |
+ doubly-annotated-tag |
+ doubly-signed-tag |
four |
one |
signed-tag |
@@ -246,10 +274,12 @@ test_expect_success 'check `%(contents:lines=99999)`' '
master |three
side |four
odd/spot |three
- double-tag |Annonated doubly
+ annotated-tag |An annotated tag
+ doubly-annotated-tag |Annonated doubly
+ doubly-signed-tag |Signed doubly
four |four
one |one
- signed-tag |A signed tag message
+ signed-tag |A signed tag
three |three
two |two
EOF
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 4008faead8..4a2570ed95 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -292,6 +292,9 @@ test_expect_success 'setup submodule' '
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
'
@@ -475,4 +478,17 @@ test_expect_success 'mv -k does not accidentally destroy submodules' '
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/t7004-tag.sh b/t/t7004-tag.sh
index cf3469b142..f9b7d79af5 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -775,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 &&
diff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh
index 4608e71343..07079a41c4 100755
--- a/t/t7030-verify-tag.sh
+++ b/t/t7030-verify-tag.sh
@@ -112,4 +112,17 @@ test_expect_success GPG 'verify signatures with --raw' '
)
'
+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/t7300-clean.sh b/t/t7300-clean.sh
index 86ceb38b01..b89fd2a6ad 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -495,7 +495,7 @@ test_expect_success 'should not clean submodules' '
test_path_is_missing to_clean
'
-test_expect_success POSIXPERM 'should avoid cleaning possible submodules' '
+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*" &&
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index a41be3142e..3570f7bb8c 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,22 @@ 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 &&
@@ -462,7 +482,7 @@ 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 &&
test_i18ngrep "not initialized" update.out &&
test_must_fail git rev-parse --resolve-git-dir init/.git &&
@@ -480,7 +500,7 @@ test_expect_success 'update --init from subdirectory' '
mkdir -p sub &&
(
cd sub &&
- git submodule update ../init >update.out &&
+ 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 &&
@@ -898,8 +918,9 @@ test_expect_success 'submodule deinit works on repository without submodules' '
git init &&
>file &&
git add file &&
- git commit -m "repo should not be empty"
- git submodule deinit .
+ git commit -m "repo should not be empty" &&
+ git submodule deinit . &&
+ git submodule deinit --all
)
'
@@ -941,6 +962,19 @@ test_expect_success 'submodule deinit . deinits all initialized submodules' '
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 &&
@@ -1007,6 +1041,10 @@ test_expect_success 'submodule deinit is silent when used on an uninitialized su
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
'
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index e5af4b4976..5f278799d5 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -108,24 +108,36 @@ pwd=$(pwd)
cat <<EOF >expect
Submodule path '../super': checked out '$supersha1'
-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'
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
+ git submodule update --init --recursive ../super >../../actual 2>../../actual2
) &&
- test_cmp expect actual
+ test_cmp expect actual &&
+ test_cmp expect2 actual2
'
apos="'";
@@ -850,4 +862,31 @@ test_expect_success 'submodule update --recursive drops module name before recur
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/t7501-commit.sh b/t/t7501-commit.sh
index 900f7de05a..d84897a67a 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -607,4 +607,24 @@ test_expect_success '--only works on to-be-born branch' '
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/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index 2ddf28c984..ed2653d46f 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'
@@ -96,4 +98,60 @@ test_expect_success 'verbose diff is stripped out with set core.commentChar' '
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/t7510-signed-commit.sh b/t/t7510-signed-commit.sh
index 18e5cf0663..4177a8609a 100755
--- a/t/t7510-signed-commit.sh
+++ b/t/t7510-signed-commit.sh
@@ -45,12 +45,18 @@ test_expect_success GPG 'create signed commits' '
git tag seventh-signed &&
echo 8 >file && test_tick && git commit -a -m eighth -SB7227189 &&
- git tag eighth-signed-alt
+ 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
+ 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 &&
@@ -60,7 +66,8 @@ test_expect_success GPG 'verify and show signatures' '
done
) &&
(
- for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned
+ 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 &&
diff --git a/t/t7605-merge-resolve.sh b/t/t7605-merge-resolve.sh
index 0cb9d11f21..5d56c38546 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/t7609-merge-co-error-msgs.sh b/t/t7609-merge-co-error-msgs.sh
index 0e4a682c64..6729cb379f 100755
--- a/t/t7609-merge-co-error-msgs.sh
+++ b/t/t7609-merge-co-error-msgs.sh
@@ -37,14 +37,14 @@ EOF
test_expect_success 'untracked files overwritten by merge (fast and non-fast forward)' '
test_must_fail git merge branch 2>out &&
- test_cmp out expect &&
+ 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_cmp out2 expect &&
+ test_i18ncmp out2 expect &&
git reset --hard HEAD^
'
@@ -53,7 +53,7 @@ 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.
+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.
@@ -65,14 +65,14 @@ test_expect_success 'untracked files or local changes ovewritten by merge' '
git add three &&
git add four &&
test_must_fail git merge branch 2>out &&
- test_cmp out expect
+ 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.
+Please commit your changes or stash them before you can switch branches.
Aborting
EOF
@@ -87,21 +87,21 @@ test_expect_success 'cannot switch branches because of local changes' '
echo uno >rep/one &&
echo dos >rep/two &&
test_must_fail git checkout branch 2>out &&
- test_cmp out expect
+ 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.
+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_cmp out expect
+ test_i18ncmp out expect
'
cat >expect <<\EOF
@@ -132,7 +132,7 @@ test_expect_success 'not_uptodate_dir porcelain checkout error' '
>rep/untracked-file &&
>rep2/untracked-file &&
test_must_fail git checkout branch 2>out &&
- test_cmp out ../expect
+ test_i18ncmp out ../expect
'
test_done
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index 4e713f7aa5..7ce4cd753e 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -20,7 +20,7 @@ difftool_test_setup ()
prompt_given ()
{
prompt="$1"
- test "$prompt" = "Launch 'test-tool' [Y/n]: branch"
+ test "$prompt" = "Launch 'test-tool' [Y/n]? branch"
}
# Create a file on master and change it on branch
@@ -419,6 +419,29 @@ run_dir_diff_test 'difftool --dir-diff when worktree file is missing' '
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
+'
+
write_script .git/CHECK_SYMLINKS <<\EOF
for f in file file2 sub/sub
do
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index 22d8367ff3..28082b134f 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -45,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'
@@ -65,14 +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
+ 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 &&
@@ -80,14 +80,14 @@ 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
+ 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 &&
@@ -95,7 +95,7 @@ test_expect_success "$name" '
git update-index --add bar/zzz/yyy &&
git commit -m "$name" &&
git svn set-tree --find-copies-harder --rmdir \
- ${remotes_git_svn}..mybranch3 &&
+ remotes/git-svn..mybranch3 &&
svn_cmd up "$SVN_TREE" &&
test -d "$SVN_TREE"/bar/zzz &&
test -e "$SVN_TREE"/bar/zzz/yyy
@@ -104,7 +104,7 @@ test_expect_success "$name" '
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 &&
@@ -112,19 +112,19 @@ 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
+ remotes/git-svn..mybranch4
'
name='remove executable bit from a file'
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'
@@ -135,7 +135,7 @@ test_expect_success POSIXPERM "$name" '
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'
@@ -147,7 +147,7 @@ test_expect_success SYMLINKS "$name" '
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 -h "$SVN_TREE"/exec.sh'
@@ -159,7 +159,7 @@ test_expect_success POSIXPERM,SYMLINKS "$name" '
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"/file &&
test -h "$SVN_TREE"/exec-2.sh'
@@ -172,7 +172,7 @@ test_expect_success POSIXPERM,SYMLINKS "$name" '
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 ! -h "$SVN_TREE"/exec-2.sh &&
@@ -194,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'
@@ -217,17 +217,17 @@ EOF
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"
'
@@ -237,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' '
@@ -259,26 +259,26 @@ 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 'dcommit should not fail with a touched file' '
test_commit "commit-new-file-foo2" foo2 &&
@@ -291,13 +291,13 @@ test_expect_success 'rebase should not fail with a touched file' '
git svn rebase
'
-test_expect_success 'able to set-tree to a subdirectory' "
+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 &&
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index e8173d5fef..07bfb63777 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -73,11 +73,11 @@ 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)"
@@ -95,7 +95,7 @@ test_expect_success "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
@@ -117,7 +117,7 @@ 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)"
@@ -168,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 eb70f4839c..66cd51102c 100755
--- a/t/t9102-git-svn-deep-rmdir.sh
+++ b/t/t9102-git-svn-deep-rmdir.sh
@@ -17,7 +17,7 @@ test_expect_success 'initialize repo' '
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 3413164cb1..b28271345c 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/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index f6d7ac7c5f..dbe8deac0d 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -44,7 +44,7 @@ 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 &&
@@ -59,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' &&
@@ -81,7 +81,7 @@ test_expect_success 'commit another change from svn side' '
'
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 9060198037..9f3ef8f2ef 100755
--- a/t/t9107-git-svn-migrate.sh
+++ b/t/t9107-git-svn-migrate.sh
@@ -19,13 +19,14 @@ test_expect_success 'setup old-looking metadata' '
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/')
@@ -35,11 +36,11 @@ test_expect_success 'initialize old-style (v0) git svn layout' '
echo "$svnrepo" > "$GIT_DIR"/svn/info/url &&
git svn migrate &&
! test -d "$GIT_DIR"/git-svn &&
- git rev-parse --verify refs/${remotes_git_svn}^0 &&
+ git rev-parse --verify refs/remotes/git-svn^0 &&
git rev-parse --verify refs/remotes/svn^0 &&
test "$(git config --get svn-remote.svn.url)" = "$svnrepo_escaped" &&
test $(git config --get svn-remote.svn.fetch) = \
- ":refs/${remotes_git_svn}"
+ ":refs/remotes/git-svn"
'
test_expect_success 'initialize a multi-repository repo' '
@@ -56,9 +57,11 @@ test_expect_success 'initialize a multi-repository repo' '
"^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
+ for i in tags/0.1 tags/0.2 tags/0.3
+ do
git config --add svn-remote.svn.fetch \
- $i:refs/remotes/origin/$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/origin/trunk$" fetch.out &&
grep "^branches/a:refs/remotes/origin/a$" fetch.out &&
@@ -66,34 +69,42 @@ test_expect_success 'initialize a multi-repository repo' '
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
+ 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/origin/\$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/origin/\$i \
- refs/remotes/origin/\$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
+ 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\.")" &&
@@ -104,7 +115,7 @@ test_expect_success 'migrate --minimize on old inited layout' '
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
+ grep "^:refs/remotes/git-svn" fetch.out
'
test_expect_success ".rev_db auto-converted to .rev_map.UUID" '
diff --git a/t/t9110-git-svn-use-svm-props.sh b/t/t9110-git-svn-use-svm-props.sh
index 29fbdfdd3f..dde0a3c222 100755
--- a/t/t9110-git-svn-use-svm-props.sh
+++ b/t/t9110-git-svn-use-svm-props.sh
@@ -22,31 +22,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_expect_success 'find commit based on SVN revision number' "
diff --git a/t/t9111-git-svn-use-svnsync-props.sh b/t/t9111-git-svn-use-svnsync-props.sh
index bd081c2ec3..22b6e5ee7d 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/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh
index 1c84ce1023..59465b147e 100755
--- a/t/t9120-git-svn-clone-with-percent-escapes.sh
+++ b/t/t9120-git-svn-clone-with-percent-escapes.sh
@@ -22,7 +22,7 @@ test_expect_success 'test clone with percent escapes' '
git svn clone "$svnrepo/pr%20ject" clone &&
(
cd clone &&
- git rev-parse refs/${remotes_git_svn}
+ git rev-parse refs/remotes/git-svn
)
'
@@ -42,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
)
'
@@ -50,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
)
'
diff --git a/t/t9123-git-svn-rebuild-with-rewriteroot.sh b/t/t9123-git-svn-rebuild-with-rewriteroot.sh
index fd8184787f..ead404589e 100755
--- a/t/t9123-git-svn-rebuild-with-rewriteroot.sh
+++ b/t/t9123-git-svn-rebuild-with-rewriteroot.sh
@@ -17,7 +17,7 @@ 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 checkout -b mybranch remotes/git-svn
'
test_expect_success 'remove rev_map' '
diff --git a/t/t9153-git-svn-rewrite-uuid.sh b/t/t9153-git-svn-rewrite-uuid.sh
index 88a2cfa233..372ef15685 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/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index d708cbf032..432c61d246 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -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" &&
diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh
index 66d3fc91a7..eb9a8ed197 100755
--- a/t/t9802-git-p4-filetype.sh
+++ b/t/t9802-git-p4-filetype.sh
@@ -223,12 +223,12 @@ build_gendouble() {
import sys
import struct
- s = struct.pack(">LL18s",
+ s = struct.pack(b">LL18s",
0x00051607, # AppleDouble
0x00020000, # version 2
- "" # pad to 26 bytes
+ b"" # pad to 26 bytes
)
- sys.stdout.write(s)
+ getattr(sys.stdout, 'buffer', sys.stdout).write(s)
EOF
}
diff --git a/t/t9824-git-p4-git-lfs.sh b/t/t9824-git-p4-git-lfs.sh
index 0b664a377c..110a7e7924 100755
--- a/t/t9824-git-p4-git-lfs.sh
+++ b/t/t9824-git-p4-git-lfs.sh
@@ -13,6 +13,10 @@ 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" &&
@@ -265,7 +269,7 @@ test_expect_success 'Add big files to repo and store files in LFS based on compr
# We only import HEAD here ("@all" is missing!)
git p4 clone --destination="$git" //depot &&
- test_file_in_lfs file6.bin 13 "content 6 bin 39 bytes XXXXXYYYYYZZZZZ"
+ 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 &&
diff --git a/t/t9826-git-p4-keep-empty-commits.sh b/t/t9826-git-p4-keep-empty-commits.sh
index be12960d39..fa8b9daf1f 100755
--- a/t/t9826-git-p4-keep-empty-commits.sh
+++ b/t/t9826-git-p4-keep-empty-commits.sh
@@ -47,23 +47,23 @@ test_expect_success 'Clone repo root path with all history' '
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 4
+ [git-p4: depot-paths = "//depot/": change = 6]
-Remove file 3
-[git-p4: depot-paths = "//depot/": change = 5]
+ Remove file 3
+ [git-p4: depot-paths = "//depot/": change = 5]
-Add file 4
-[git-p4: depot-paths = "//depot/": change = 4]
+ Add file 4
+ [git-p4: depot-paths = "//depot/": change = 4]
-Add file 3
-[git-p4: depot-paths = "//depot/": change = 3]
+ Add file 3
+ [git-p4: depot-paths = "//depot/": change = 3]
-Add file 2
-[git-p4: depot-paths = "//depot/": change = 2]
+ Add file 2
+ [git-p4: depot-paths = "//depot/": change = 2]
-Add file 1
-[git-p4: depot-paths = "//depot/": change = 1]
+ Add file 1
+ [git-p4: depot-paths = "//depot/": change = 1]
EOF
git log --format=%B >actual &&
@@ -80,23 +80,23 @@ test_expect_success 'Clone repo subdir with all history but keep empty commits'
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 4
+ [git-p4: depot-paths = "//depot/": change = 6]
-Remove file 3
-[git-p4: depot-paths = "//depot/": change = 5]
+ Remove file 3
+ [git-p4: depot-paths = "//depot/": change = 5]
-Add file 4
-[git-p4: depot-paths = "//depot/": change = 4]
+ Add file 4
+ [git-p4: depot-paths = "//depot/": change = 4]
-Add file 3
-[git-p4: depot-paths = "//depot/": change = 3]
+ Add file 3
+ [git-p4: depot-paths = "//depot/": change = 3]
-Add file 2
-[git-p4: depot-paths = "//depot/": change = 2]
+ Add file 2
+ [git-p4: depot-paths = "//depot/": change = 2]
-Add file 1
-[git-p4: depot-paths = "//depot/": change = 1]
+ Add file 1
+ [git-p4: depot-paths = "//depot/": change = 1]
EOF
git log --format=%B >actual &&
@@ -112,14 +112,14 @@ test_expect_success 'Clone repo subdir with all history' '
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]
+ Remove file 3
+ [git-p4: depot-paths = "//depot/": change = 5]
-Add file 3
-[git-p4: depot-paths = "//depot/": change = 3]
+ Add file 3
+ [git-p4: depot-paths = "//depot/": change = 3]
-Add file 1
-[git-p4: depot-paths = "//depot/": change = 1]
+ Add file 1
+ [git-p4: depot-paths = "//depot/": change = 1]
EOF
git log --format=%B >actual &&
diff --git a/t/t9828-git-p4-map-user.sh b/t/t9828-git-p4-map-user.sh
new file mode 100755
index 0000000000..e20395c89f
--- /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 0000000000..971aeeea1f
--- /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/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index ffbfa0efb8..0db4469c89 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -107,7 +107,7 @@ test_expect_success 'prompt - describe detached head - contains' '
'
test_expect_success 'prompt - describe detached head - branch' '
- printf " ((b1~1))" >expected &&
+ printf " ((tags/t2~1))" >expected &&
git checkout b1^ &&
test_when_finished "git checkout master" &&
(
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 8d99eb303f..3978fc0b45 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -718,20 +718,13 @@ test_cmp_rev () {
test_cmp expect.rev actual.rev
}
-# Print a sequence of numbers or letters in increasing order. This is
-# similar to GNU seq(1), but the latter might not be available
-# everywhere (and does not do letters). It may be used like:
-#
-# for i in $(test_seq 100)
-# do
-# for j in $(test_seq 10 20)
-# do
-# for k in $(test_seq a z)
-# do
-# echo $i-$j-$k
-# done
-# done
-# done
+# 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
@@ -739,7 +732,12 @@ test_seq () {
2) ;;
*) error "bug in the test script: not 1 or 2 parameters to test_seq" ;;
esac
- perl -le 'print for $ARGV[0]..$ARGV[1]' -- "$@"
+ 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
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 0b47eb6bb2..0055ebba46 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -202,13 +202,13 @@ do
}
run_list=$1; shift ;;
--run=*)
- run_list=$(expr "z$1" : 'z[^=]*=\(.*\)'); shift ;;
+ 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=$(expr "z$1" : 'z[^=]*=\(.*\)')
+ verbose_only=${1#--*=}
shift ;;
-q|--q|--qu|--qui|--quie|--quiet)
# Ignore --quiet under a TAP::Harness. Saying how many tests
@@ -222,15 +222,15 @@ do
valgrind=memcheck
shift ;;
--valgrind=*)
- valgrind=$(expr "z$1" : 'z[^=]*=\(.*\)')
+ valgrind=${1#--*=}
shift ;;
--valgrind-only=*)
- valgrind_only=$(expr "z$1" : 'z[^=]*=\(.*\)')
+ 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
@@ -322,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
@@ -854,10 +867,10 @@ test -d "$GIT_BUILD_DIR"/templates/blt || {
error "You haven't built things yet, have you?"
}
-if ! test -x "$GIT_BUILD_DIR"/test-chmtime
+if ! test -x "$GIT_BUILD_DIR"/t/helper/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
diff --git a/tag.c b/tag.c
index d72f742af9..d1dcd18cd7 100644
--- a/tag.c
+++ b/tag.c
@@ -6,6 +6,59 @@
const char *tag_type = "tag";
+static int run_gpg_verify(const char *buf, unsigned long size, unsigned flags)
+{
+ struct signature_check sigc;
+ size_t payload_size;
+ int ret;
+
+ memset(&sigc, 0, sizeof(sigc));
+
+ payload_size = parse_signature(buf, size);
+
+ if (size == payload_size) {
+ if (flags & GPG_VERIFY_VERBOSE)
+ write_in_full(1, buf, payload_size);
+ return error("no signature found");
+ }
+
+ ret = check_signature(buf, payload_size, buf + payload_size,
+ size - payload_size, &sigc);
+ print_signature_buffer(&sigc, flags);
+
+ signature_check_clear(&sigc);
+ return ret;
+}
+
+int gpg_verify_tag(const unsigned char *sha1, const char *name_to_report,
+ unsigned flags)
+{
+ enum object_type type;
+ char *buf;
+ unsigned long size;
+ int ret;
+
+ type = sha1_object_info(sha1, NULL);
+ if (type != OBJ_TAG)
+ return error("%s: cannot verify a non-tag object of type %s.",
+ name_to_report ?
+ name_to_report :
+ find_unique_abbrev(sha1, DEFAULT_ABBREV),
+ typename(type));
+
+ buf = read_sha1_file(sha1, &type, &size);
+ if (!buf)
+ return error("%s: unable to read file.",
+ name_to_report ?
+ name_to_report :
+ find_unique_abbrev(sha1, DEFAULT_ABBREV));
+
+ ret = run_gpg_verify(buf, size, flags);
+
+ free(buf);
+ return ret;
+}
+
struct object *deref_tag(struct object *o, const char *warn, int warnlen)
{
while (o && o->type == OBJ_TAG)
diff --git a/tag.h b/tag.h
index f4580aea42..a5721b6731 100644
--- a/tag.h
+++ b/tag.h
@@ -17,5 +17,7 @@ extern int parse_tag_buffer(struct tag *item, const void *data, unsigned long si
extern int parse_tag(struct tag *item);
extern struct object *deref_tag(struct object *, const char *, int);
extern struct object *deref_tag_noverify(struct object *);
+extern int gpg_verify_tag(const unsigned char *sha1,
+ const char *name_to_report, unsigned flags);
#endif /* TAG_H */
diff --git a/transport-helper.c b/transport-helper.c
index b934183236..bd666b29ec 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -1152,7 +1152,7 @@ static void udt_close_if_finished(struct unidirectional_transfer *t)
}
/*
- * Tries to read read data from source into buffer. If buffer is full,
+ * Tries to read data from source into buffer. If buffer is full,
* no data is read. Returns 0 on success, -1 on error.
*/
static int udt_do_read(struct unidirectional_transfer *t)
@@ -1166,7 +1166,7 @@ static int udt_do_read(struct unidirectional_transfer *t)
bytes = read(t->src, t->buf + t->bufuse, BUFFERSIZE - t->bufuse);
if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN &&
errno != EINTR) {
- error("read(%s) failed: %s", t->src_name, strerror(errno));
+ error_errno("read(%s) failed", t->src_name);
return -1;
} else if (bytes == 0) {
transfer_debug("%s EOF (with %i bytes in buffer)",
@@ -1193,7 +1193,7 @@ static int udt_do_write(struct unidirectional_transfer *t)
transfer_debug("%s is writable", t->dest_name);
bytes = xwrite(t->dest, t->buf, t->bufuse);
if (bytes < 0 && errno != EWOULDBLOCK) {
- error("write(%s) failed: %s", t->dest_name, strerror(errno));
+ error_errno("write(%s) failed", t->dest_name);
return -1;
} else if (bytes > 0) {
t->bufuse -= bytes;
@@ -1306,7 +1306,7 @@ static int tloop_join(pid_t pid, const char *name)
{
int tret;
if (waitpid(pid, &tret, 0) < 0) {
- error("%s process failed to wait: %s", name, strerror(errno));
+ error_errno("%s process failed to wait", name);
return 1;
}
if (!WIFEXITED(tret) || WEXITSTATUS(tret)) {
diff --git a/tree-diff.c b/tree-diff.c
index 4dda9a14ab..ff4e0d3cb6 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -183,7 +183,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,
if (t) {
/* path present in resulting tree */
- sha1 = tree_entry_extract(t, &path, &mode);
+ sha1 = tree_entry_extract(t, &path, &mode)->hash;
pathlen = tree_entry_len(&t->entry);
isdir = S_ISDIR(mode);
} else {
@@ -229,7 +229,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,
DIFF_STATUS_ADDED;
if (tpi_valid) {
- sha1_i = tp[i].entry.sha1;
+ sha1_i = tp[i].entry.oid->hash;
mode_i = tp[i].entry.mode;
}
else {
@@ -270,7 +270,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,
/* same rule as in emitthis */
int tpi_valid = tp && !(tp[i].entry.mode & S_IFXMIN_NEQ);
- parents_sha1[i] = tpi_valid ? tp[i].entry.sha1
+ parents_sha1[i] = tpi_valid ? tp[i].entry.oid->hash
: NULL;
}
@@ -482,7 +482,7 @@ static struct combine_diff_path *ll_diff_tree_paths(
continue;
/* diff(t,pi) != ΓΈ */
- if (hashcmp(t.entry.sha1, tp[i].entry.sha1) ||
+ if (oidcmp(t.entry.oid, tp[i].entry.oid) ||
(t.entry.mode != tp[i].entry.mode))
continue;
diff --git a/tree-walk.c b/tree-walk.c
index cd4bb2c38b..ce27842439 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -38,7 +38,7 @@ static void decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned
/* Initialize the descriptor entry */
desc->entry.path = path;
desc->entry.mode = canon_mode(mode);
- desc->entry.sha1 = (const unsigned char *)(path + len);
+ desc->entry.oid = (const struct object_id *)(path + len);
}
void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
@@ -76,7 +76,7 @@ static void entry_extract(struct tree_desc *t, struct name_entry *a)
void update_tree_entry(struct tree_desc *desc)
{
const void *buf = desc->buffer;
- const unsigned char *end = desc->entry.sha1 + 20;
+ const unsigned char *end = desc->entry.oid->hash + 20;
unsigned long size = desc->size;
unsigned long len = end - (const unsigned char *)buf;
@@ -110,7 +110,7 @@ void setup_traverse_info(struct traverse_info *info, const char *base)
pathlen--;
info->pathlen = pathlen ? pathlen + 1 : 0;
info->name.path = base;
- info->name.sha1 = (void *)(base + pathlen + 1);
+ info->name.oid = (void *)(base + pathlen + 1);
if (pathlen)
info->prev = &dummy;
}
@@ -433,10 +433,10 @@ static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char
int namelen = strlen(name);
while (t->size) {
const char *entry;
- const unsigned char *sha1;
+ const struct object_id *oid;
int entrylen, cmp;
- sha1 = tree_entry_extract(t, &entry, mode);
+ oid = tree_entry_extract(t, &entry, mode);
entrylen = tree_entry_len(&t->entry);
update_tree_entry(t);
if (entrylen > namelen)
@@ -447,7 +447,7 @@ static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char
if (cmp < 0)
break;
if (entrylen == namelen) {
- hashcpy(result, sha1);
+ hashcpy(result, oid->hash);
return 0;
}
if (name[entrylen] != '/')
@@ -455,10 +455,10 @@ static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char
if (!S_ISDIR(*mode))
break;
if (++entrylen == namelen) {
- hashcpy(result, sha1);
+ hashcpy(result, oid->hash);
return 0;
}
- return get_tree_entry(sha1, name + entrylen, result, mode);
+ return get_tree_entry(oid->hash, name + entrylen, result, mode);
}
return -1;
}
diff --git a/tree-walk.h b/tree-walk.h
index 174eb617df..97a7d6957e 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -2,7 +2,7 @@
#define TREE_WALK_H
struct name_entry {
- const unsigned char *sha1;
+ const struct object_id *oid;
const char *path;
unsigned int mode;
};
@@ -13,16 +13,16 @@ struct tree_desc {
unsigned int size;
};
-static inline const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
+static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
{
*pathp = desc->entry.path;
*modep = desc->entry.mode;
- return desc->entry.sha1;
+ return desc->entry.oid;
}
static inline int tree_entry_len(const struct name_entry *ne)
{
- return (const char *)ne->sha1 - ne->path - 1;
+ return (const char *)ne->oid - ne->path - 1;
}
void update_tree_entry(struct tree_desc *);
diff --git a/tree.c b/tree.c
index f79ff9813e..0089e52d9d 100644
--- a/tree.c
+++ b/tree.c
@@ -76,7 +76,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
continue;
}
- switch (fn(entry.sha1, base,
+ switch (fn(entry.oid->hash, base,
entry.path, entry.mode, stage, context)) {
case 0:
continue;
@@ -87,19 +87,19 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
}
if (S_ISDIR(entry.mode))
- hashcpy(sha1, entry.sha1);
+ hashcpy(sha1, entry.oid->hash);
else if (S_ISGITLINK(entry.mode)) {
struct commit *commit;
- commit = lookup_commit(entry.sha1);
+ commit = lookup_commit(entry.oid->hash);
if (!commit)
die("Commit %s in submodule path %s%s not found",
- sha1_to_hex(entry.sha1),
+ oid_to_hex(entry.oid),
base->buf, entry.path);
if (parse_commit(commit))
die("Invalid commit %s in submodule path %s%s",
- sha1_to_hex(entry.sha1),
+ oid_to_hex(entry.oid),
base->buf, entry.path);
hashcpy(sha1, commit->tree->object.oid.hash);
diff --git a/unpack-trees.c b/unpack-trees.c
index 9f55cc28b9..6bc9512a45 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -58,40 +58,74 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
int i;
const char **msgs = opts->msgs;
const char *msg;
- const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches";
- if (advice_commit_before_merge)
- msg = "Your local changes to the following files would be overwritten by %s:\n%%s"
- "Please, commit your changes or stash them before you can %s.";
+ if (!strcmp(cmd, "checkout"))
+ msg = advice_commit_before_merge
+ ? _("Your local changes to the following files would be overwritten by checkout:\n%%s"
+ "Please commit your changes or stash them before you can switch branches.")
+ : _("Your local changes to the following files would be overwritten by checkout:\n%%s");
+ else if (!strcmp(cmd, "merge"))
+ msg = advice_commit_before_merge
+ ? _("Your local changes to the following files would be overwritten by merge:\n%%s"
+ "Please commit your changes or stash them before you can merge.")
+ : _("Your local changes to the following files would be overwritten by merge:\n%%s");
else
- msg = "Your local changes to the following files would be overwritten by %s:\n%%s";
+ msg = advice_commit_before_merge
+ ? _("Your local changes to the following files would be overwritten by %s:\n%%s"
+ "Please commit your changes or stash them before you can %s.")
+ : _("Your local changes to the following files would be overwritten by %s:\n%%s");
msgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] =
- xstrfmt(msg, cmd, cmd2);
+ xstrfmt(msg, cmd, cmd);
msgs[ERROR_NOT_UPTODATE_DIR] =
- "Updating the following directories would lose untracked files in it:\n%s";
-
- if (advice_commit_before_merge)
- msg = "The following untracked working tree files would be %s by %s:\n%%s"
- "Please move or remove them before you can %s.";
+ _("Updating the following directories would lose untracked files in it:\n%s");
+
+ if (!strcmp(cmd, "checkout"))
+ msg = advice_commit_before_merge
+ ? _("The following untracked working tree files would be removed by checkout:\n%%s"
+ "Please move or remove them before you can switch branches.")
+ : _("The following untracked working tree files would be removed by checkout:\n%%s");
+ else if (!strcmp(cmd, "merge"))
+ msg = advice_commit_before_merge
+ ? _("The following untracked working tree files would be removed by merge:\n%%s"
+ "Please move or remove them before you can merge.")
+ : _("The following untracked working tree files would be removed by merge:\n%%s");
else
- msg = "The following untracked working tree files would be %s by %s:\n%%s";
-
- msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = xstrfmt(msg, "removed", cmd, cmd2);
- msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = xstrfmt(msg, "overwritten", cmd, cmd2);
+ msg = advice_commit_before_merge
+ ? _("The following untracked working tree files would be removed by %s:\n%%s"
+ "Please move or remove them before you can %s.")
+ : _("The following untracked working tree files would be removed by %s:\n%%s");
+ msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = xstrfmt(msg, cmd, cmd);
+
+ if (!strcmp(cmd, "checkout"))
+ msg = advice_commit_before_merge
+ ? _("The following untracked working tree files would be overwritten by checkout:\n%%s"
+ "Please move or remove them before you can switch branches.")
+ : _("The following untracked working tree files would be overwritten by checkout:\n%%s");
+ else if (!strcmp(cmd, "merge"))
+ msg = advice_commit_before_merge
+ ? _("The following untracked working tree files would be overwritten by merge:\n%%s"
+ "Please move or remove them before you can merge.")
+ : _("The following untracked working tree files would be overwritten by merge:\n%%s");
+ else
+ msg = advice_commit_before_merge
+ ? _("The following untracked working tree files would be overwritten by %s:\n%%s"
+ "Please move or remove them before you can %s.")
+ : _("The following untracked working tree files would be overwritten by %s:\n%%s");
+ msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = xstrfmt(msg, cmd, cmd);
/*
* Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
* cannot easily display it as a list.
*/
- msgs[ERROR_BIND_OVERLAP] = "Entry '%s' overlaps with '%s'. Cannot bind.";
+ msgs[ERROR_BIND_OVERLAP] = _("Entry '%s' overlaps with '%s'. Cannot bind.");
msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] =
- "Cannot update sparse checkout: the following entries are not up-to-date:\n%s";
+ _("Cannot update sparse checkout: the following entries are not up-to-date:\n%s");
msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] =
- "The following Working tree files would be overwritten by sparse checkout update:\n%s";
+ _("The following Working tree files would be overwritten by sparse checkout update:\n%s");
msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =
- "The following Working tree files would be removed by sparse checkout update:\n%s";
+ _("The following Working tree files would be removed by sparse checkout update:\n%s");
opts->show_all_errors = 1;
/* rejected paths may not have a static buffer */
@@ -168,7 +202,7 @@ static void display_error_msgs(struct unpack_trees_options *o)
string_list_clear(rejects, 0);
}
if (something_displayed)
- fprintf(stderr, "Aborting\n");
+ fprintf(stderr, _("Aborting\n"));
}
/*
@@ -475,7 +509,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
for (i = 0; i < n; i++, dirmask >>= 1) {
const unsigned char *sha1 = NULL;
if (dirmask & 1)
- sha1 = names[i].sha1;
+ sha1 = names[i].oid->hash;
buf[i] = fill_tree_descriptor(t+i, sha1);
}
@@ -591,7 +625,7 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info, con
ce->ce_mode = create_ce_mode(n->mode);
ce->ce_flags = create_ce_flags(stage);
ce->ce_namelen = len;
- hashcpy(ce->sha1, n->sha1);
+ hashcpy(ce->sha1, n->oid->hash);
make_traverse_path(ce->name, info, n);
return ce;
@@ -1499,8 +1533,7 @@ static int verify_absent_1(const struct cache_entry *ce,
path = xmemdupz(ce->name, len);
if (lstat(path, &st))
- ret = error("cannot stat '%s': %s", path,
- strerror(errno));
+ ret = error_errno("cannot stat '%s'", path);
else
ret = check_ok_to_remove(path, len, DT_UNKNOWN, NULL,
&st, error_type, o);
@@ -1508,8 +1541,7 @@ static int verify_absent_1(const struct cache_entry *ce,
return ret;
} else if (lstat(ce->name, &st)) {
if (errno != ENOENT)
- return error("cannot stat '%s': %s", ce->name,
- strerror(errno));
+ return error_errno("cannot stat '%s'", ce->name);
return 0;
} else {
return check_ok_to_remove(ce->name, ce_namelen(ce),
diff --git a/upload-pack.c b/upload-pack.c
index dc802a07c2..f19444df7b 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -174,8 +174,7 @@ static void create_pack_file(void)
if (ret < 0) {
if (errno != EINTR) {
- error("poll failed, resuming: %s",
- strerror(errno));
+ error_errno("poll failed, resuming");
sleep(1);
}
continue;
diff --git a/usage.c b/usage.c
index 82ff13163b..1dad03fb5c 100644
--- a/usage.c
+++ b/usage.c
@@ -109,19 +109,11 @@ void NORETURN die(const char *err, ...)
va_end(params);
}
-void NORETURN die_errno(const char *fmt, ...)
+static const char *fmt_with_err(char *buf, int n, const char *fmt)
{
- va_list params;
- char fmt_with_err[1024];
char str_error[256], *err;
int i, j;
- if (die_is_recursing()) {
- fputs("fatal: recursion detected in die_errno handler\n",
- stderr);
- exit(128);
- }
-
err = strerror(errno);
for (i = j = 0; err[i] && j < sizeof(str_error) - 1; ) {
if ((str_error[j++] = err[i++]) != '%')
@@ -136,13 +128,37 @@ void NORETURN die_errno(const char *fmt, ...)
}
}
str_error[j] = 0;
- snprintf(fmt_with_err, sizeof(fmt_with_err), "%s: %s", fmt, str_error);
+ snprintf(buf, n, "%s: %s", fmt, str_error);
+ return buf;
+}
+
+void NORETURN die_errno(const char *fmt, ...)
+{
+ char buf[1024];
+ va_list params;
+
+ if (die_is_recursing()) {
+ fputs("fatal: recursion detected in die_errno handler\n",
+ stderr);
+ exit(128);
+ }
va_start(params, fmt);
- die_routine(fmt_with_err, params);
+ die_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
va_end(params);
}
+int error_errno(const char *fmt, ...)
+{
+ char buf[1024];
+ va_list params;
+
+ va_start(params, fmt);
+ error_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
+ va_end(params);
+ return -1;
+}
+
#undef error
int error(const char *err, ...)
{
@@ -154,6 +170,16 @@ int error(const char *err, ...)
return -1;
}
+void warning_errno(const char *warn, ...)
+{
+ char buf[1024];
+ va_list params;
+
+ va_start(params, warn);
+ warn_routine(fmt_with_err(buf, sizeof(buf), warn), params);
+ va_end(params);
+}
+
void warning(const char *warn, ...)
{
va_list params;
diff --git a/utf8.h b/utf8.h
index 7930b44f19..6bbcf31a83 100644
--- a/utf8.h
+++ b/utf8.h
@@ -48,7 +48,7 @@ static inline char *reencode_string(const char *in,
int mbs_chrlen(const char **text, size_t *remainder_p, const char *encoding);
/*
- * Returns true if the the path would match ".git" after HFS case-folding.
+ * Returns true if the path would match ".git" after HFS case-folding.
* The path should be NUL-terminated, but we will match variants of both ".git\0"
* and ".git/..." (but _not_ ".../.git"). This makes it suitable for both fsck
* and verify_path().
diff --git a/vcs-svn/line_buffer.c b/vcs-svn/line_buffer.c
index 57cc1cec03..e416caf8a4 100644
--- a/vcs-svn/line_buffer.c
+++ b/vcs-svn/line_buffer.c
@@ -53,9 +53,9 @@ long buffer_tmpfile_prepare_to_read(struct line_buffer *buf)
{
long pos = ftell(buf->infile);
if (pos < 0)
- return error("ftell error: %s", strerror(errno));
+ return error_errno("ftell error");
if (fseek(buf->infile, 0, SEEK_SET))
- return error("seek error: %s", strerror(errno));
+ return error_errno("seek error");
return pos;
}
diff --git a/vcs-svn/sliding_window.c b/vcs-svn/sliding_window.c
index f11d490995..06d273c9e8 100644
--- a/vcs-svn/sliding_window.c
+++ b/vcs-svn/sliding_window.c
@@ -12,7 +12,7 @@ static int input_error(struct line_buffer *file)
{
if (!buffer_ferror(file))
return error("delta preimage ends early");
- return error("cannot read delta preimage: %s", strerror(errno));
+ return error_errno("cannot read delta preimage");
}
static int skip_or_whine(struct line_buffer *file, off_t gap)
diff --git a/vcs-svn/svndiff.c b/vcs-svn/svndiff.c
index 74c97c4543..75c753162a 100644
--- a/vcs-svn/svndiff.c
+++ b/vcs-svn/svndiff.c
@@ -64,13 +64,13 @@ static int write_strbuf(struct strbuf *sb, FILE *out)
{
if (fwrite(sb->buf, 1, sb->len, out) == sb->len) /* Success. */
return 0;
- return error("cannot write delta postimage: %s", strerror(errno));
+ return error_errno("cannot write delta postimage");
}
static int error_short_read(struct line_buffer *input)
{
if (buffer_ferror(input))
- return error("error reading delta: %s", strerror(errno));
+ return error_errno("error reading delta");
return error("invalid delta: unexpected end of file");
}
diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c
index 31d1d83d45..e4b395963b 100644
--- a/vcs-svn/svndump.c
+++ b/vcs-svn/svndump.c
@@ -501,7 +501,7 @@ static void init(int report_fd)
int svndump_init(const char *filename)
{
if (buffer_init(&input, filename))
- return error("cannot open %s: %s", filename ? filename : "NULL", strerror(errno));
+ return error_errno("cannot open %s", filename ? filename : "NULL");
init(REPORT_FILENO);
return 0;
}
@@ -509,7 +509,7 @@ int svndump_init(const char *filename)
int svndump_init_fd(int in_fd, int back_fd)
{
if(buffer_fdinit(&input, xdup(in_fd)))
- return error("cannot open fd %d: %s", in_fd, strerror(errno));
+ return error_errno("cannot open fd %d", in_fd);
init(xdup(back_fd));
return 0;
}
diff --git a/walker.c b/walker.c
index 08773d419f..d95b007294 100644
--- a/walker.c
+++ b/walker.c
@@ -43,12 +43,12 @@ static int process_tree(struct walker *walker, struct tree *tree)
if (S_ISGITLINK(entry.mode))
continue;
if (S_ISDIR(entry.mode)) {
- struct tree *tree = lookup_tree(entry.sha1);
+ struct tree *tree = lookup_tree(entry.oid->hash);
if (tree)
obj = &tree->object;
}
else {
- struct blob *blob = lookup_blob(entry.sha1);
+ struct blob *blob = lookup_blob(entry.oid->hash);
if (blob)
obj = &blob->object;
}
diff --git a/wildmatch.c b/wildmatch.c
index f91ba99f32..57c8765805 100644
--- a/wildmatch.c
+++ b/wildmatch.c
@@ -136,7 +136,7 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
/*
* Try to advance faster when an asterisk is
* followed by a literal. We know in this case
- * that the the string before the literal
+ * that the string before the literal
* must belong to "*".
* If match_slash is false, do not look past
* the first slash as it cannot belong to '*'.
diff --git a/worktree.c b/worktree.c
index 6181a66f1e..199b1ef94b 100644
--- a/worktree.c
+++ b/worktree.c
@@ -2,6 +2,8 @@
#include "refs.h"
#include "strbuf.h"
#include "worktree.h"
+#include "dir.h"
+#include "wt-status.h"
void free_worktrees(struct worktree **worktrees)
{
@@ -9,7 +11,7 @@ void free_worktrees(struct worktree **worktrees)
for (i = 0; worktrees[i]; i++) {
free(worktrees[i]->path);
- free(worktrees[i]->git_dir);
+ free(worktrees[i]->id);
free(worktrees[i]->head_ref);
free(worktrees[i]);
}
@@ -18,7 +20,7 @@ void free_worktrees(struct worktree **worktrees)
/*
* read 'path_to_ref' into 'ref'. Also if is_detached is not NULL,
- * set is_detached to 1 (0) if the ref is detatched (is not detached).
+ * set is_detached to 1 (0) if the ref is detached (is not detached).
*
* $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside $GIT_DIR so
* for linked worktrees, `resolve_ref_unsafe()` won't work (it uses
@@ -74,13 +76,11 @@ static struct worktree *get_main_worktree(void)
struct worktree *worktree = NULL;
struct strbuf path = STRBUF_INIT;
struct strbuf worktree_path = STRBUF_INIT;
- struct strbuf gitdir = STRBUF_INIT;
struct strbuf head_ref = STRBUF_INIT;
int is_bare = 0;
int is_detached = 0;
- strbuf_addf(&gitdir, "%s", absolute_path(get_git_common_dir()));
- strbuf_addbuf(&worktree_path, &gitdir);
+ strbuf_addstr(&worktree_path, absolute_path(get_git_common_dir()));
is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
if (is_bare)
strbuf_strip_suffix(&worktree_path, "/.");
@@ -92,15 +92,15 @@ static struct worktree *get_main_worktree(void)
worktree = xmalloc(sizeof(struct worktree));
worktree->path = strbuf_detach(&worktree_path, NULL);
- worktree->git_dir = strbuf_detach(&gitdir, NULL);
+ worktree->id = NULL;
worktree->is_bare = is_bare;
worktree->head_ref = NULL;
worktree->is_detached = is_detached;
+ worktree->is_current = 0;
add_head_info(&head_ref, worktree);
done:
strbuf_release(&path);
- strbuf_release(&gitdir);
strbuf_release(&worktree_path);
strbuf_release(&head_ref);
return worktree;
@@ -111,16 +111,13 @@ static struct worktree *get_linked_worktree(const char *id)
struct worktree *worktree = NULL;
struct strbuf path = STRBUF_INIT;
struct strbuf worktree_path = STRBUF_INIT;
- struct strbuf gitdir = STRBUF_INIT;
struct strbuf head_ref = STRBUF_INIT;
int is_detached = 0;
if (!id)
die("Missing linked worktree name");
- strbuf_addf(&gitdir, "%s/worktrees/%s",
- absolute_path(get_git_common_dir()), id);
- strbuf_addf(&path, "%s/gitdir", gitdir.buf);
+ strbuf_git_common_path(&path, "worktrees/%s/gitdir", id);
if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
/* invalid gitdir file */
goto done;
@@ -140,20 +137,39 @@ static struct worktree *get_linked_worktree(const char *id)
worktree = xmalloc(sizeof(struct worktree));
worktree->path = strbuf_detach(&worktree_path, NULL);
- worktree->git_dir = strbuf_detach(&gitdir, NULL);
+ worktree->id = xstrdup(id);
worktree->is_bare = 0;
worktree->head_ref = NULL;
worktree->is_detached = is_detached;
+ worktree->is_current = 0;
add_head_info(&head_ref, worktree);
done:
strbuf_release(&path);
- strbuf_release(&gitdir);
strbuf_release(&worktree_path);
strbuf_release(&head_ref);
return worktree;
}
+static void mark_current_worktree(struct worktree **worktrees)
+{
+ struct strbuf git_dir = STRBUF_INIT;
+ struct strbuf path = STRBUF_INIT;
+ int i;
+
+ strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
+ for (i = 0; worktrees[i]; i++) {
+ struct worktree *wt = worktrees[i];
+ strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
+ wt->is_current = !fspathcmp(git_dir.buf, path.buf);
+ strbuf_reset(&path);
+ if (wt->is_current)
+ break;
+ }
+ strbuf_release(&git_dir);
+ strbuf_release(&path);
+}
+
struct worktree **get_worktrees(void)
{
struct worktree **list = NULL;
@@ -185,35 +201,105 @@ struct worktree **get_worktrees(void)
}
ALLOC_GROW(list, counter + 1, alloc);
list[counter] = NULL;
+
+ mark_current_worktree(list);
return list;
}
-char *find_shared_symref(const char *symref, const char *target)
+const char *get_worktree_git_dir(const struct worktree *wt)
+{
+ if (!wt)
+ return get_git_dir();
+ else if (!wt->id)
+ return get_git_common_dir();
+ else
+ return git_common_path("worktrees/%s", wt->id);
+}
+
+int is_worktree_being_rebased(const struct worktree *wt,
+ const char *target)
+{
+ struct wt_status_state state;
+ int found_rebase;
+
+ memset(&state, 0, sizeof(state));
+ found_rebase = wt_status_check_rebase(wt, &state) &&
+ ((state.rebase_in_progress ||
+ state.rebase_interactive_in_progress) &&
+ state.branch &&
+ starts_with(target, "refs/heads/") &&
+ !strcmp(state.branch, target + strlen("refs/heads/")));
+ free(state.branch);
+ free(state.onto);
+ return found_rebase;
+}
+
+int is_worktree_being_bisected(const struct worktree *wt,
+ const char *target)
{
- char *existing = NULL;
+ struct wt_status_state state;
+ int found_rebase;
+
+ memset(&state, 0, sizeof(state));
+ found_rebase = wt_status_check_bisect(wt, &state) &&
+ state.branch &&
+ starts_with(target, "refs/heads/") &&
+ !strcmp(state.branch, target + strlen("refs/heads/"));
+ free(state.branch);
+ return found_rebase;
+}
+
+/*
+ * note: this function should be able to detect shared symref even if
+ * HEAD is temporarily detached (e.g. in the middle of rebase or
+ * bisect). New commands that do similar things should update this
+ * function as well.
+ */
+const struct worktree *find_shared_symref(const char *symref,
+ const char *target)
+{
+ const struct worktree *existing = NULL;
struct strbuf path = STRBUF_INIT;
struct strbuf sb = STRBUF_INIT;
- struct worktree **worktrees = get_worktrees();
+ static struct worktree **worktrees;
int i = 0;
+ if (worktrees)
+ free_worktrees(worktrees);
+ worktrees = get_worktrees();
+
for (i = 0; worktrees[i]; i++) {
+ struct worktree *wt = worktrees[i];
+
+ if (wt->is_detached && !strcmp(symref, "HEAD")) {
+ if (is_worktree_being_rebased(wt, target)) {
+ existing = wt;
+ break;
+ }
+ if (is_worktree_being_bisected(wt, target)) {
+ existing = wt;
+ break;
+ }
+ }
+
strbuf_reset(&path);
strbuf_reset(&sb);
- strbuf_addf(&path, "%s/%s", worktrees[i]->git_dir, symref);
+ strbuf_addf(&path, "%s/%s",
+ get_worktree_git_dir(wt),
+ symref);
if (parse_ref(path.buf, &sb, NULL)) {
continue;
}
if (!strcmp(sb.buf, target)) {
- existing = xstrdup(worktrees[i]->path);
+ existing = wt;
break;
}
}
strbuf_release(&path);
strbuf_release(&sb);
- free_worktrees(worktrees);
return existing;
}
diff --git a/worktree.h b/worktree.h
index b4b3dda792..13949093cc 100644
--- a/worktree.h
+++ b/worktree.h
@@ -3,11 +3,12 @@
struct worktree {
char *path;
- char *git_dir;
+ char *id;
char *head_ref;
unsigned char head_sha1[20];
int is_detached;
int is_bare;
+ int is_current;
};
/* Functions for acting on the information about worktrees. */
@@ -23,16 +24,33 @@ struct worktree {
extern struct worktree **get_worktrees(void);
/*
+ * Return git dir of the worktree. Note that the path may be relative.
+ * If wt is NULL, git dir of current worktree is returned.
+ */
+extern const char *get_worktree_git_dir(const struct worktree *wt);
+
+/*
* Free up the memory for worktree(s)
*/
extern void free_worktrees(struct worktree **);
/*
* Check if a per-worktree symref points to a ref in the main worktree
- * or any linked worktree, and return the path to the exising worktree
- * if it is. Returns NULL if there is no existing ref. The caller is
- * responsible for freeing the returned path.
+ * or any linked worktree, and return the worktree that holds the ref,
+ * or NULL otherwise. The result may be destroyed by the next call.
+ */
+extern const struct worktree *find_shared_symref(const char *symref,
+ const char *target);
+
+int is_worktree_being_rebased(const struct worktree *wt, const char *target);
+int is_worktree_being_bisected(const struct worktree *wt, const char *target);
+
+/*
+ * Similar to git_path() but can produce paths for a specified
+ * worktree instead of current one
*/
-extern char *find_shared_symref(const char *symref, const char *target);
+extern const char *worktree_git_path(const struct worktree *wt,
+ const char *fmt, ...)
+ __attribute__((format (printf, 2, 3)));
#endif
diff --git a/wrap-for-bin.sh b/wrap-for-bin.sh
index db0ec6a737..22b6e4948f 100644
--- a/wrap-for-bin.sh
+++ b/wrap-for-bin.sh
@@ -17,6 +17,7 @@ fi
GITPERLLIB='@@BUILD_DIR@@/perl/blib/lib'"${GITPERLLIB:+:$GITPERLLIB}"
GIT_TEXTDOMAINDIR='@@BUILD_DIR@@/po/build/locale'
PATH='@@BUILD_DIR@@/bin-wrappers:'"$PATH"
+
export GIT_EXEC_PATH GITPERLLIB PATH GIT_TEXTDOMAINDIR
if test -n "$GIT_TEST_GDB"
diff --git a/wrapper.c b/wrapper.c
index 9afc1a021c..5dc4e15aa9 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -446,23 +446,6 @@ int git_mkstemp(char *path, size_t len, const char *template)
return mkstemp(path);
}
-/* git_mkstemps() - create tmp file with suffix honoring TMPDIR variable. */
-int git_mkstemps(char *path, size_t len, const char *template, int suffix_len)
-{
- const char *tmp;
- size_t n;
-
- tmp = getenv("TMPDIR");
- if (!tmp)
- tmp = "/tmp";
- n = snprintf(path, len, "%s/%s", tmp, template);
- if (len <= n) {
- errno = ENAMETOOLONG;
- return -1;
- }
- return mkstemps(path, suffix_len);
-}
-
/* Adapted from libiberty's mkstemp.c. */
#undef TMP_MAX
@@ -572,7 +555,7 @@ static int warn_if_unremovable(const char *op, const char *file, int rc)
if (!rc || errno == ENOENT)
return 0;
err = errno;
- warning("unable to %s %s: %s", op, file, strerror(errno));
+ warning_errno("unable to %s %s", op, file);
errno = err;
return rc;
}
@@ -608,7 +591,7 @@ int remove_or_warn(unsigned int mode, const char *file)
void warn_on_inaccessible(const char *path)
{
- warning(_("unable to access '%s': %s"), path, strerror(errno));
+ warning_errno(_("unable to access '%s'"), path);
}
static int access_error_is_ok(int err, unsigned flag)
diff --git a/wt-status.c b/wt-status.c
index ef7486474a..4f27bd62af 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -15,6 +15,7 @@
#include "column.h"
#include "strbuf.h"
#include "utf8.h"
+#include "worktree.h"
static const char cut_line[] =
"------------------------ >8 ------------------------\n";
@@ -950,6 +951,7 @@ static void show_merge_in_progress(struct wt_status *s,
status_printf_ln(s, color,
_(" (fix conflicts and run \"git commit\")"));
} else {
+ s-> commitable = 1;
status_printf_ln(s, color,
_("All conflicts fixed but you are still merging."));
if (s->hints)
@@ -1063,9 +1065,7 @@ static void abbrev_sha1_in_line(struct strbuf *line)
strbuf_addf(line, "%s", split[i]->buf);
}
}
- for (i = 0; split[i]; i++)
- strbuf_release(split[i]);
-
+ strbuf_list_free(split);
}
static void read_rebase_todolist(const char *fname, struct string_list *lines)
@@ -1264,13 +1264,13 @@ static void show_bisect_in_progress(struct wt_status *s,
/*
* Extract branch information from rebase/bisect
*/
-static char *read_and_strip_branch(const char *path)
+static char *get_branch(const struct worktree *wt, const char *path)
{
struct strbuf sb = STRBUF_INIT;
unsigned char sha1[20];
const char *branch_name;
- if (strbuf_read_file(&sb, git_path("%s", path), 0) <= 0)
+ if (strbuf_read_file(&sb, worktree_git_path(wt, "%s", path), 0) <= 0)
goto got_nothing;
while (sb.len && sb.buf[sb.len - 1] == '\n')
@@ -1362,40 +1362,62 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
strbuf_release(&cb.buf);
}
-void wt_status_get_state(struct wt_status_state *state,
- int get_detached_from)
+int wt_status_check_rebase(const struct worktree *wt,
+ struct wt_status_state *state)
{
struct stat st;
- unsigned char sha1[20];
- if (!stat(git_path_merge_head(), &st)) {
- state->merge_in_progress = 1;
- } else if (!stat(git_path("rebase-apply"), &st)) {
- if (!stat(git_path("rebase-apply/applying"), &st)) {
+ if (!stat(worktree_git_path(wt, "rebase-apply"), &st)) {
+ if (!stat(worktree_git_path(wt, "rebase-apply/applying"), &st)) {
state->am_in_progress = 1;
- if (!stat(git_path("rebase-apply/patch"), &st) && !st.st_size)
+ if (!stat(worktree_git_path(wt, "rebase-apply/patch"), &st) && !st.st_size)
state->am_empty_patch = 1;
} else {
state->rebase_in_progress = 1;
- state->branch = read_and_strip_branch("rebase-apply/head-name");
- state->onto = read_and_strip_branch("rebase-apply/onto");
+ state->branch = get_branch(wt, "rebase-apply/head-name");
+ state->onto = get_branch(wt, "rebase-apply/onto");
}
- } else if (!stat(git_path("rebase-merge"), &st)) {
- if (!stat(git_path("rebase-merge/interactive"), &st))
+ } else if (!stat(worktree_git_path(wt, "rebase-merge"), &st)) {
+ if (!stat(worktree_git_path(wt, "rebase-merge/interactive"), &st))
state->rebase_interactive_in_progress = 1;
else
state->rebase_in_progress = 1;
- state->branch = read_and_strip_branch("rebase-merge/head-name");
- state->onto = read_and_strip_branch("rebase-merge/onto");
+ state->branch = get_branch(wt, "rebase-merge/head-name");
+ state->onto = get_branch(wt, "rebase-merge/onto");
+ } else
+ return 0;
+ return 1;
+}
+
+int wt_status_check_bisect(const struct worktree *wt,
+ struct wt_status_state *state)
+{
+ struct stat st;
+
+ if (!stat(worktree_git_path(wt, "BISECT_LOG"), &st)) {
+ state->bisect_in_progress = 1;
+ state->branch = get_branch(wt, "BISECT_START");
+ return 1;
+ }
+ return 0;
+}
+
+void wt_status_get_state(struct wt_status_state *state,
+ int get_detached_from)
+{
+ struct stat st;
+ unsigned char sha1[20];
+
+ if (!stat(git_path_merge_head(), &st)) {
+ state->merge_in_progress = 1;
+ } else if (wt_status_check_rebase(NULL, state)) {
+ ; /* all set */
} else if (!stat(git_path_cherry_pick_head(), &st) &&
!get_sha1("CHERRY_PICK_HEAD", sha1)) {
state->cherry_pick_in_progress = 1;
hashcpy(state->cherry_pick_head_sha1, sha1);
}
- if (!stat(git_path("BISECT_LOG"), &st)) {
- state->bisect_in_progress = 1;
- state->branch = read_and_strip_branch("BISECT_START");
- }
+ wt_status_check_bisect(NULL, state);
if (!stat(git_path_revert_head(), &st) &&
!get_sha1("REVERT_HEAD", sha1)) {
state->revert_in_progress = 1;
diff --git a/wt-status.h b/wt-status.h
index c9b3b744e9..2ca93f6957 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -6,6 +6,8 @@
#include "color.h"
#include "pathspec.h"
+struct worktree;
+
enum color_wt_status {
WT_STATUS_HEADER = 0,
WT_STATUS_UPDATED,
@@ -100,6 +102,10 @@ void wt_status_prepare(struct wt_status *s);
void wt_status_print(struct wt_status *s);
void wt_status_collect(struct wt_status *s);
void wt_status_get_state(struct wt_status_state *state, int get_detached_from);
+int wt_status_check_rebase(const struct worktree *wt,
+ struct wt_status_state *state);
+int wt_status_check_bisect(const struct worktree *wt,
+ struct wt_status_state *state);
void wt_shortstatus_print(struct wt_status *s);
void wt_porcelain_print(struct wt_status *s);
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index 4fb7e79410..7423f77fc8 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -41,6 +41,8 @@ extern "C" {
#define XDF_IGNORE_BLANK_LINES (1 << 7)
+#define XDF_COMPACTION_HEURISTIC (1 << 8)
+
#define XDL_EMIT_FUNCNAMES (1 << 0)
#define XDL_EMIT_FUNCCONTEXT (1 << 2)
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index 2358a2d632..b3c6848875 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -400,9 +400,23 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,
}
+static int is_blank_line(xrecord_t **recs, long ix, long flags)
+{
+ return xdl_blankline(recs[ix]->ptr, recs[ix]->size, flags);
+}
+
+static int recs_match(xrecord_t **recs, long ixs, long ix, long flags)
+{
+ return (recs[ixs]->ha == recs[ix]->ha &&
+ xdl_recmatch(recs[ixs]->ptr, recs[ixs]->size,
+ recs[ix]->ptr, recs[ix]->size,
+ flags));
+}
+
int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec;
char *rchg = xdf->rchg, *rchgo = xdfo->rchg;
+ unsigned int blank_lines;
xrecord_t **recs = xdf->recs;
/*
@@ -436,14 +450,14 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
do {
grpsiz = ix - ixs;
+ blank_lines = 0;
/*
* If the line before the current change group, is equal to
* the last line of the current change group, shift backward
* the group.
*/
- while (ixs > 0 && recs[ixs - 1]->ha == recs[ix - 1]->ha &&
- xdl_recmatch(recs[ixs - 1]->ptr, recs[ixs - 1]->size, recs[ix - 1]->ptr, recs[ix - 1]->size, flags)) {
+ while (ixs > 0 && recs_match(recs, ixs - 1, ix - 1, flags)) {
rchg[--ixs] = 1;
rchg[--ix] = 0;
@@ -470,8 +484,9 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
* the line next of the current change group, shift forward
* the group.
*/
- while (ix < nrec && recs[ixs]->ha == recs[ix]->ha &&
- xdl_recmatch(recs[ixs]->ptr, recs[ixs]->size, recs[ix]->ptr, recs[ix]->size, flags)) {
+ while (ix < nrec && recs_match(recs, ixs, ix, flags)) {
+ blank_lines += is_blank_line(recs, ix, flags);
+
rchg[ixs++] = 0;
rchg[ix++] = 1;
@@ -498,6 +513,23 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
rchg[--ix] = 0;
while (rchgo[--ixo]);
}
+
+ /*
+ * If a group can be moved back and forth, see if there is a
+ * blank line in the moving space. If there is a blank line,
+ * make sure the last blank line is the end of the group.
+ *
+ * As we already shifted the group forward as far as possible
+ * in the earlier loop, we need to shift it back only if at all.
+ */
+ if ((flags & XDF_COMPACTION_HEURISTIC) && blank_lines) {
+ while (ixs > 0 &&
+ !is_blank_line(recs, ix - 1, flags) &&
+ recs_match(recs, ixs - 1, ix - 1, flags)) {
+ rchg[--ixs] = 1;
+ rchg[--ix] = 0;
+ }
+ }
}
return 0;