summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore33
-rw-r--r--.mailmap1
-rw-r--r--.travis.yml32
-rw-r--r--Documentation/RelNotes/2.9.0.txt395
-rw-r--r--Documentation/SubmittingPatches80
-rw-r--r--Documentation/config.txt118
-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-config.txt6
-rw-r--r--Documentation/git-filter-branch.txt2
-rw-r--r--Documentation/git-for-each-ref.txt2
-rw-r--r--Documentation/git-init.txt7
-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-submodule.txt26
-rw-r--r--Documentation/git-worktree.txt8
-rw-r--r--Documentation/gitcredentials.txt5
-rw-r--r--Documentation/githooks.txt73
-rw-r--r--Documentation/glossary-content.txt2
-rw-r--r--Documentation/merge-options.txt8
-rw-r--r--Documentation/pretty-options.txt14
-rw-r--r--Documentation/technical/api-credentials.txt4
-rw-r--r--Documentation/technical/pack-protocol.txt2
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile49
-rw-r--r--README.md7
l---------RelNotes2
-rw-r--r--bisect.c7
-rw-r--r--builtin/am.c11
-rw-r--r--builtin/apply.c2
-rw-r--r--builtin/branch.c15
-rw-r--r--builtin/checkout.c3
-rw-r--r--builtin/clone.c26
-rw-r--r--builtin/commit-tree.c4
-rw-r--r--builtin/commit.c1
-rw-r--r--builtin/diff.c1
-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.c17
-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.c5
-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.c31
-rw-r--r--bundle.c23
-rw-r--r--cache-tree.c4
-rw-r--r--cache.h9
-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/precompose_utf8.c2
-rw-r--r--compat/win32/syslog.c6
-rw-r--r--compat/win32mmap.c31
-rw-r--r--config.c32
-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--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.c7
-rw-r--r--dir.h1
-rw-r--r--editor.c3
-rw-r--r--entry.c10
-rw-r--r--environment.c1
-rw-r--r--fast-import.c10
-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-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.sh3
-rw-r--r--git-rebase--merge.sh4
-rwxr-xr-xgit-rebase.sh12
-rwxr-xr-xgit-send-email.perl5
-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--path.c2
-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.c644
-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.c32
-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/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)0
-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
-rwxr-xr-xt/lib-gpg.sh5
-rw-r--r--t/lib-httpd/apache.conf8
-rwxr-xr-xt/t0000-basic.sh2
-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.sh12
-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/t3421-rebase-topology-linear.sh4
-rwxr-xr-xt/t3513-revert-submodule.sh4
-rwxr-xr-xt/t4001-diff-rename.sh125
-rwxr-xr-xt/t4013-diff-various.sh2
-rwxr-xr-xt/t4014-format-patch.sh4
-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/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.sh7
-rwxr-xr-xt/t5601-clone.sh4
-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/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.sh2
-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.sh14
-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.c2
-rw-r--r--wrap-for-bin.sh1
-rw-r--r--wrapper.c21
-rw-r--r--wt-status.c4
-rw-r--r--xdiff/xdiff.h2
-rw-r--r--xdiff/xdiffi.c40
291 files changed, 5058 insertions, 2149 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..adab5b89bb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -22,8 +22,11 @@ addons:
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 +35,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/RelNotes/2.9.0.txt b/Documentation/RelNotes/2.9.0.txt
new file mode 100644
index 0000000000..2448a7eb7f
--- /dev/null
+++ b/Documentation/RelNotes/2.9.0.txt
@@ -0,0 +1,395 @@
+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 890fca8 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.
+
+
+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.
+
+ Note that this can break your tests if you check out revisions
+ across the merge boundary of this topic, e0b58519 (Merge branch
+ 'nd/test-helpers', 2016-04-29), as bin-wrappers/test-* are not
+ rebuilt to point the underlying executables. For now, "make
+ distclean" is your friend.
+
+ * 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.
+ (merge 1fb3fb4 ld/p4-test-py3 later to maint).
+
+ * 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.
+
+
+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.
+ (merge 1245c74 ky/imap-send-openssl-1.1.0 later to maint).
+
+ * 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.
+ (merge 2ab5660 sb/submodule-path-misc-bugs later to maint).
+
+ * 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.
+ (merge d3c06c1 da/user-useconfigonly later to maint).
+
+ * "git mv old new" did not adjust the path for a submodule that lives
+ as a subdirectory inside old/ directory correctly.
+ (merge a127331 sb/mv-submodule-fix later to maint).
+
+ * "git replace -e" did not honour "core.editor" configuration.
+ (merge 36b1437 js/replace-edit-use-editor-configuration later to maint).
+
+ * "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.
+ (merge f924b52a jk/push-client-deadlock-fix later to maint).
+
+ * mmap emulation on Windows has been optimized and work better without
+ consuming paging store when not needed.
+ (merge d5425d1 js/win32-mmap later to maint).
+
+ * A question by "git send-email" to ask the identity of the sender
+ has been updated.
+ (merge 0d6b21e jd/send-email-to-whom later to maint).
+
+ * UI consistency improvements for "git mergetool".
+ (merge cce076e nf/mergetool-prompt later to maint).
+
+ * "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.
+ (merge 9e220fe ls/p4-lfs-test-fix-2.7.0 later to maint).
+
+ * Recent update to Git LFS broke "git p4" by changing the output from
+ its "lfs pointer" subcommand.
+ (merge 82f2567 ls/p4-lfs later to maint).
+
+ * "git fetch" test t5510 was flaky while running a (forced) automagic
+ garbage collection.
+ (merge bb05510 js/close-packs-before-gc later to maint).
+
+ * Documentation updates to help contributors setting up Travis CI
+ test for their patches.
+ (merge 0e5d028 ls/travis-submitting-patches later to maint).
+
+ * Some multi-byte encoding can have a backslash byte as a later part
+ of one letter, which would confuse "highlight" filter used in
+ gitweb.
+ (merge 029f372 sk/gitweb-highlight-encoding later to maint).
+
+ * "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).
+
+
+ * Other minor clean-ups and documentation updates
+ (merge 8b5a3e9 kn/for-each-tag-branch later to maint).
+ (merge 99dab16 sb/misc-cleanups later to maint).
+ (merge 7a6a44c cc/apply later to maint).
+ (merge 6594883 nd/remove-unused later to maint).
+ (merge 0ff7410 sg/test-lib-simplify-expr-away later to maint).
+ (merge 060e776 jk/fix-attribute-macro-in-2.5 later to maint).
+ (merge d16df0c rt/string-list-lookup-cleanup later to maint).
+ (merge 376eb60 sb/config-exit-status-list later to maint).
+ (merge 9cea46c ew/doc-split-pack-disables-bitmap later to maint).
+ (merge fa72245 ew/normal-to-e later to maint).
+ (merge 2e39a24 rn/glossary-typofix later to maint).
+ (merge cadfbef sb/clean-test-fix 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..3127aed1d8 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
~~~~~~~~~
@@ -486,10 +496,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 +603,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 +624,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 +1132,15 @@ 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.
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
@@ -1334,7 +1360,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 +1500,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 +1519,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 +1680,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 +1919,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
@@ -2147,8 +2188,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 +2592,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 +2774,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-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-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-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-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/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/merge-options.txt b/Documentation/merge-options.txt
index f08e9b80c5..dfb43d000f 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -114,3 +114,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-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/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..655b49011f 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.8.0.GIT
LF='
'
diff --git a/Makefile b/Makefile
index a83e322f46..bc3d41ebf3 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)
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/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/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..eacec57593 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -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);
}
@@ -593,8 +595,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 +631,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 +839,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..ea2fe1cf3f 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);
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..391126e58d 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -186,6 +186,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() */
}
diff --git a/builtin/diff.c b/builtin/diff.c
index 52c98a9217..343c6b8f25 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -318,6 +318,7 @@ 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);
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..dff3fbbb43 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);
@@ -1280,10 +1286,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;
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..6fd058de92 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;
}
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..331ecf6761 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)
@@ -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"));
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..eddf3e8b0c 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;
@@ -958,8 +959,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 +1155,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 +1767,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/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/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/config.c b/config.c
index 47b2ce1d5a..21ff89cfb0 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;
@@ -1309,14 +1312,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 +2012,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 +2026,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 +2110,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 +2119,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 +2175,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 +2326,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 +2381,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/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/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..656f272adc 100644
--- a/dir.c
+++ b/dir.c
@@ -64,13 +64,6 @@ int strncmp_icase(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)
diff --git a/dir.h b/dir.h
index 301b737a37..d56d2fb48f 100644
--- a/dir.h
+++ b/dir.h
@@ -272,7 +272,6 @@ 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);
/*
* 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..2857e3f366 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;
diff --git a/fast-import.c b/fast-import.c
index 9fc7093406..21881d1fdd 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -414,7 +414,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;
}
@@ -1806,8 +1806,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;
}
@@ -1822,8 +1822,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-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 655ebaa471..1c6dfb6d56 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -549,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)
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-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/path.c b/path.c
index bbaea5ab0b..503766784c 100644
--- a/path.c
+++ b/path.c
@@ -134,7 +134,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")
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..1810c040da 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;
- /* Is there a recorded resolution we could attempt to apply? */
- if (has_rerere_resolution(id)) {
- if (merge(id, path))
- return; /* failed to replay */
+ vid.variant = variant;
+ if (merge(&vid, path))
+ continue; /* 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,38 @@ 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);
+ return error("Failed to update conflicted state in '%s'",
+ path);
+ }
+ 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)
+ return error("no remembered resolution for '%s'", path);
+
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)));
+ : error_errno("cannot unlink %s", filename));
/*
* Update the preimage so that the user can resolve the
@@ -897,29 +1122,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 +1141,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 +1170,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 +1186,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 +1232,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..a7f45b3490 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -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/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..2c8c8f18ed 100644
--- a/test-parse-options.c
+++ b/t/helper/test-parse-options.c
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-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..b8ed96fac6 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -102,6 +102,14 @@ Alias /auth/dumb/ www/auth/dumb/
SetEnv GIT_HTTP_EXPORT_ALL
Header set Set-Cookie name=value
</LocationMatch>
+<LocationMatch /smart_headers/>
+ <RequireAll>
+ Require expr %{HTTP:x-magic-one} == 'abra'
+ Require expr %{HTTP:x-magic-two} == 'cadabra'
+ </RequireAll>
+ 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/
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/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..3acb9926f2 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -213,4 +213,16 @@ 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_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/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/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..eed2981b96 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 &&
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/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..e44fe72c7a 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -282,5 +282,12 @@ 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 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"
+'
+
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/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..20a3ffed69
--- /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' '
+ 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 &&
+ 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/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..ff7a9e968f 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
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..286c5f33d1 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
@@ -854,10 +854,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..89ebe67a50 100644
--- a/worktree.c
+++ b/worktree.c
@@ -18,7 +18,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
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..1ea2ebe4c0 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1063,9 +1063,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)
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;