summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore33
-rw-r--r--Documentation/RelNotes/2.8.2.txt15
-rw-r--r--Documentation/RelNotes/2.8.3.txt61
-rw-r--r--Documentation/RelNotes/2.9.0.txt342
-rw-r--r--Documentation/config.txt24
-rw-r--r--Documentation/diff-config.txt10
-rw-r--r--Documentation/diff-options.txt2
-rw-r--r--Documentation/git-clone.txt6
-rw-r--r--Documentation/git-for-each-ref.txt2
-rw-r--r--Documentation/git-merge.txt1
-rw-r--r--Documentation/git-p4.txt11
-rw-r--r--Documentation/git-pull.txt9
-rw-r--r--Documentation/git-rebase.txt6
-rw-r--r--Documentation/git-submodule.txt7
-rw-r--r--Documentation/git-worktree.txt8
-rw-r--r--Documentation/git.txt3
-rw-r--r--Documentation/gitcredentials.txt5
-rw-r--r--Documentation/merge-options.txt8
-rw-r--r--Documentation/pretty-options.txt14
-rw-r--r--Documentation/technical/api-config.txt7
-rw-r--r--Documentation/technical/api-trace.txt2
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile38
-rw-r--r--README.md7
l---------RelNotes2
-rw-r--r--abspath.c5
-rw-r--r--branch.c23
-rw-r--r--branch.h7
-rw-r--r--builtin/apply.c32
-rw-r--r--builtin/blame.c1
-rw-r--r--builtin/branch.c44
-rw-r--r--builtin/checkout.c3
-rw-r--r--builtin/clone.c19
-rw-r--r--builtin/commit.c7
-rw-r--r--builtin/diff.c1
-rw-r--r--builtin/fetch.c2
-rw-r--r--builtin/fsck.c3
-rw-r--r--builtin/init-db.c45
-rw-r--r--builtin/log.c17
-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/pull.c36
-rw-r--r--builtin/receive-pack.c55
-rw-r--r--builtin/replace.c1
-rw-r--r--builtin/send-pack.c2
-rw-r--r--builtin/submodule--helper.c370
-rw-r--r--builtin/tag.c28
-rw-r--r--builtin/verify-tag.c61
-rw-r--r--builtin/worktree.c29
-rw-r--r--bundle.c23
-rw-r--r--cache.h42
-rw-r--r--commit.h1
-rw-r--r--compat/apple-common-crypto.h16
-rw-r--r--compat/mingw.c9
-rw-r--r--compat/mingw.h2
-rw-r--r--compat/snprintf.c2
-rw-r--r--compat/vcbuild/include/unistd.h4
-rw-r--r--compat/win32mmap.c31
-rw-r--r--config.c32
-rw-r--r--config.mak.uname1
-rw-r--r--configure.ac4
-rw-r--r--contrib/completion/git-completion.bash7
-rw-r--r--credential-cache.c1
-rw-r--r--credential.c9
-rw-r--r--diff.c5
-rw-r--r--diff.h1
-rw-r--r--diffcore-rename.c6
-rw-r--r--dir.c7
-rw-r--r--dir.h1
-rw-r--r--environment.c23
-rw-r--r--fetch-pack.c4
-rwxr-xr-xgit-add--interactive.perl12
-rw-r--r--git-compat-util.h3
-rwxr-xr-xgit-merge-octopus.sh6
-rw-r--r--git-mergetool--lib.sh25
-rwxr-xr-xgit-p4.py21
-rw-r--r--git-rebase--interactive.sh3
-rwxr-xr-xgit-rebase.sh7
-rwxr-xr-xgit-send-email.perl6
-rwxr-xr-xgit-submodule.sh109
-rw-r--r--gpg-interface.c2
-rw-r--r--http-backend.c4
-rw-r--r--http.c5
-rw-r--r--ident.c16
-rw-r--r--imap-send.c32
-rw-r--r--log-tree.c1
-rw-r--r--merge-recursive.c8
-rw-r--r--mergetools/examdiff18
-rw-r--r--mergetools/winmerge21
-rw-r--r--path.c10
-rw-r--r--po/fr.po10
-rw-r--r--pretty.c90
-rw-r--r--quote.c13
-rw-r--r--quote.h3
-rw-r--r--refs.c149
-rw-r--r--refs.h9
-rw-r--r--refs/files-backend.c442
-rw-r--r--refs/refs-internal.h15
-rw-r--r--rerere.c626
-rw-r--r--rerere.h4
-rw-r--r--revision.c14
-rw-r--r--revision.h2
-rw-r--r--run-command.c22
-rw-r--r--run-command.h11
-rw-r--r--send-pack.c7
-rw-r--r--setup.c146
-rw-r--r--strbuf.c6
-rw-r--r--strbuf.h6
-rw-r--r--submodule-config.c22
-rw-r--r--submodule-config.h2
-rw-r--r--submodule.c37
-rw-r--r--submodule.h18
-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)0
-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.sh33
-rwxr-xr-xt/lib-gpg.sh5
-rwxr-xr-xt/t0300-credentials.sh11
-rwxr-xr-xt/t1020-subdirectory.sh10
-rwxr-xr-xt/t1300-repo-config.sh14
-rwxr-xr-xt/t1410-reflog.sh10
-rwxr-xr-xt/t1430-bad-ref-name.sh132
-rwxr-xr-xt/t2025-worktree-add.sh12
-rwxr-xr-xt/t3033-merge-toplevel.sh16
-rwxr-xr-xt/t3200-branch.sh29
-rwxr-xr-xt/t3203-branch-output.sh12
-rwxr-xr-xt/t3404-rebase-interactive.sh26
-rwxr-xr-xt/t3412-rebase-root.sh2
-rwxr-xr-xt/t4001-diff-rename.sh136
-rwxr-xr-xt/t4013-diff-various.sh2
-rwxr-xr-xt/t4014-format-patch.sh4
-rwxr-xr-xt/t4047-diff-dirstat.sh3
-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/t5400-send-pack.sh12
-rwxr-xr-xt/t5500-fetch-pack.sh6
-rwxr-xr-xt/t5504-fetch-receive-strict.sh10
-rwxr-xr-xt/t5520-pull.sh74
-rwxr-xr-xt/t5521-pull-options.sh21
-rwxr-xr-xt/t5526-fetch-submodules.sh14
-rwxr-xr-xt/t5532-fetch-proxy.sh21
-rwxr-xr-xt/t5550-http-fetch-dumb.sh17
-rwxr-xr-xt/t5601-clone.sh2
-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/t6026-merge-attr.sh3
-rwxr-xr-xt/t6029-merge-subtree.sh2
-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.sh95
-rwxr-xr-xt/t7001-mv.sh16
-rwxr-xr-xt/t7004-tag.sh41
-rwxr-xr-xt/t7030-verify-tag.sh13
-rwxr-xr-xt/t7400-submodule-basic.sh45
-rwxr-xr-xt/t7406-submodule-update.sh109
-rwxr-xr-xt/t7407-submodule-foreach.sh49
-rwxr-xr-xt/t7412-submodule--helper.sh26
-rwxr-xr-xt/t7501-commit.sh20
-rwxr-xr-xt/t7605-merge-resolve.sh6
-rwxr-xr-xt/t8003-blame-corner-cases.sh14
-rwxr-xr-xt/t9400-git-cvsserver-server.sh3
-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.sh4
-rw-r--r--tag.c53
-rw-r--r--tag.h2
-rw-r--r--wrapper.c17
-rw-r--r--wt-status.c4
207 files changed, 4235 insertions, 1312 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/Documentation/RelNotes/2.8.2.txt b/Documentation/RelNotes/2.8.2.txt
index 3db67f4c55..447b1933a8 100644
--- a/Documentation/RelNotes/2.8.2.txt
+++ b/Documentation/RelNotes/2.8.2.txt
@@ -52,4 +52,19 @@ Fixes since v2.8.1
nothing into an unborn history (which is arguably unusual usage,
which perhaps was the reason why nobody noticed it).
+ * Build updates for MSVC.
+
+ * "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.
+
+ * 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.
+
Also contains minor documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.8.3.txt b/Documentation/RelNotes/2.8.3.txt
new file mode 100644
index 0000000000..6030f38063
--- /dev/null
+++ b/Documentation/RelNotes/2.8.3.txt
@@ -0,0 +1,61 @@
+Git v2.8.3 Release Notes
+========================
+
+Fixes since v2.8.2
+------------------
+
+ * "git send-email" now uses a more readable timestamps when
+ formulating a message ID.
+
+ * 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.
+
+ * When "git worktree" feature is in use, "git branch -d" allowed
+ deletion of a branch that is checked out in another worktree
+
+ * 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 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.
+
+ * 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.
+
+ * 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
+ and http://thread.gmane.org/gmane.comp.version-control.git/275680.
+
+Also contains minor documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.9.0.txt b/Documentation/RelNotes/2.9.0.txt
new file mode 100644
index 0000000000..b4783b84c7
--- /dev/null
+++ b/Documentation/RelNotes/2.9.0.txt
@@ -0,0 +1,342 @@
+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.
+
+
+Updates since v2.8
+------------------
+
+UI, Workflows & Features
+
+ * 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.
+ (merge f916ab0 ew/send-email-readable-message-id later to maint).
+
+ * "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).
+
+
+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.
+ (merge 65a3629 mp/upload-pack-use-embedded-args later to maint).
+
+ * 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.
+ (merge 11e6b3f jk/startup-info later to maint).
+
+ * 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.
+ (merge 8fbb03a sb/clone-t57-t56 later to maint).
+
+ * The tests that involve running httpd leaked the system-wide
+ configuration in /etc/gitconfig to the tested environment.
+ (merge 1fad503 jk/test-httpd-config-nosystem later to maint).
+
+ * Build updates for MSVC.
+ (merge 0ef60af ss/msvc later to maint).
+
+ * 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.
+ (merge 274db84 jk/check-repository-format later to maint).
+
+ * 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).
+
+
+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.
+ (merge 24990b2 jk/config-get-urlmatch later to maint).
+
+ * 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.
+ (merge fc7d47f jk/rev-parse-local-env-vars later to maint).
+
+ * "git index-pack --keep[=<msg>] pack-$name.pack" simply did not work.
+ (merge 0e94242 jc/maint-index-pack-keep later to maint).
+
+ * Fetching of history by naming a commit object name directly didn't
+ work across remote-curl transport.
+ (merge 754ecb1 gf/fetch-pack-direct-object-fetch later to maint).
+
+ * A small memory leak in an error codepath has been plugged in xdiff
+ code.
+ (merge 87f1625 rj/xdiff-prepare-plug-leak-on-error-codepath later to maint).
+
+ * strbuf_getwholeline() did not NUL-terminate the buffer on certain
+ corner cases in its error codepath.
+ (merge b709043 jk/getwholeline-getdelim-empty later to maint).
+
+ * "git mergetool" did not work well with conflicts that both sides
+ deleted.
+ (merge a298604 da/mergetool-delete-delete-conflict later to maint).
+
+ * "git send-email" had trouble parsing alias file in mailrc format
+ when lines in it had trailing whitespaces on them.
+ (merge a277d1e jk/send-email-rtrim-mailrc-alias later to maint).
+
+ * 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.
+ (merge b64c1e0 ss/commit-squash-msg later to maint).
+
+ * "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).
+ (merge b84e65d jv/merge-nothing-into-void later to maint).
+
+ * 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.
+ (merge f292244 ky/branch-d-worktree later to maint).
+
+ * 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.
+ (merge 18eb3a9 ky/branch-m-worktree later to maint).
+
+ * "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.
+ (merge ca4e3ca sg/diff-multiple-identical-renames later to maint).
+
+ * "git send-pack --all <there>" was broken when its command line
+ option parsing was written in the 2.6 timeframe.
+ (merge c677756 sk/send-pack-all-fix later to maint).
+
+ * "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.
+ (merge b73a1bc es/format-patch-doc-hide-no-patch later to maint).
+
+ * 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.
+ (merge a08feb8 tb/blame-force-read-cache-to-workaround-safe-crlf later to maint).
+
+ * 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).
+ (merge 95c38fb jk/branch-shortening-funny-symrefs later to maint).
+
+ * 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.
+ (merge 1f15ba1 sb/submodule-helper-clone-regression-fix later to maint).
+
+ * "git commit" misbehaved in a few minor ways when an empty message
+ is given via -m '', all of which has been corrected.
+ (merge 27014cb ad/commit-have-m-option later to maint).
+
+ * Support for CRAM-MD5 authentication method in "git imap-send" did
+ not work well.
+ (merge eb94ee7 ky/imap-send later to maint).
+
+ * 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.
+ (merge 87f8a0b jc/http-socks5h later to maint).
+
+ * "git config" had a codepath that tried to pass a NULL to
+ printf("%s"), which nobody seems to have noticed.
+ (merge 1cae428 jk/do-not-printf-NULL later to maint).
+
+ * 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 e53a64b ad/cygwin-wants-rename later to maint).
+
+ * "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 c4b2751 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).
+
+ * Other minor clean-ups and documentation updates
+ (merge aed7480 mm/lockfile-error-message later to maint).
+ (merge bfee614 jc/index-pack later to maint).
+ (merge f870899 ss/exc-flag-is-a-collection-of-bits later to maint).
+ (merge dde7891 pb/t7502-drop-dup later to maint).
+ (merge 3bd1b51 cc/doc-recommend-performance-trace-to-file later to maint).
+ (merge 7d5e9c9 jk/credential-cache-comment-exit later to maint).
+ (merge 16a86d4 nd/apply-doc later to maint).
+ (merge c3f6b85 pb/opt-cmdmode-doc later to maint).
+ (merge 30211fb oa/doc-diff-check later to maint).
+ (merge 01d98e8 ak/use-hashmap-iter-first-in-submodule-config later to maint).
+ (merge 8b5a3e9 kn/for-each-tag-branch later to maint).
+ (merge 9c60d9f sb/misc-cleanups later to maint).
+ (merge 7a6a44c cc/apply later to maint).
+ (merge 8e9b208 js/mingw-tests-2.8 later to maint).
+ (merge d55de70 jc/makefile-redirection-stderr later to maint).
+ (merge 4232b21 ep/trace-doc-sample-fix later to maint).
+ (merge ef8c95e ew/send-email-drop-data-dumper later to maint).
+ (merge 24041d6 jc/xstrfmt-null-with-prec-0 later to maint).
+ (merge 7bec7f5 jk/use-write-script-more later to maint).
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 2cd6bdd7d2..42d2b50477 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1113,8 +1113,9 @@ commit.template::
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
@@ -1886,6 +1887,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
@@ -2729,6 +2738,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 32f48ed647..4b0318e2ac 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -26,12 +26,12 @@ ifndef::git-format-patch[]
ifdef::git-diff[]
This is the default.
endif::git-diff[]
-endif::git-format-patch[]
-s::
--no-patch::
Suppress diff output. Useful for commands like `git show` that
show the patch by default, or to cancel the effect of `--patch`.
+endif::git-format-patch[]
-U<n>::
--unified=<n>::
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index b7c467a001..45d74be297 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -14,7 +14,7 @@ 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>
+ [--recursive | --recurse-submodules] [--jobs <n>] [--] <repository>
[<directory>]
DESCRIPTION
@@ -219,6 +219,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-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 012e8f9a08..c52578bb87 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -76,7 +76,7 @@ OPTIONS
specified commit (HEAD if not specified).
--contains [<object>]::
- Only list tags which contain the specified commit (HEAD if not
+ Only list refs which contain the specified commit (HEAD if not
specified).
FIELD NAMES
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-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-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-submodule.txt b/Documentation/git-submodule.txt
index 1572f058f5..13adebf7b7 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -16,7 +16,7 @@ SYNOPSIS
'git submodule' [--quiet] deinit [-f|--force] [--] <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>
@@ -377,6 +377,11 @@ for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully.
clone with a history truncated to the specified number of revisions.
See linkgit:git-clone[1]
+-j <n>::
+--jobs <n>::
+ This option is only valid for the update command.
+ Clone new submodules in parallel with as many jobs.
+ Defaults to the `submodule.fetchJobs` option.
<path>...::
Paths to submodule(s). When specified this will restrict the command
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 62c76c1c89..c62234538b 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -9,7 +9,7 @@ git-worktree - Manage multiple working trees
SYNOPSIS
--------
[verse]
-'git worktree add' [-f] [--detach] [-b <new-branch>] <path> [<branch>]
+'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
'git worktree prune' [-n] [-v] [--expire <expire>]
'git worktree list' [--porcelain]
@@ -87,6 +87,12 @@ OPTIONS
With `add`, detach HEAD in the new working tree. See "DETACHED HEAD"
in linkgit:git-checkout[1].
+--[no-]checkout::
+ By default, `add` checks out `<branch>`, however, `--no-checkout` can
+ be used to suppress checkout in order to make customizations,
+ such as configuring sparse-checkout. See "Sparse checkout"
+ in linkgit:git-read-tree[1].
+
-n::
--dry-run::
With `prune`, do not remove anything; just report what it would
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 8afe349781..34ff007a98 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -43,9 +43,10 @@ unreleased) version of Git, that is available from the 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v2.8.1/git.html[documentation for release 2.8.1]
+* link:v2.8.2/git.html[documentation for release 2.8.2]
* release notes for
+ link:RelNotes/2.8.2.txt[2.8.2].
link:RelNotes/2.8.1.txt[2.8.1].
link:RelNotes/2.8.0.txt[2.8].
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/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-config.txt b/Documentation/technical/api-config.txt
index 0d8b99b368..20741f345e 100644
--- a/Documentation/technical/api-config.txt
+++ b/Documentation/technical/api-config.txt
@@ -63,13 +63,6 @@ parse for configuration, rather than looking in the usual files. Regular
Specify whether include directives should be followed in parsed files.
Regular `git_config` defaults to `1`.
-There is a special version of `git_config` called `git_config_early`.
-This version takes an additional parameter to specify the repository
-config, instead of having it looked up via `git_path`. This is useful
-early in a Git program before the repository has been found. Unless
-you're working with early setup code, you probably don't want to use
-this.
-
Reading Specific Files
----------------------
diff --git a/Documentation/technical/api-trace.txt b/Documentation/technical/api-trace.txt
index 389ae16d15..fadb5979c4 100644
--- a/Documentation/technical/api-trace.txt
+++ b/Documentation/technical/api-trace.txt
@@ -28,7 +28,7 @@ static struct trace_key trace_foo = TRACE_KEY_INIT(FOO);
static void trace_print_foo(const char *message)
{
- trace_print_key(&trace_foo, message);
+ trace_printf_key(&trace_foo, "%s", message);
}
------------
+
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 46595dad22..655b49011f 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.8.1
+DEF_VER=v2.8.0.GIT
LF='
'
diff --git a/Makefile b/Makefile
index 2742a6977c..6c4e2dbbc8 100644
--- a/Makefile
+++ b/Makefile
@@ -355,9 +355,6 @@ all::
#
# Define HAVE_CLOCK_MONOTONIC if your platform has CLOCK_MONOTONIC in librt.
#
-# Define NO_HMAC_CTX_CLEANUP if your OpenSSL is version 0.9.6b or earlier to
-# cleanup the HMAC context with the older HMAC_cleanup function.
-#
# Define USE_PARENS_AROUND_GETTEXT_N to "yes" if your compiler happily
# compiles the following initialization:
#
@@ -624,7 +621,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.
@@ -1138,9 +1135,6 @@ ifndef NO_OPENSSL
ifdef NEEDS_CRYPTO_WITH_SSL
OPENSSL_LIBSSL += -lcrypto
endif
- ifdef NO_HMAC_CTX_CLEANUP
- BASIC_CFLAGS += -DNO_HMAC_CTX_CLEANUP
- endif
else
BASIC_CFLAGS += -DNO_OPENSSL
BLK_SHA1 = 1
@@ -1904,7 +1898,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) \
@@ -2211,7 +2205,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.
@@ -2231,25 +2225,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))
@@ -2263,10 +2257,10 @@ sparse: $(SP_OBJ)
check: common-cmds.h
@if sparse; \
then \
- echo 2>&1 "Use 'make sparse' instead"; \
+ echo >&2 "Use 'make sparse' instead"; \
$(MAKE) --no-print-directory sparse; \
else \
- echo 2>&1 "Did you mean 'make test'?"; \
+ echo >&2 "Did you mean 'make test'?"; \
exit 1; \
fi
@@ -2456,8 +2450,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 d40c3e126c..66606735cb 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.8.1.txt \ No newline at end of file
+Documentation/RelNotes/2.9.0.txt \ No newline at end of file
diff --git a/abspath.c b/abspath.c
index 5edb4e7816..2825de8591 100644
--- a/abspath.c
+++ b/abspath.c
@@ -167,7 +167,6 @@ const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
strbuf_add(&path, pfx, pfx_len);
strbuf_addstr(&path, arg);
#else
- char *p;
/* don't add prefix to absolute paths, but still replace '\' by '/' */
strbuf_reset(&path);
if (is_absolute_path(arg))
@@ -175,9 +174,7 @@ const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
else if (pfx_len)
strbuf_add(&path, pfx, pfx_len);
strbuf_addstr(&path, arg);
- for (p = path.buf + pfx_len; *p; p++)
- if (*p == '\\')
- *p = '/';
+ convert_slashes(path.buf + pfx_len);
#endif
return path.buf;
}
diff --git a/branch.c b/branch.c
index c50ea42172..4162443707 100644
--- a/branch.c
+++ b/branch.c
@@ -344,3 +344,26 @@ void die_if_checked_out(const char *branch)
die(_("'%s' is already checked out at '%s'"), branch, existing);
}
}
+
+int replace_each_worktree_head_symref(const char *oldref, const char *newref)
+{
+ int ret = 0;
+ struct worktree **worktrees = get_worktrees();
+ int i;
+
+ for (i = 0; worktrees[i]; i++) {
+ if (worktrees[i]->is_detached)
+ continue;
+ if (strcmp(oldref, worktrees[i]->head_ref))
+ continue;
+
+ if (set_worktree_head_symref(worktrees[i]->git_dir, newref)) {
+ ret = -1;
+ error(_("HEAD of working tree %s is not updated"),
+ worktrees[i]->path);
+ }
+ }
+
+ free_worktrees(worktrees);
+ return ret;
+}
diff --git a/branch.h b/branch.h
index 78ad4387cd..d69163daf7 100644
--- a/branch.h
+++ b/branch.h
@@ -60,4 +60,11 @@ extern int read_branch_desc(struct strbuf *, const char *branch_name);
*/
extern void die_if_checked_out(const char *branch);
+/*
+ * Update all per-worktree HEADs pointing at the old ref to point the new ref.
+ * This will be used when renaming a branch. Returns 0 if successful, non-zero
+ * otherwise.
+ */
+extern int replace_each_worktree_head_symref(const char *oldref, const char *newref);
+
#endif
diff --git a/builtin/apply.c b/builtin/apply.c
index 42c610e2ec..8e4da2e1bd 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -931,22 +931,19 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name,
return find_name(line, NULL, p_value, TERM_TAB);
if (orig_name) {
- int len;
- const char *name;
+ int len = strlen(orig_name);
char *another;
- name = orig_name;
- len = strlen(name);
if (isnull)
- die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), name, linenr);
+ die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"),
+ orig_name, linenr);
another = find_name(line, NULL, p_value, TERM_TAB);
- if (!another || memcmp(another, name, len + 1))
+ if (!another || memcmp(another, orig_name, len + 1))
die((side == DIFF_NEW_NAME) ?
_("git apply: bad git-diff - inconsistent new filename on line %d") :
_("git apply: bad git-diff - inconsistent old filename on line %d"), linenr);
free(another);
return orig_name;
- }
- else {
+ } else {
/* expect "/dev/null" */
if (memcmp("/dev/null", line, 9) || line[9] != '\n')
die(_("git apply: bad git-diff - expected /dev/null on line %d"), linenr);
@@ -956,21 +953,15 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name,
static int gitdiff_oldname(const char *line, struct patch *patch)
{
- char *orig = patch->old_name;
patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name,
DIFF_OLD_NAME);
- if (orig != patch->old_name)
- free(orig);
return 0;
}
static int gitdiff_newname(const char *line, struct patch *patch)
{
- char *orig = patch->new_name;
patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name,
DIFF_NEW_NAME);
- if (orig != patch->new_name)
- free(orig);
return 0;
}
@@ -1872,6 +1863,11 @@ static struct fragment *parse_binary_hunk(char **buf_p,
return NULL;
}
+/*
+ * Returns:
+ * -1 in case of error,
+ * the length of the parsed binary patch otherwise
+ */
static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
{
/*
@@ -2017,6 +2013,8 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
linenr++;
used = parse_binary(buffer + hd + llen,
size - hd - llen, patch);
+ if (used < 0)
+ return -1;
if (used)
patchsize = used + llen;
else
@@ -4373,8 +4371,10 @@ static int apply_patch(int fd, const char *filename, int options)
patch->inaccurate_eof = !!(options & INACCURATE_EOF);
patch->recount = !!(options & RECOUNT);
nr = parse_chunk(buf.buf + offset, buf.len - offset, patch);
- if (nr < 0)
+ if (nr < 0) {
+ free_patch(patch);
break;
+ }
if (apply_in_reverse)
reverse_patches(patch);
if (use_patch(patch)) {
@@ -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/blame.c b/builtin/blame.c
index e982fb8137..21f42b0b62 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -2307,6 +2307,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
unsigned mode;
struct strbuf msg = STRBUF_INIT;
+ read_cache();
time(&now);
commit = alloc_commit_node();
commit->object.parsed = 1;
diff --git a/builtin/branch.c b/builtin/branch.c
index 7b45b6bd6b..0adba629d2 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -20,6 +20,7 @@
#include "utf8.h"
#include "wt-status.h"
#include "ref-filter.h"
+#include "worktree.h"
static const char * const builtin_branch_usage[] = {
N_("git branch [<options>] [-r | -a] [--merged | --no-merged]"),
@@ -215,16 +216,21 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
int flags = 0;
strbuf_branchname(&bname, argv[i]);
- if (kinds == FILTER_REFS_BRANCHES && !strcmp(head, bname.buf)) {
- error(_("Cannot delete the branch '%s' "
- "which you are currently on."), bname.buf);
- ret = 1;
- continue;
- }
-
free(name);
-
name = mkpathdup(fmt, bname.buf);
+
+ if (kinds == FILTER_REFS_BRANCHES) {
+ char *worktree = find_shared_symref("HEAD", name);
+ if (worktree) {
+ error(_("Cannot delete branch '%s' "
+ "checked out at '%s'"),
+ bname.buf, worktree);
+ free(worktree);
+ ret = 1;
+ continue;
+ }
+ }
+
target = resolve_ref_unsafe(name,
RESOLVE_REF_READING
| RESOLVE_REF_NO_RECURSE
@@ -393,22 +399,25 @@ static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth,
int current = 0;
int color;
struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
- const char *prefix = "";
+ const char *prefix_to_show = "";
+ const char *prefix_to_skip = NULL;
const char *desc = item->refname;
char *to_free = NULL;
switch (item->kind) {
case FILTER_REFS_BRANCHES:
- skip_prefix(desc, "refs/heads/", &desc);
+ prefix_to_skip = "refs/heads/";
+ skip_prefix(desc, prefix_to_skip, &desc);
if (!filter->detached && !strcmp(desc, head))
current = 1;
else
color = BRANCH_COLOR_LOCAL;
break;
case FILTER_REFS_REMOTES:
- skip_prefix(desc, "refs/remotes/", &desc);
+ prefix_to_skip = "refs/remotes/";
+ skip_prefix(desc, prefix_to_skip, &desc);
color = BRANCH_COLOR_REMOTE;
- prefix = remote_prefix;
+ prefix_to_show = remote_prefix;
break;
case FILTER_REFS_DETACHED_HEAD:
desc = to_free = get_head_description();
@@ -425,7 +434,7 @@ static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth,
color = BRANCH_COLOR_CURRENT;
}
- strbuf_addf(&name, "%s%s", prefix, desc);
+ strbuf_addf(&name, "%s%s", prefix_to_show, desc);
if (filter->verbose) {
int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf);
strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
@@ -436,8 +445,10 @@ static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth,
name.buf, branch_get_color(BRANCH_COLOR_RESET));
if (item->symref) {
- skip_prefix(item->symref, "refs/remotes/", &desc);
- strbuf_addf(&out, " -> %s", desc);
+ const char *symref = item->symref;
+ if (prefix_to_skip)
+ skip_prefix(symref, prefix_to_skip, &symref);
+ strbuf_addf(&out, " -> %s", symref);
}
else if (filter->verbose)
/* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
@@ -552,8 +563,7 @@ static void rename_branch(const char *oldname, const char *newname, int force)
if (recovery)
warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11);
- /* no need to pass logmsg here as HEAD didn't really move */
- if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL))
+ if (replace_each_worktree_head_symref(oldref.buf, newref.buf))
die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
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..6576ecf343 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -51,6 +51,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 +74,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"),
@@ -100,10 +103,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 +731,16 @@ 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 (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.c b/builtin/commit.c
index c733ec98b7..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() */
}
@@ -694,7 +695,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
}
}
- if (message.len) {
+ if (have_option_m) {
strbuf_addbuf(&sb, &message);
hook_arg1 = "message";
} else if (logfile && !strcmp(logfile, "-")) {
@@ -1171,9 +1172,9 @@ static int parse_and_validate_options(int argc, const char *argv[],
f++;
if (f > 1)
die(_("Only one of -c/-C/-F/--fixup can be used."));
- if (message.len && f > 0)
+ if (have_option_m && f > 0)
die((_("Option -m cannot be combined with -c/-C/-F/--fixup.")));
- if (f || message.len)
+ if (f || have_option_m)
template_file = NULL;
if (edit_message)
use_message = edit_message;
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..f8455bde7a 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;
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/init-db.c b/builtin/init-db.c
index da531f6b76..b2d8d40a67 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -95,6 +95,8 @@ static void copy_templates(const char *template_dir)
struct strbuf path = STRBUF_INIT;
struct strbuf template_path = STRBUF_INIT;
size_t template_len;
+ struct repository_format template_format;
+ struct strbuf err = STRBUF_INIT;
DIR *dir;
char *to_free = NULL;
@@ -121,17 +123,18 @@ static void copy_templates(const char *template_dir)
/* Make sure that template is from the correct vintage */
strbuf_addstr(&template_path, "config");
- repository_format_version = 0;
- git_config_from_file(check_repository_format_version,
- template_path.buf, NULL);
+ read_repository_format(&template_format, template_path.buf);
strbuf_setlen(&template_path, template_len);
- if (repository_format_version &&
- repository_format_version != GIT_REPO_VERSION) {
- warning(_("not copying templates of "
- "a wrong format version %d from '%s'"),
- repository_format_version,
- template_dir);
+ /*
+ * No mention of version at all is OK, but anything else should be
+ * verified.
+ */
+ if (template_format.version >= 0 &&
+ verify_repository_format(&template_format, &err) < 0) {
+ warning(_("not copying templates from '%s': %s"),
+ template_dir, err.buf);
+ strbuf_release(&err);
goto close_free_return;
}
@@ -199,13 +202,13 @@ static int create_default_files(const char *template_path)
/* reading existing config may have overwrote it */
if (init_shared_repository != -1)
- shared_repository = init_shared_repository;
+ set_shared_repository(init_shared_repository);
/*
* We would have created the above under user's umask -- under
* shared-repository settings, we would need to fix them up.
*/
- if (shared_repository) {
+ if (get_shared_repository()) {
adjust_shared_perm(get_git_dir());
adjust_shared_perm(git_path_buf(&buf, "refs"));
adjust_shared_perm(git_path_buf(&buf, "refs/heads"));
@@ -370,7 +373,7 @@ int init_db(const char *template_dir, unsigned int flags)
create_object_directory();
- if (shared_repository) {
+ if (get_shared_repository()) {
char buf[10];
/* We do not spell "group" and such, so that
* the configuration can be read by older version
@@ -378,12 +381,12 @@ int init_db(const char *template_dir, unsigned int flags)
* and compatibility values for PERM_GROUP and
* PERM_EVERYBODY.
*/
- if (shared_repository < 0)
+ if (get_shared_repository() < 0)
/* force to the mode value */
- xsnprintf(buf, sizeof(buf), "0%o", -shared_repository);
- else if (shared_repository == PERM_GROUP)
+ xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository());
+ else if (get_shared_repository() == PERM_GROUP)
xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP);
- else if (shared_repository == PERM_EVERYBODY)
+ else if (get_shared_repository() == PERM_EVERYBODY)
xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
else
die("BUG: invalid value for shared_repository");
@@ -399,7 +402,7 @@ int init_db(const char *template_dir, unsigned int flags)
"", and the last '%s%s' is the verbatim directory name. */
printf(_("%s%s Git repository in %s%s\n"),
reinit ? _("Reinitialized existing") : _("Initialized empty"),
- shared_repository ? _(" shared") : "",
+ get_shared_repository() ? _(" shared") : "",
git_dir, len && git_dir[len-1] != '/' ? "/" : "");
}
@@ -494,8 +497,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
* and we know shared_repository should always be 0;
* but just in case we play safe.
*/
- saved = shared_repository;
- shared_repository = 0;
+ saved = get_shared_repository();
+ set_shared_repository(0);
switch (safe_create_leading_directories_const(argv[0])) {
case SCLD_OK:
case SCLD_PERMS:
@@ -507,7 +510,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
die_errno(_("cannot mkdir %s"), argv[0]);
break;
}
- shared_repository = saved;
+ set_shared_repository(saved);
if (mkdir(argv[0], 0777) < 0)
die_errno(_("cannot mkdir %s"), argv[0]);
mkdir_tried = 1;
@@ -525,7 +528,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
}
if (init_shared_repository != -1)
- shared_repository = init_shared_repository;
+ set_shared_repository(init_shared_repository);
/*
* GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
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/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/pull.c b/builtin/pull.c
index 10eff03967..596b92fc56 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)
@@ -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/replace.c b/builtin/replace.c
index 748c6ca954..b58c714cb8 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -440,6 +440,7 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
};
check_replace_refs = 0;
+ git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 5b9dd6a9d8..1ff5a67538 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -225,7 +225,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
* --all and --mirror are incompatible; neither makes sense
* with any refspecs.
*/
- if ((refspecs && (send_all || args.send_mirror)) ||
+ if ((nr_refspecs > 0 && (send_all || args.send_mirror)) ||
(send_all && args.send_mirror))
usage_with_options(send_pack_usage, options);
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 5295b727d4..3bd6883eff 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -118,6 +118,55 @@ static int module_name(int argc, const char **argv, const char *prefix)
return 0;
}
+
+/*
+ * Rules to sanitize configuration variables that are Ok to be passed into
+ * submodule operations from the parent project using "-c". Should only
+ * include keys which are both (a) safe and (b) necessary for proper
+ * operation.
+ */
+static int submodule_config_ok(const char *var)
+{
+ if (starts_with(var, "credential."))
+ return 1;
+ return 0;
+}
+
+static int sanitize_submodule_config(const char *var, const char *value, void *data)
+{
+ struct strbuf *out = data;
+
+ if (submodule_config_ok(var)) {
+ if (out->len)
+ strbuf_addch(out, ' ');
+
+ if (value)
+ sq_quotef(out, "%s=%s", var, value);
+ else
+ sq_quote_buf(out, var);
+ }
+
+ return 0;
+}
+
+static 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)) {
+ struct strbuf sanitized_config = STRBUF_INIT;
+ git_config_from_parameters(sanitize_submodule_config,
+ &sanitized_config);
+ argv_array_pushf(out, "%s=%s", *var, sanitized_config.buf);
+ strbuf_release(&sanitized_config);
+ } else {
+ argv_array_push(out, *var);
+ }
+ }
+
+}
+
static int clone_submodule(const char *path, const char *gitdir, const char *url,
const char *depth, const char *reference, int quiet)
{
@@ -139,7 +188,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);
@@ -147,11 +196,11 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url
static int module_clone(int argc, const char **argv, const char *prefix)
{
- const char *path = NULL, *name = NULL, *url = NULL;
+ const char *name = NULL, *url = NULL;
const char *reference = NULL, *depth = NULL;
int quiet = 0;
FILE *submodule_dot_git;
- char *sm_gitdir, *cwd, *p;
+ char *p, *path = NULL, *sm_gitdir;
struct strbuf rel_path = STRBUF_INIT;
struct strbuf sb = STRBUF_INIT;
@@ -180,16 +229,27 @@ 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 (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 = strbuf_detach(&sb, NULL);
+ sm_gitdir = xstrdup(absolute_path(sb.buf));
+ strbuf_reset(&sb);
+
+ if (!is_absolute_path(path)) {
+ strbuf_addf(&sb, "%s/%s", get_git_work_tree(), path);
+ path = strbuf_detach(&sb, NULL);
+ } else
+ path = xstrdup(path);
if (!file_exists(sm_gitdir)) {
if (safe_create_leading_directories_const(sm_gitdir) < 0)
@@ -206,49 +266,301 @@ static int module_clone(int argc, const char **argv, const char *prefix)
}
/* Write a .git file in the submodule to redirect to the superproject. */
- if (safe_create_leading_directories_const(path) < 0)
- die(_("could not create directory '%s'"), path);
-
- if (path && *path)
- strbuf_addf(&sb, "%s/.git", path);
- else
- strbuf_addstr(&sb, ".git");
-
+ strbuf_addf(&sb, "%s/.git", path);
if (safe_create_leading_directories_const(sb.buf) < 0)
die(_("could not create leading directories of '%s'"), sb.buf);
submodule_dot_git = fopen(sb.buf, "w");
if (!submodule_dot_git)
die_errno(_("cannot open file '%s'"), sb.buf);
- fprintf(submodule_dot_git, "gitdir: %s\n",
- relative_path(sm_gitdir, path, &rel_path));
+ fprintf_or_die(submodule_dot_git, "gitdir: %s\n",
+ relative_path(sm_gitdir, path, &rel_path));
if (fclose(submodule_dot_git))
die(_("could not close file %s"), sb.buf);
strbuf_reset(&sb);
strbuf_reset(&rel_path);
- cwd = xgetcwd();
/* Redirect the worktree of the submodule in the superproject's config */
- if (!is_absolute_path(sm_gitdir)) {
- strbuf_addf(&sb, "%s/%s", cwd, sm_gitdir);
- free(sm_gitdir);
- sm_gitdir = strbuf_detach(&sb, NULL);
- }
-
- strbuf_addf(&sb, "%s/%s", cwd, path);
p = git_pathdup_submodule(path, "config");
if (!p)
die(_("could not get submodule directory for '%s'"), path);
git_config_set_in_file(p, "core.worktree",
- relative_path(sb.buf, sm_gitdir, &rel_path));
+ relative_path(path, sm_gitdir, &rel_path));
strbuf_release(&sb);
strbuf_release(&rel_path);
free(sm_gitdir);
- free(cwd);
+ free(path);
free(p);
return 0;
}
+static int module_sanitize_config(int argc, const char **argv, const char *prefix)
+{
+ struct strbuf sanitized_config = STRBUF_INIT;
+
+ if (argc > 1)
+ usage(_("git submodule--helper sanitize-config"));
+
+ git_config_from_parameters(sanitize_submodule_config, &sanitized_config);
+ if (sanitized_config.len)
+ printf("%s\n", sanitized_config.buf);
+
+ strbuf_release(&sanitized_config);
+
+ 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}
+
+/**
+ * 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 (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) {
+ /*
+ * Only mention uninitialized submodules when their
+ * path 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');
+ }
+ 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 *);
@@ -258,19 +570,21 @@ static struct cmd_struct commands[] = {
{"list", module_list},
{"name", module_name},
{"clone", module_clone},
+ {"sanitize-config", module_sanitize_config},
+ {"update-clone", update_clone}
};
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/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..d8e3795dc4 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;
};
@@ -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.h b/cache.h
index 9f09540bbc..fd728f0793 100644
--- a/cache.h
+++ b/cache.h
@@ -651,7 +651,6 @@ extern int prefer_symlink_refs;
extern int log_all_ref_updates;
extern int warn_ambiguous_refs;
extern int warn_on_object_refname_ambiguity;
-extern int shared_repository;
extern const char *apply_default_whitespace;
extern const char *apply_default_ignorewhitespace;
extern const char *git_attributes_file;
@@ -664,6 +663,9 @@ extern size_t delta_base_cache_limit;
extern unsigned long big_file_threshold;
extern unsigned long pack_size_limit_cfg;
+void set_shared_repository(int value);
+int get_shared_repository(void);
+
/*
* Do replace refs need to be checked this run? This variable is
* initialized to true unless --no-replace-object is used or
@@ -745,9 +747,39 @@ extern int grafts_replace_parents;
*/
#define GIT_REPO_VERSION 0
#define GIT_REPO_VERSION_READ 1
-extern int repository_format_version;
extern int repository_format_precious_objects;
-extern int check_repository_format(void);
+
+struct repository_format {
+ int version;
+ int precious_objects;
+ int is_bare;
+ char *work_tree;
+ struct string_list unknown_extensions;
+};
+
+/*
+ * Read the repository format characteristics from the config file "path" into
+ * "format" struct. Returns the numeric version. On error, -1 is returned,
+ * format->version is set to -1, and all other fields in the struct are
+ * undefined.
+ */
+int read_repository_format(struct repository_format *format, const char *path);
+
+/*
+ * Verify that the repository described by repository_format is something we
+ * can read. If it is, return 0. Otherwise, return -1, and "err" will describe
+ * any errors encountered.
+ */
+int verify_repository_format(const struct repository_format *format,
+ struct strbuf *err);
+
+/*
+ * Check the repository format version in the path found in get_git_dir(),
+ * and die if it is a version we don't understand. Generally one would
+ * set_git_dir() before calling this, and use it only for "are we in a valid
+ * repo?".
+ */
+extern void check_repository_format(void);
#define MTIME_CHANGED 0x0001
#define CTIME_CHANGED 0x0002
@@ -926,8 +958,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);
@@ -1526,7 +1556,6 @@ extern void git_config(config_fn_t fn, void *);
extern int git_config_with_options(config_fn_t fn, void *,
struct git_config_source *config_source,
int respect_includes);
-extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
extern int git_parse_ulong(const char *, unsigned long *);
extern int git_parse_maybe_bool(const char *);
extern int git_config_int(const char *, const char *);
@@ -1550,7 +1579,6 @@ extern void git_config_set_multivar_in_file(const char *, const char *, const ch
extern int git_config_rename_section(const char *, const char *);
extern int git_config_rename_section_in_file(const char *, const char *, const char *);
extern const char *git_etc_gitconfig(void);
-extern int check_repository_format_version(const char *var, const char *value, void *cb);
extern int git_env_bool(const char *, int);
extern unsigned long git_env_ulong(const char *, unsigned long);
extern int git_config_system(void);
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/apple-common-crypto.h b/compat/apple-common-crypto.h
index d3fb264181..11727f3e1e 100644
--- a/compat/apple-common-crypto.h
+++ b/compat/apple-common-crypto.h
@@ -3,12 +3,18 @@
#define HEADER_HMAC_H
#define HEADER_SHA_H
#include <CommonCrypto/CommonHMAC.h>
-#define HMAC_CTX CCHmacContext
-#define HMAC_Init(hmac, key, len, algo) CCHmacInit(hmac, algo, key, len)
-#define HMAC_Update CCHmacUpdate
-#define HMAC_Final(hmac, hash, ptr) CCHmacFinal(hmac, hash)
-#define HMAC_CTX_cleanup(ignore)
#define EVP_md5(...) kCCHmacAlgMD5
+/* CCHmac doesn't take md_len and the return type is void */
+#define HMAC git_CC_HMAC
+static inline unsigned char *git_CC_HMAC(CCHmacAlgorithm alg,
+ const void *key, int key_len,
+ const unsigned char *data, size_t data_len,
+ unsigned char *md, unsigned int *md_len)
+{
+ CCHmac(alg, key, key_len, data, data_len, md);
+ return md;
+}
+
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
#define APPLE_LION_OR_NEWER
#include <Security/Security.h>
diff --git a/compat/mingw.c b/compat/mingw.c
index 54c82ecf20..0413d5c3cd 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -763,15 +763,12 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
char *mingw_getcwd(char *pointer, int len)
{
- int i;
wchar_t wpointer[MAX_PATH];
if (!_wgetcwd(wpointer, ARRAY_SIZE(wpointer)))
return NULL;
if (xwcstoutf(pointer, wpointer, len) < 0)
return NULL;
- for (i = 0; pointer[i]; i++)
- if (pointer[i] == '\\')
- pointer[i] = '/';
+ convert_slashes(pointer);
return pointer;
}
@@ -2112,9 +2109,7 @@ static void setup_windows_environment()
* executable (by not mistaking the dir separators
* for escape characters).
*/
- for (; *tmp; tmp++)
- if (*tmp == '\\')
- *tmp = '/';
+ convert_slashes(tmp);
}
/* simulate TERM to enable auto-color (see color.c) */
diff --git a/compat/mingw.h b/compat/mingw.h
index c008694639..1de70ffd62 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -406,7 +406,7 @@ static inline void convert_slashes(char *path)
int mingw_offset_1st_component(const char *path);
#define offset_1st_component mingw_offset_1st_component
#define PATH_SEP ';'
-#ifndef __MINGW64_VERSION_MAJOR
+#if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
#define PRIuMAX "I64u"
#define PRId64 "I64d"
#else
diff --git a/compat/snprintf.c b/compat/snprintf.c
index 42ea1ac110..0b11688537 100644
--- a/compat/snprintf.c
+++ b/compat/snprintf.c
@@ -9,7 +9,7 @@
* always have room for a trailing NUL byte.
*/
#ifndef SNPRINTF_SIZE_CORR
-#if defined(WIN32) && (!defined(__GNUC__) || __GNUC__ < 4)
+#if defined(WIN32) && (!defined(__GNUC__) || __GNUC__ < 4) && (!defined(_MSC_VER) || _MSC_VER < 1900)
#define SNPRINTF_SIZE_CORR 1
#else
#define SNPRINTF_SIZE_CORR 0
diff --git a/compat/vcbuild/include/unistd.h b/compat/vcbuild/include/unistd.h
index c65c2cd566..3a959d124c 100644
--- a/compat/vcbuild/include/unistd.h
+++ b/compat/vcbuild/include/unistd.h
@@ -45,11 +45,15 @@ typedef unsigned long long uintmax_t;
typedef int64_t off64_t;
+#if !defined(_MSC_VER) || _MSC_VER < 1600
#define INTMAX_MIN _I64_MIN
#define INTMAX_MAX _I64_MAX
#define UINTMAX_MAX _UI64_MAX
#define UINT32_MAX 0xffffffff /* 4294967295U */
+#else
+#include <stdint.h>
+#endif
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
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 9ba40bc1b0..10b5c957ae 100644
--- a/config.c
+++ b/config.c
@@ -108,7 +108,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc
expanded = expand_user_path(path);
if (!expanded)
- return error("Could not expand include path '%s'", path);
+ return error("could not expand include path '%s'", path);
path = expanded;
/*
@@ -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, ' ');
}
@@ -950,7 +950,7 @@ static int git_default_branch_config(const char *var, const char *value)
else if (!strcmp(value, "always"))
autorebase = AUTOREBASE_ALWAYS;
else
- return error("Malformed value for %s", var);
+ return error("malformed value for %s", var);
return 0;
}
@@ -976,7 +976,7 @@ static int git_default_push_config(const char *var, const char *value)
else if (!strcmp(value, "current"))
push_default = PUSH_DEFAULT_CURRENT;
else {
- error("Malformed value for %s: %s", var, value);
+ error("malformed value for %s: %s", var, value);
return error("Must be one of nothing, matching, simple, "
"upstream or current.");
}
@@ -1188,11 +1188,12 @@ int git_config_system(void)
return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
}
-int git_config_early(config_fn_t fn, void *data, const char *repo_config)
+static int do_git_config_sequence(config_fn_t fn, void *data)
{
int ret = 0, found = 0;
char *xdg_config = xdg_config_home("config");
char *user_config = expand_user_path("~/.gitconfig");
+ char *repo_config = git_pathdup("config");
if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) {
ret += git_config_from_file(fn, git_etc_gitconfig(),
@@ -1228,6 +1229,7 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
free(xdg_config);
free(user_config);
+ free(repo_config);
return ret == 0 ? found : ret;
}
@@ -1235,8 +1237,6 @@ int git_config_with_options(config_fn_t fn, void *data,
struct git_config_source *config_source,
int respect_includes)
{
- char *repo_config = NULL;
- int ret;
struct config_include_data inc = CONFIG_INCLUDE_INIT;
if (respect_includes) {
@@ -1257,11 +1257,7 @@ int git_config_with_options(config_fn_t fn, void *data,
else if (config_source && config_source->blob)
return git_config_from_blob_ref(fn, config_source->blob, data);
- repo_config = git_pathdup("config");
- ret = git_config_early(fn, data, repo_config);
- if (repo_config)
- free(repo_config);
- return ret;
+ return do_git_config_sequence(fn, data);
}
static void git_config_raw(config_fn_t fn, void *data)
@@ -2221,9 +2217,13 @@ void git_config_set_multivar_in_file(const char *config_filename,
const char *key, const char *value,
const char *value_regex, int multi_replace)
{
- if (git_config_set_multivar_in_file_gently(config_filename, key, value,
- value_regex, multi_replace) < 0)
- die(_("Could not set '%s' to '%s'"), key, value);
+ if (!git_config_set_multivar_in_file_gently(config_filename, key, value,
+ value_regex, multi_replace))
+ return;
+ if (value)
+ die(_("could not set '%s' to '%s'"), key, value);
+ else
+ die(_("could not unset '%s'"), key);
}
int git_config_set_multivar_gently(const char *key, const char *value,
@@ -2404,7 +2404,7 @@ int git_config_rename_section(const char *old_name, const char *new_name)
#undef config_error_nonbool
int config_error_nonbool(const char *var)
{
- return error("Missing value for '%s'", var);
+ return error("missing value for '%s'", var);
}
int parse_config_key(const char *var,
diff --git a/config.mak.uname b/config.mak.uname
index fe8096f8a6..40d6b29eee 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -187,6 +187,7 @@ ifeq ($(uname_O),Cygwin)
X = .exe
UNRELIABLE_FSTAT = UnfortunatelyYes
SPARSE_FLAGS = -isystem /usr/include/w32api -Wno-one-bit-signed-bitfield
+ OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
endif
ifeq ($(uname_S),FreeBSD)
NEEDS_LIBICONV = YesPlease
diff --git a/configure.ac b/configure.ac
index 0cd9f4680b..c279025747 100644
--- a/configure.ac
+++ b/configure.ac
@@ -970,10 +970,6 @@ AC_CHECK_LIB([iconv], [locale_charset],
[CHARSET_LIB=-lcharset])])
GIT_CONF_SUBST([CHARSET_LIB])
#
-# Define NO_HMAC_CTX_CLEANUP=YesPlease if HMAC_CTX_cleanup is missing.
-AC_CHECK_LIB([crypto], [HMAC_CTX_cleanup],
- [], [GIT_CONF_SUBST([NO_HMAC_CTX_CLEANUP], [YesPlease])])
-#
# Define HAVE_CLOCK_GETTIME=YesPlease if clock_gettime is available.
GIT_CHECK_FUNC(clock_gettime,
[HAVE_CLOCK_GETTIME=YesPlease],
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/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.c b/diff.c
index 059123c5dc..4dfe6609d0 100644
--- a/diff.c
+++ b/diff.c
@@ -168,6 +168,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")) {
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/diffcore-rename.c b/diffcore-rename.c
index 3b3c1ed535..7f03eb5a04 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -340,9 +340,11 @@ static int find_exact_renames(struct diff_options *options)
int i, renames = 0;
struct hashmap file_table;
- /* Add all sources to the hash table */
+ /* Add all sources to the hash table in reverse order, because
+ * later on they will be retrieved in LIFO order.
+ */
hashmap_init(&file_table, NULL, rename_src_nr);
- for (i = 0; i < rename_src_nr; i++)
+ for (i = rename_src_nr-1; i >= 0; i--)
insert_file_table(&file_table, i, rename_src[i].p->one);
/* Walk the destinations and find best source match */
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/environment.c b/environment.c
index 6cc0a7780f..57acb2fe2a 100644
--- a/environment.c
+++ b/environment.c
@@ -25,11 +25,9 @@ int log_all_ref_updates = -1; /* unspecified */
int warn_ambiguous_refs = 1;
int warn_on_object_refname_ambiguity = 1;
int ref_paranoia = -1;
-int repository_format_version;
int repository_format_precious_objects;
const char *git_commit_encoding;
const char *git_log_output_encoding;
-int shared_repository = PERM_UMASK;
const char *apply_default_whitespace;
const char *apply_default_ignorewhitespace;
const char *git_attributes_file;
@@ -324,3 +322,24 @@ const char *get_commit_output_encoding(void)
{
return git_commit_encoding ? git_commit_encoding : "UTF-8";
}
+
+static int the_shared_repository = PERM_UMASK;
+static int need_shared_repository_from_config = 1;
+
+void set_shared_repository(int value)
+{
+ the_shared_repository = value;
+ need_shared_repository_from_config = 0;
+}
+
+int get_shared_repository(void)
+{
+ if (need_shared_repository_from_config) {
+ const char *var = "core.sharedrepository";
+ const char *value;
+ if (!git_config_get_value(var, &value))
+ the_shared_repository = git_config_perm(var, value);
+ need_shared_repository_from_config = 0;
+ }
+ return the_shared_repository;
+}
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/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 474395471f..1f8b5f3b1f 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -279,9 +279,6 @@ extern char *gitdirname(char *);
#endif
#include <openssl/ssl.h>
#include <openssl/err.h>
-#ifdef NO_HMAC_CTX_CLEANUP
-#define HMAC_CTX_cleanup HMAC_cleanup
-#endif
#endif
/* On most systems <netdb.h> would have given us this, but
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..302c56de5b 100644
--- a/git-mergetool--lib.sh
+++ b/git-mergetool--lib.sh
@@ -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-p4.py b/git-p4.py
index 825b9f32d5..8f869d74a1 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -1160,6 +1160,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 +2320,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 +2674,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 +2702,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-rebase--interactive.sh b/git-rebase--interactive.sh
index 4cde685b43..9ea30756f1 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -548,7 +548,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.sh b/git-rebase.sh
index cf60c43908..0bf41ee72b 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -248,6 +248,7 @@ do
;;
--exec=*)
cmd="${cmd}exec ${1#--exec=}${LF}"
+ test -z "$interactive_rebase" && interactive_rebase=implied
;;
--interactive)
interactive_rebase=explicit
@@ -348,12 +349,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 c45b22a19a..1406f64d78 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -19,10 +19,10 @@
use 5.008;
use strict;
use warnings;
+use POSIX qw/strftime/;
use Term::ReadLine;
use Getopt::Long;
use Text::ParseWords;
-use Data::Dumper;
use Term::ANSIColor;
use File::Temp qw/ tempdir tempfile /;
use File::Spec::Functions qw(catfile);
@@ -949,7 +949,7 @@ my ($message_id_stamp, $message_id_serial);
sub make_message_id {
my $uniq;
if (!defined $message_id_stamp) {
- $message_id_stamp = sprintf("%s-%s", time, $$);
+ $message_id_stamp = strftime("%Y%m%d%H%M%S.$$", gmtime(time));
$message_id_serial = 0;
}
$message_id_serial++;
@@ -964,7 +964,7 @@ sub make_message_id {
require Sys::Hostname;
$du_part = 'user@' . Sys::Hostname::hostname();
}
- my $message_id_template = "<%s-git-send-email-%s>";
+ my $message_id_template = "<%s-%s>";
$message_id = sprintf($message_id_template, $uniq, $du_part);
#print "new message id = $message_id\n"; # Was useful for debugging
}
diff --git a/git-submodule.sh b/git-submodule.sh
index 43c68deee9..2a84d7e66a 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -192,6 +192,16 @@ 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()
+{
+ sanitized_config=$(git submodule--helper sanitize-config)
+ clear_local_git_env
+ GIT_CONFIG_PARAMETERS=$sanitized_config
+}
+
#
# Add a new submodule to the working tree, .gitmodules and the index
#
@@ -347,9 +357,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
@@ -413,12 +423,12 @@ cmd_foreach()
die_if_unmatched "$mode"
if test -e "$sm_path"/.git
then
- displaypath=$(relative_path "$sm_path")
- say "$(eval_gettext "Entering '\$prefix\$displaypath'")"
+ displaypath=$(relative_path "$prefix$sm_path")
+ say "$(eval_gettext "Entering '\$displaypath'")"
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 ...
@@ -434,7 +444,7 @@ cmd_foreach()
cmd_foreach "--recursive" "$@"
fi
) <&3 3<&- ||
- die "$(eval_gettext "Stopping at '\$prefix\$displaypath'; script returned non-zero status.")"
+ die "$(eval_gettext "Stopping at '\$displaypath'; script returned non-zero status.")"
fi
done
}
@@ -473,7 +483,7 @@ cmd_init()
die_if_unmatched "$mode"
name=$(git submodule--helper name "$sm_path") || exit
- displaypath=$(relative_path "$sm_path")
+ displaypath=$(relative_path "$prefix$sm_path")
# Copy url setting when it is not set yet
if test -z "$(git config "submodule.$name.url")"
@@ -592,14 +602,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 +673,14 @@ cmd_update()
--depth=*)
depth=$1
;;
+ -j|--jobs)
+ case "$2" in '') usage ;; esac
+ jobs="--jobs=$2"
+ shift
+ ;;
+ --jobs=*)
+ jobs=$1
+ ;;
--)
shift
break
@@ -682,17 +700,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 +731,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"
+ if test $just_cloned -eq 1
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
- 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 +746,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 +779,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)
@@ -802,15 +800,15 @@ Maybe you want to use 'update --init'?")"
;;
!*)
command="${update_module#!}"
- die_msg="$(eval_gettext "Execution of '\$command \$sha1' failed in submodule path '\$prefix\$sm_path'")"
- say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': '\$command \$sha1'")"
+ die_msg="$(eval_gettext "Execution of '\$command \$sha1' failed in submodule path '\$displaypath'")"
+ say_msg="$(eval_gettext "Submodule path '\$displaypath': '\$command \$sha1'")"
must_die_on_failure=yes
;;
*)
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"
@@ -826,7 +824,7 @@ Maybe you want to use 'update --init'?")"
then
(
prefix="$prefix$sm_path/"
- clear_local_git_env
+ sanitize_submodule_env
cd "$sm_path" &&
eval cmd_update
)
@@ -864,7 +862,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 +1146,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 +1156,8 @@ cmd_status()
then
(
prefix="$displaypath/"
- clear_local_git_env
+ sanitize_submodule_env
+ wt_prefix=
cd "$sm_path" &&
eval cmd_status
) ||
@@ -1232,7 +1231,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/gpg-interface.c b/gpg-interface.c
index 3dc2fe397e..2259938236 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -237,6 +237,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 +251,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/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.c b/http.c
index 69da4454d8..4304b80ad3 100644
--- a/http.c
+++ b/http.c
@@ -605,7 +605,10 @@ static CURL *get_curl_handle(void)
if (curl_http_proxy) {
curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
#if LIBCURL_VERSION_NUM >= 0x071800
- if (starts_with(curl_http_proxy, "socks5"))
+ if (starts_with(curl_http_proxy, "socks5h"))
+ curl_easy_setopt(result,
+ CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);
+ else if (starts_with(curl_http_proxy, "socks5"))
curl_easy_setopt(result,
CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
else if (starts_with(curl_http_proxy, "socks4a"))
diff --git a/ident.c b/ident.c
index 6e125821f0..4fd82d1043 100644
--- a/ident.c
+++ b/ident.c
@@ -351,15 +351,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 +376,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/imap-send.c b/imap-send.c
index 2c52027c84..938c691585 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -287,17 +287,20 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
SSL_library_init();
SSL_load_error_strings();
- if (use_tls_only)
- meth = TLSv1_method();
- else
- meth = SSLv23_method();
-
+ meth = SSLv23_method();
if (!meth) {
ssl_socket_perror("SSLv23_method");
return -1;
}
ctx = SSL_CTX_new(meth);
+ if (!ctx) {
+ ssl_socket_perror("SSL_CTX_new");
+ return -1;
+ }
+
+ if (use_tls_only)
+ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
if (verify)
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
@@ -862,7 +865,6 @@ static char hexchar(unsigned int b)
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
- HMAC_CTX hmac;
unsigned char hash[16];
char hex[33];
char *response, *response_64, *challenge;
@@ -877,10 +879,8 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
(unsigned char *)challenge_64, encoded_len);
if (decoded_len < 0)
die("invalid challenge %s", challenge_64);
- HMAC_Init(&hmac, (unsigned char *)pass, strlen(pass), EVP_md5());
- HMAC_Update(&hmac, (unsigned char *)challenge, decoded_len);
- HMAC_Final(&hmac, hash, NULL);
- HMAC_CTX_cleanup(&hmac);
+ if (!HMAC(EVP_md5(), pass, strlen(pass), (unsigned char *)challenge, decoded_len, hash, NULL))
+ die("HMAC error");
hex[32] = 0;
for (i = 0; i < 16; i++) {
@@ -890,7 +890,7 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
/* response: "<user> <digest in hex>" */
response = xstrfmt("%s %s", user, hex);
- resp_len = strlen(response) + 1;
+ resp_len = strlen(response);
response_64 = xmallocz(ENCODED_SIZE(resp_len));
encoded_len = EVP_EncodeBlock((unsigned char *)response_64,
@@ -1095,11 +1095,6 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, char *f
srvc->pass = xstrdup(cred.password);
}
- if (CAP(NOLOGIN)) {
- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host);
- goto bail;
- }
-
if (srvc->auth_method) {
struct imap_cmd_cb cb;
@@ -1123,6 +1118,11 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, char *f
goto bail;
}
} else {
+ if (CAP(NOLOGIN)) {
+ fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
+ srvc->user, srvc->host);
+ goto bail;
+ }
if (!imap->buf.sock.ssl)
imap_warn("*** IMAP Warning *** Password is being "
"sent in the clear\n");
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/merge-recursive.c b/merge-recursive.c
index b880ae50e7..06d31ed873 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -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/path.c b/path.c
index 969b494d72..bbaea5ab0b 100644
--- a/path.c
+++ b/path.c
@@ -702,17 +702,17 @@ static int calc_shared_perm(int mode)
{
int tweak;
- if (shared_repository < 0)
- tweak = -shared_repository;
+ if (get_shared_repository() < 0)
+ tweak = -get_shared_repository();
else
- tweak = shared_repository;
+ tweak = get_shared_repository();
if (!(mode & S_IWUSR))
tweak &= ~0222;
if (mode & S_IXUSR)
/* Copy read bits to execute bits */
tweak |= (tweak & 0444) >> 2;
- if (shared_repository < 0)
+ if (get_shared_repository() < 0)
mode = (mode & ~0777) | tweak;
else
mode |= tweak;
@@ -725,7 +725,7 @@ int adjust_shared_perm(const char *path)
{
int old_mode, new_mode;
- if (!shared_repository)
+ if (!get_shared_repository())
return 0;
if (get_st_mode_bits(path, &old_mode) < 0)
return -1;
diff --git a/po/fr.po b/po/fr.po
index 88b0b8a78a..55ca387ba4 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -2945,7 +2945,7 @@ msgstr "utiliser l'horodatage actuel pour la date d'auteur"
#: builtin/am.c:2321 builtin/commit.c:1593 builtin/merge.c:225
#: builtin/pull.c:159 builtin/revert.c:92 builtin/tag.c:355
msgid "key-id"
-msgstr "id de clé"
+msgstr "id-clé"
#: builtin/am.c:2322
msgid "GPG-sign commits"
@@ -4545,7 +4545,7 @@ msgstr "style"
#: builtin/checkout.c:1154
msgid "conflict style (merge or diff3)"
-msgstr "style de conflit (fusion ou diff3)"
+msgstr "style de conflit (merge (fusion) ou diff3)"
#: builtin/checkout.c:1157
msgid "do not limit pathspecs to sparse entries only"
@@ -6197,7 +6197,7 @@ msgstr "convertir en un dépôt complet"
#: builtin/fetch.c:122 builtin/log.c:1236
msgid "dir"
-msgstr "dir"
+msgstr "répertoire"
#: builtin/fetch.c:123
msgid "prepend this to submodule path output"
@@ -10809,11 +10809,11 @@ msgstr "git show-ref --exclude-existing[=<motif>]"
#: builtin/show-ref.c:165
msgid "only show tags (can be combined with heads)"
-msgstr "afficher seulement les étiquettes (peut être combiné avec des têtes)"
+msgstr "afficher seulement les étiquettes (peut être combiné avec heads)"
#: builtin/show-ref.c:166
msgid "only show heads (can be combined with tags)"
-msgstr "afficher seulement les têtes (peut être combiné avec des étiquettes)"
+msgstr "afficher seulement les têtes (peut être combiné avec tags)"
#: builtin/show-ref.c:167
msgid "stricter reference checking, requires exact ref path"
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/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.h b/refs.h
index 2f3decb432..9230d47142 100644
--- a/refs.h
+++ b/refs.h
@@ -306,6 +306,15 @@ extern int rename_ref(const char *oldref, const char *newref, const char *logmsg
extern int create_symref(const char *refname, const char *target, const char *logmsg);
+/*
+ * Update HEAD of the specified gitdir.
+ * Similar to create_symref("relative-git-dir/HEAD", target, NULL), but
+ * this can update the main working tree's HEAD regardless of where
+ * $GIT_DIR points to.
+ * Return 0 if successful, non-zero otherwise.
+ * */
+extern int set_worktree_head_symref(const char *gitdir, const char *target);
+
enum action_on_err {
UPDATE_REFS_MSG_ON_ERR,
UPDATE_REFS_DIE_ON_ERR,
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 81f68f846b..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 */
@@ -2894,6 +2763,42 @@ int create_symref(const char *refname, const char *target, const char *logmsg)
return ret;
}
+int set_worktree_head_symref(const char *gitdir, const char *target)
+{
+ static struct lock_file head_lock;
+ struct ref_lock *lock;
+ struct strbuf head_path = STRBUF_INIT;
+ const char *head_rel;
+ int ret;
+
+ strbuf_addf(&head_path, "%s/HEAD", absolute_path(gitdir));
+ if (hold_lock_file_for_update(&head_lock, head_path.buf,
+ LOCK_NO_DEREF) < 0) {
+ struct strbuf err = STRBUF_INIT;
+ unable_to_lock_message(head_path.buf, errno, &err);
+ error("%s", err.buf);
+ strbuf_release(&err);
+ strbuf_release(&head_path);
+ return -1;
+ }
+
+ /* head_rel will be "HEAD" for the main tree, "worktrees/wt/HEAD" for
+ linked trees */
+ head_rel = remove_leading_path(head_path.buf,
+ absolute_path(get_git_common_dir()));
+ /* to make use of create_symref_locked(), initialize ref_lock */
+ lock = xcalloc(1, sizeof(struct ref_lock));
+ lock->lk = &head_lock;
+ lock->ref_name = xstrdup(head_rel);
+ lock->orig_ref_name = xstrdup(head_rel);
+
+ ret = create_symref_locked(lock, head_rel, target, NULL);
+
+ unlock_ref(lock); /* will free lock */
+ strbuf_release(&head_path);
+ return ret;
+}
+
int reflog_exists(const char *refname)
{
struct stat st;
@@ -3445,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/rerere.c b/rerere.c
index 587b7e2717..c8b9f40787 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");
@@ -365,103 +515,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 +622,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 +662,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;
@@ -631,8 +701,6 @@ static int merge(const struct rerere_id *id, const char *path)
out:
free(cur.ptr);
- free(base.ptr);
- free(other.ptr);
free(result.ptr);
return ret;
@@ -661,6 +729,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 +747,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 +795,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 +850,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 +915,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,6 +1040,33 @@ 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
@@ -897,29 +1126,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 +1145,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 +1174,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 +1190,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 +1236,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..b683476b9c 100644
--- a/revision.c
+++ b/revision.c
@@ -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..e4593cd99b 100644
--- a/run-command.c
+++ b/run-command.c
@@ -590,6 +590,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;
@@ -902,7 +912,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 +920,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 +1004,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 +1089,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 +1127,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/setup.c b/setup.c
index 3439ec6d81..c86bf5c9fa 100644
--- a/setup.c
+++ b/setup.c
@@ -5,7 +5,6 @@
static int inside_git_dir = -1;
static int inside_work_tree = -1;
static int work_tree_config_is_bogus;
-static struct string_list unknown_extensions = STRING_LIST_INIT_DUP;
static struct startup_info the_startup_info;
struct startup_info *startup_info = &the_startup_info;
@@ -103,7 +102,7 @@ char *prefix_path_gently(const char *prefix, int len,
return NULL;
}
} else {
- sanitized = xstrfmt("%.*s%s", len, prefix, path);
+ sanitized = xstrfmt("%.*s%s", len, len ? prefix : "", path);
if (remaining_prefix)
*remaining_prefix = len;
if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix)) {
@@ -373,14 +372,13 @@ void setup_work_tree(void)
initialized = 1;
}
-static int check_repo_format(const char *var, const char *value, void *cb)
+static int check_repo_format(const char *var, const char *value, void *vdata)
{
+ struct repository_format *data = vdata;
const char *ext;
if (strcmp(var, "core.repositoryformatversion") == 0)
- repository_format_version = git_config_int(var, value);
- else if (strcmp(var, "core.sharedrepository") == 0)
- shared_repository = git_config_perm(var, value);
+ data->version = git_config_int(var, value);
else if (skip_prefix(var, "extensions.", &ext)) {
/*
* record any known extensions here; otherwise,
@@ -390,9 +388,15 @@ static int check_repo_format(const char *var, const char *value, void *cb)
if (!strcmp(ext, "noop"))
;
else if (!strcmp(ext, "preciousobjects"))
- repository_format_precious_objects = git_config_bool(var, value);
+ data->precious_objects = git_config_bool(var, value);
else
- string_list_append(&unknown_extensions, ext);
+ string_list_append(&data->unknown_extensions, ext);
+ } else if (strcmp(var, "core.bare") == 0) {
+ data->is_bare = git_config_bool(var, value);
+ } else if (strcmp(var, "core.worktree") == 0) {
+ if (!value)
+ return config_error_nonbool(var);
+ data->work_tree = xstrdup(value);
}
return 0;
}
@@ -400,56 +404,84 @@ static int check_repo_format(const char *var, const char *value, void *cb)
static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
{
struct strbuf sb = STRBUF_INIT;
- const char *repo_config;
- config_fn_t fn;
- int ret = 0;
-
- string_list_clear(&unknown_extensions, 0);
+ struct strbuf err = STRBUF_INIT;
+ struct repository_format candidate;
+ int has_common;
- if (get_common_dir(&sb, gitdir))
- fn = check_repo_format;
- else
- fn = check_repository_format_version;
+ has_common = get_common_dir(&sb, gitdir);
strbuf_addstr(&sb, "/config");
- repo_config = sb.buf;
+ read_repository_format(&candidate, sb.buf);
+ strbuf_release(&sb);
/*
- * git_config() can't be used here because it calls git_pathdup()
- * to get $GIT_CONFIG/config. That call will make setup_git_env()
- * set git_dir to ".git".
- *
- * We are in gitdir setup, no git dir has been found useable yet.
- * Use a gentler version of git_config() to check if this repo
- * is a good one.
+ * For historical use of check_repository_format() in git-init,
+ * we treat a missing config as a silent "ok", even when nongit_ok
+ * is unset.
*/
- git_config_early(fn, NULL, repo_config);
- if (GIT_REPO_VERSION_READ < repository_format_version) {
- if (!nongit_ok)
- die ("Expected git repo version <= %d, found %d",
- GIT_REPO_VERSION_READ, repository_format_version);
- warning("Expected git repo version <= %d, found %d",
- GIT_REPO_VERSION_READ, repository_format_version);
- warning("Please upgrade Git");
- *nongit_ok = -1;
- ret = -1;
+ if (candidate.version < 0)
+ return 0;
+
+ if (verify_repository_format(&candidate, &err) < 0) {
+ if (nongit_ok) {
+ warning("%s", err.buf);
+ strbuf_release(&err);
+ *nongit_ok = -1;
+ return -1;
+ }
+ die("%s", err.buf);
}
- if (repository_format_version >= 1 && unknown_extensions.nr) {
+ repository_format_precious_objects = candidate.precious_objects;
+ string_list_clear(&candidate.unknown_extensions, 0);
+ if (!has_common) {
+ if (candidate.is_bare != -1) {
+ is_bare_repository_cfg = candidate.is_bare;
+ if (is_bare_repository_cfg == 1)
+ inside_work_tree = -1;
+ }
+ if (candidate.work_tree) {
+ free(git_work_tree_cfg);
+ git_work_tree_cfg = candidate.work_tree;
+ inside_work_tree = -1;
+ }
+ } else {
+ free(candidate.work_tree);
+ }
+
+ return 0;
+}
+
+int read_repository_format(struct repository_format *format, const char *path)
+{
+ memset(format, 0, sizeof(*format));
+ format->version = -1;
+ format->is_bare = -1;
+ string_list_init(&format->unknown_extensions, 1);
+ git_config_from_file(check_repo_format, path, format);
+ return format->version;
+}
+
+int verify_repository_format(const struct repository_format *format,
+ struct strbuf *err)
+{
+ if (GIT_REPO_VERSION_READ < format->version) {
+ strbuf_addf(err, _("Expected git repo version <= %d, found %d"),
+ GIT_REPO_VERSION_READ, format->version);
+ return -1;
+ }
+
+ if (format->version >= 1 && format->unknown_extensions.nr) {
int i;
- if (!nongit_ok)
- die("unknown repository extension: %s",
- unknown_extensions.items[0].string);
+ strbuf_addstr(err, _("unknown repository extensions found:"));
- for (i = 0; i < unknown_extensions.nr; i++)
- warning("unknown repository extension: %s",
- unknown_extensions.items[i].string);
- *nongit_ok = -1;
- ret = -1;
+ for (i = 0; i < format->unknown_extensions.nr; i++)
+ strbuf_addf(err, "\n\t%s",
+ format->unknown_extensions.items[i].string);
+ return -1;
}
- strbuf_release(&sb);
- return ret;
+ return 0;
}
/*
@@ -965,30 +997,10 @@ int git_config_perm(const char *var, const char *value)
return -(i & 0666);
}
-int check_repository_format_version(const char *var, const char *value, void *cb)
-{
- int ret = check_repo_format(var, value, cb);
- if (ret)
- return ret;
- if (strcmp(var, "core.bare") == 0) {
- is_bare_repository_cfg = git_config_bool(var, value);
- if (is_bare_repository_cfg == 1)
- inside_work_tree = -1;
- } else if (strcmp(var, "core.worktree") == 0) {
- if (!value)
- return config_error_nonbool(var);
- free(git_work_tree_cfg);
- git_work_tree_cfg = xstrdup(value);
- inside_work_tree = -1;
- }
- return 0;
-}
-
-int check_repository_format(void)
+void check_repository_format(void)
{
check_repository_format_gently(get_git_dir(), NULL);
startup_info->have_repository = 1;
- return 0;
}
/*
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/submodule-config.c b/submodule-config.c
index 92502b594d..8ac5031ade 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -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);
@@ -392,8 +405,7 @@ static const struct submodule *config_from(struct submodule_cache *cache,
struct hashmap_iter iter;
struct submodule_entry *entry;
- hashmap_iter_init(&cache->for_name, &iter);
- entry = hashmap_iter_next(&iter);
+ entry = hashmap_iter_first(&cache->for_name, &iter);
if (!entry)
return NULL;
return entry->config;
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..90825e17fa 100644
--- a/submodule.c
+++ b/submodule.c
@@ -15,6 +15,7 @@
#include "thread-utils.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 +170,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 +216,27 @@ 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;
+}
+
void handle_ignore_submodules_arg(struct diff_options *diffopt,
const char *arg)
{
@@ -750,6 +777,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,
@@ -1094,3 +1124,8 @@ 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;
+}
diff --git a/submodule.h b/submodule.h
index e06eaa5ebb..7ef3775184 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,8 @@ 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);
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 +59,6 @@ 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);
#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..4dad7095f1 100644
--- a/test-match-trees.c
+++ b/t/helper/test-match-trees.c
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..ce3536e4a0 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,7 +49,7 @@ 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() {
+time_in_seconds () {
python -c 'import time; print int(time.time())'
}
@@ -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,13 +195,13 @@ 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 &&
@@ -208,7 +217,7 @@ marshal_dump() {
#
# Construct a client with this list of View lines
#
-client_view() {
+client_view () {
(
cat <<-EOF &&
Client: $P4CLIENT
@@ -222,7 +231,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/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/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
index 8e22b03cdd..df3183ea1a 100755
--- a/t/t1020-subdirectory.sh
+++ b/t/t1020-subdirectory.sh
@@ -141,13 +141,13 @@ test_expect_success 'GIT_PREFIX for !alias' '
test_expect_success 'GIT_PREFIX for built-ins' '
# Use GIT_EXTERNAL_DIFF to test that the "diff" built-in
# receives the GIT_PREFIX variable.
- printf "dir/" >expect &&
- printf "#!/bin/sh\n" >diff &&
- printf "printf \"\$GIT_PREFIX\"" >>diff &&
- chmod +x diff &&
+ echo "dir/" >expect &&
+ write_script diff <<-\EOF &&
+ printf "%s\n" "$GIT_PREFIX"
+ EOF
(
cd dir &&
- printf "change" >two &&
+ echo "change" >two &&
GIT_EXTERNAL_DIFF=./diff git diff >../actual
git checkout -- two
) &&
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/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/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/t3200-branch.sh b/t/t3200-branch.sh
index a897248490..f3e3b6cf2e 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -126,7 +126,28 @@ test_expect_success 'git branch -M foo bar should fail when bar is checked out'
test_expect_success 'git branch -M baz bam should succeed when baz is checked out' '
git checkout -b baz &&
git branch bam &&
- git branch -M baz bam
+ git branch -M baz bam &&
+ test $(git rev-parse --abbrev-ref HEAD) = bam
+'
+
+test_expect_success 'git branch -M baz bam should succeed when baz is checked out as linked working tree' '
+ git checkout master &&
+ git worktree add -b baz bazdir &&
+ git worktree add -f bazdir2 baz &&
+ git branch -M baz bam &&
+ test $(git -C bazdir rev-parse --abbrev-ref HEAD) = bam &&
+ test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam
+'
+
+test_expect_success 'git branch -M baz bam should succeed within a worktree in which baz is checked out' '
+ git checkout -b baz &&
+ git worktree add -f bazdir3 baz &&
+ (
+ cd bazdir3 &&
+ git branch -M baz bam &&
+ test $(git rev-parse --abbrev-ref HEAD) = bam
+ ) &&
+ test $(git rev-parse --abbrev-ref HEAD) = bam
'
test_expect_success 'git branch -M master should work when master is checked out' '
@@ -403,6 +424,12 @@ test_expect_success 'test deleting branch without config' '
test_i18ncmp expect actual
'
+test_expect_success 'deleting currently checked out branch fails' '
+ git worktree add -b my7 my7 &&
+ test_must_fail git -C my7 branch -d my7 &&
+ test_must_fail git branch -d my7
+'
+
test_expect_success 'test --track without .fetch entries' '
git branch --track my8 &&
test "$(git config branch.my8.remote)" &&
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 4261403cf6..c6a3ccba1b 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -184,4 +184,16 @@ test_expect_success 'ambiguous branch/tag not marked' '
test_cmp expect actual
'
+test_expect_success 'local-branch symrefs shortened properly' '
+ git symbolic-ref refs/heads/ref-to-branch refs/heads/branch-one &&
+ git symbolic-ref refs/heads/ref-to-remote refs/remotes/origin/branch-one &&
+ cat >expect <<-\EOF &&
+ ref-to-branch -> branch-one
+ ref-to-remote -> refs/remotes/origin/branch-one
+ EOF
+ git branch >actual.raw &&
+ grep ref-to <actual.raw >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 544f9ad508..d96d0e4c9b 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -555,10 +555,9 @@ test_expect_success 'rebase a detached HEAD' '
test_expect_success 'rebase a commit violating pre-commit' '
mkdir -p .git/hooks &&
- PRE_COMMIT=.git/hooks/pre-commit &&
- echo "#!/bin/sh" > $PRE_COMMIT &&
- echo "test -z \"\$(git diff --cached --check)\"" >> $PRE_COMMIT &&
- chmod a+x $PRE_COMMIT &&
+ write_script .git/hooks/pre-commit <<-\EOF &&
+ test -z "$(git diff --cached --check)"
+ EOF
echo "monde! " >> file1 &&
test_tick &&
test_must_fail git commit -m doesnt-verify file1 &&
@@ -771,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 &&
@@ -780,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 &&
@@ -793,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 &&
@@ -807,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 &&
@@ -835,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 &&
@@ -850,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 &&
@@ -876,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/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index 2f327b7495..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 &&
@@ -77,6 +140,17 @@ test_expect_success 'favour same basenames even with minor differences' '
git show HEAD:path1 | sed "s/15/16/" > subdir/path1 &&
git status | test_i18ngrep "renamed: .*path1 -> subdir/path1"'
+test_expect_success 'two files with same basename and same content' '
+ git reset --hard &&
+ mkdir -p dir/A dir/B &&
+ cp path1 dir/A/file &&
+ cp path1 dir/B/file &&
+ git add dir &&
+ git commit -m 2 &&
+ git mv dir other-dir &&
+ git status | test_i18ngrep "renamed: .*dir/A/file -> other-dir/A/file"
+'
+
test_expect_success 'setup for many rename source candidates' '
git reset --hard &&
for i in 0 1 2 3 4 5 6 7 8 9;
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/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/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 04cea97f87..305ca7a930 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -128,6 +128,18 @@ test_expect_success 'denyNonFastforwards trumps --force' '
test "$victim_orig" = "$victim_head"
'
+test_expect_success 'send-pack --all sends all branches' '
+ # make sure we have at least 2 branches with different
+ # values, just to be thorough
+ git branch other-branch HEAD^ &&
+
+ git init --bare all.git &&
+ git send-pack --all all.git &&
+ git for-each-ref refs/heads >expect &&
+ git -C all.git for-each-ref refs/heads >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'push --all excludes remote-tracking hierarchy' '
mkdir parent &&
(
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/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/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh
index d75ef0ea2b..51c9669398 100755
--- a/t/t5532-fetch-proxy.sh
+++ b/t/t5532-fetch-proxy.sh
@@ -12,10 +12,8 @@ test_expect_success 'setup remote repo' '
)
'
-cat >proxy <<'EOF'
-#!/bin/sh
-echo >&2 "proxying for $*"
-cmd=$("$PERL_PATH" -e '
+test_expect_success 'setup proxy script' '
+ write_script proxy-get-cmd "$PERL_PATH" <<-\EOF &&
read(STDIN, $buf, 4);
my $n = hex($buf) - 4;
read(STDIN, $buf, $n);
@@ -23,11 +21,16 @@ cmd=$("$PERL_PATH" -e '
# drop absolute-path on repo name
$cmd =~ s{ /}{ };
print $cmd;
-')
-echo >&2 "Running '$cmd'"
-exec $cmd
-EOF
-chmod +x proxy
+ EOF
+
+ write_script proxy <<-\EOF
+ echo >&2 "proxying for $*"
+ cmd=$(./proxy-get-cmd)
+ echo >&2 "Running $cmd"
+ exec $cmd
+ EOF
+'
+
test_expect_success 'setup local repo' '
git remote add fake git://example.com/remote &&
git config core.gitproxy ./proxy
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index 64146352ae..48e2ab62da 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -91,6 +91,23 @@ test_expect_success 'configured username does not override URL' '
expect_askpass pass user@host
'
+test_expect_success 'cmdline credential config passes into 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"
+ ) &&
+ 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.$HTTP_URL.username=user@host" \
+ clone --recursive super super-clone &&
+ expect_askpass pass user@host
+'
+
test_expect_success 'fetch changes via http' '
echo content >>file &&
git commit -a -m two &&
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index c1efb8e445..150aeaf713 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 &&
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/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/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..70afb44271 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -5,11 +5,14 @@ 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_prepare_expect () {
+ if test_have_prereq GPG
+ then
+ cat
+ else
+ sed '/signed/d'
+ fi
+}
test_expect_success 'setup some history and refs' '
test_commit one &&
@@ -17,8 +20,13 @@ test_expect_success 'setup some history and refs' '
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 &&
+ if test_have_prereq GPG
+ then
+ git tag -s -m "A signed tag" signed-tag &&
+ git tag -s -m "Signed doubly" doubly-signed-tag signed-tag
+ fi &&
git checkout master &&
git update-ref refs/odd/spot master
'
@@ -34,8 +42,9 @@ test_expect_success 'filtering with --points-at' '
'
test_expect_success 'check signed tags with --points-at' '
- sed -e "s/Z$//" >expect <<-\EOF &&
+ test_prepare_expect <<-\EOF | sed -e "s/Z$//" >expect &&
refs/heads/side Z
+ refs/tags/annotated-tag four
refs/tags/four Z
refs/tags/signed-tag four
EOF
@@ -56,9 +65,11 @@ test_expect_success 'filtering with --merged' '
'
test_expect_success 'filtering with --no-merged' '
- cat >expect <<-\EOF &&
+ test_prepare_expect >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
@@ -67,11 +78,13 @@ test_expect_success 'filtering with --no-merged' '
'
test_expect_success 'filtering with --contains' '
- cat >expect <<-\EOF &&
+ test_prepare_expect >expect <<-\EOF &&
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
@@ -86,11 +99,13 @@ test_expect_success '%(color) must fail' '
'
test_expect_success 'left alignment is default' '
- cat >expect <<-\EOF &&
+ test_prepare_expect >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
@@ -102,11 +117,13 @@ test_expect_success 'left alignment is default' '
'
test_expect_success 'middle alignment' '
- cat >expect <<-\EOF &&
+ test_prepare_expect >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
@@ -118,11 +135,13 @@ test_expect_success 'middle alignment' '
'
test_expect_success 'right alignment' '
- cat >expect <<-\EOF &&
+ test_prepare_expect >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
@@ -133,11 +152,13 @@ test_expect_success 'right alignment' '
test_cmp expect actual
'
-cat >expect <<-\EOF
+test_prepare_expect >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
@@ -178,11 +199,13 @@ EOF
# Individual atoms inside %(align:...) and %(end) must not be quoted.
test_expect_success 'alignment with format quote' "
- cat >expect <<-\EOF &&
+ test_prepare_expect >expect <<-\EOF &&
|' '\''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| '\'' '|
@@ -194,11 +217,13 @@ test_expect_success 'alignment with format quote' "
"
test_expect_success 'nested alignment with quote formatting' "
- cat >expect <<-\EOF &&
+ test_prepare_expect >expect <<-\EOF &&
|' master '|
|' side '|
|' odd/spot '|
- |' double-tag '|
+ |' annotated-tag '|
+ |'doubly-annotated-tag '|
+ |'doubly-signed-tag '|
|' four '|
|' one '|
|' signed-tag '|
@@ -210,14 +235,16 @@ test_expect_success 'nested alignment with quote formatting' "
"
test_expect_success 'check `%(contents:lines=1)`' '
- cat >expect <<-\EOF &&
+ test_prepare_expect >expect <<-\EOF &&
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
@@ -226,11 +253,13 @@ test_expect_success 'check `%(contents:lines=1)`' '
'
test_expect_success 'check `%(contents:lines=0)`' '
- cat >expect <<-\EOF &&
+ test_prepare_expect >expect <<-\EOF &&
master |
side |
odd/spot |
- double-tag |
+ annotated-tag |
+ doubly-annotated-tag |
+ doubly-signed-tag |
four |
one |
signed-tag |
@@ -242,14 +271,16 @@ test_expect_success 'check `%(contents:lines=0)`' '
'
test_expect_success 'check `%(contents:lines=99999)`' '
- cat >expect <<-\EOF &&
+ test_prepare_expect >expect <<-\EOF &&
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/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index e1abd19230..f99f674ac7 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -462,7 +462,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 +480,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 &&
@@ -818,6 +818,47 @@ test_expect_success 'submodule add --name allows to replace a submodule with ano
)
'
+test_expect_success 'recursive relative submodules stay relative' '
+ test_when_finished "rm -rf super clone2 subsub sub3" &&
+ mkdir subsub &&
+ (
+ cd subsub &&
+ git init &&
+ >t &&
+ git add t &&
+ git commit -m "initial commit"
+ ) &&
+ mkdir sub3 &&
+ (
+ cd sub3 &&
+ git init &&
+ >t &&
+ git add t &&
+ git commit -m "initial commit" &&
+ git submodule add ../subsub dirdir/subsub &&
+ git commit -m "add submodule subsub"
+ ) &&
+ mkdir super &&
+ (
+ cd super &&
+ git init &&
+ >t &&
+ git add t &&
+ git commit -m "initial commit" &&
+ git submodule add ../sub3 &&
+ git commit -m "add submodule sub"
+ ) &&
+ git clone super clone2 &&
+ (
+ cd clone2 &&
+ git submodule update --init --recursive &&
+ echo "gitdir: ../.git/modules/sub3" >./sub3/.git_expect &&
+ echo "gitdir: ../../../.git/modules/sub3/modules/dirdir/subsub" >./sub3/dirdir/subsub/.git_expect
+ ) &&
+ test_cmp clone2/sub3/.git_expect clone2/sub3/.git &&
+ test_cmp clone2/sub3/dirdir/subsub/.git_expect clone2/sub3/dirdir/subsub/.git
+'
+
test_expect_success 'submodule add with an existing name fails unless forced' '
(
cd addtest2 &&
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 68ea31d693..fd741f506f 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -63,6 +63,10 @@ test_expect_success 'setup a submodule tree' '
git submodule add ../none none &&
test_tick &&
git commit -m "none"
+ ) &&
+ git clone . recursivesuper &&
+ ( cd recursivesuper
+ git submodule add ../super super
)
'
@@ -95,6 +99,35 @@ test_expect_success 'submodule update from subdirectory' '
)
'
+supersha1=$(git -C super rev-parse HEAD)
+mergingsha1=$(git -C super/merging rev-parse HEAD)
+nonesha1=$(git -C super/none rev-parse HEAD)
+rebasingsha1=$(git -C super/rebasing rev-parse HEAD)
+submodulesha1=$(git -C super/submodule rev-parse HEAD)
+pwd=$(pwd)
+
+cat <<EOF >expect
+Submodule path '../super': checked out '$supersha1'
+Submodule '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
+
+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
+ ) &&
+ test_cmp expect actual
+'
+
apos="'";
test_expect_success 'submodule update does not fetch already present commits' '
(cd submodule &&
@@ -311,16 +344,59 @@ test_expect_success 'submodule update - command in .git/config' '
)
'
+cat << EOF >expect
+Execution of 'false $submodulesha1' failed in submodule path 'submodule'
+EOF
+
test_expect_success 'submodule update - command in .git/config catches failure' '
(cd super &&
git config submodule.submodule.update "!false"
) &&
(cd super/submodule &&
- git reset --hard HEAD^
+ git reset --hard $submodulesha1^
) &&
(cd super &&
- test_must_fail git submodule update submodule
- )
+ test_must_fail git submodule update submodule 2>../actual
+ ) &&
+ test_cmp actual expect
+'
+
+cat << EOF >expect
+Execution of 'false $submodulesha1' failed in submodule path '../submodule'
+EOF
+
+test_expect_success 'submodule update - command in .git/config catches failure -- subdirectory' '
+ (cd super &&
+ git config submodule.submodule.update "!false"
+ ) &&
+ (cd super/submodule &&
+ git reset --hard $submodulesha1^
+ ) &&
+ (cd super &&
+ mkdir tmp && cd tmp &&
+ test_must_fail git submodule update ../submodule 2>../../actual
+ ) &&
+ test_cmp actual expect
+'
+
+cat << EOF >expect
+Execution of 'false $submodulesha1' failed in submodule path '../super/submodule'
+Failed to recurse into submodule path '../super'
+EOF
+
+test_expect_success 'recursive submodule update - command in .git/config catches failure -- subdirectory' '
+ (cd recursivesuper &&
+ git submodule update --remote super &&
+ git add super &&
+ git commit -m "update to latest to have more than one commit in submodules"
+ ) &&
+ git -C recursivesuper/super config submodule.submodule.update "!false" &&
+ git -C recursivesuper/super/submodule reset --hard $submodulesha1^ &&
+ (cd recursivesuper &&
+ mkdir -p tmp && cd tmp &&
+ test_must_fail git submodule update --recursive ../super 2>../../actual
+ ) &&
+ test_cmp actual expect
'
test_expect_success 'submodule init does not copy command into .git/config' '
@@ -774,4 +850,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/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
index 7ca10b8606..6ba5daf42e 100755
--- a/t/t7407-submodule-foreach.sh
+++ b/t/t7407-submodule-foreach.sh
@@ -178,6 +178,26 @@ test_expect_success 'test messages from "foreach --recursive"' '
'
cat > expect <<EOF
+Entering '../nested1'
+Entering '../nested1/nested2'
+Entering '../nested1/nested2/nested3'
+Entering '../nested1/nested2/nested3/submodule'
+Entering '../sub1'
+Entering '../sub2'
+Entering '../sub3'
+EOF
+
+test_expect_success 'test messages from "foreach --recursive" from subdirectory' '
+ (
+ cd clone2 &&
+ mkdir untracked &&
+ cd untracked &&
+ git submodule foreach --recursive >../../actual
+ ) &&
+ test_i18ncmp expect actual
+'
+
+cat > expect <<EOF
nested1-nested1
nested2-nested2
nested3-nested3
@@ -242,8 +262,12 @@ test_expect_success 'test "status --recursive"' '
test_cmp expect actual
'
-sed -e "/nested2 /s/.*/+$nested2sha1 nested1\/nested2 (file2~1)/;/sub[1-3]/d" < expect > expect2
-mv -f expect2 expect
+cat > expect <<EOF
+ $nested1sha1 nested1 (heads/master)
++$nested2sha1 nested1/nested2 (file2~1)
+ $nested3sha1 nested1/nested2/nested3 (heads/master)
+ $submodulesha1 nested1/nested2/nested3/submodule (heads/master)
+EOF
test_expect_success 'ensure "status --cached --recursive" preserves the --cached flag' '
(
@@ -257,6 +281,27 @@ test_expect_success 'ensure "status --cached --recursive" preserves the --cached
test_cmp expect actual
'
+nested2sha1=$(git -C clone3/nested1/nested2 rev-parse HEAD)
+
+cat > expect <<EOF
+ $nested1sha1 ../nested1 (heads/master)
++$nested2sha1 ../nested1/nested2 (file2)
+ $nested3sha1 ../nested1/nested2/nested3 (heads/master)
+ $submodulesha1 ../nested1/nested2/nested3/submodule (heads/master)
+ $sub1sha1 ../sub1 ($sub1sha1_short)
+ $sub2sha1 ../sub2 ($sub2sha1_short)
+ $sub3sha1 ../sub3 (heads/master)
+EOF
+
+test_expect_success 'test "status --recursive" from sub directory' '
+ (
+ cd clone3 &&
+ mkdir tmp && cd tmp &&
+ git submodule status --recursive > ../../actual
+ ) &&
+ test_cmp expect actual
+'
+
test_expect_success 'use "git clone --recursive" to checkout all submodules' '
git clone --recursive super clone4 &&
(
diff --git a/t/t7412-submodule--helper.sh b/t/t7412-submodule--helper.sh
new file mode 100755
index 0000000000..149d42864f
--- /dev/null
+++ b/t/t7412-submodule--helper.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# Copyright (c) 2016 Jacob Keller
+#
+
+test_description='Basic plumbing support of submodule--helper
+
+This test verifies the submodule--helper plumbing command used to implement
+git-submodule.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'sanitize-config clears configuration' '
+ git -c user.name="Some User" submodule--helper sanitize-config >actual &&
+ test_must_be_empty actual
+'
+
+sq="'"
+test_expect_success 'sanitize-config keeps credential.helper' '
+ git -c credential.helper=helper submodule--helper sanitize-config >actual &&
+ echo "${sq}credential.helper=helper${sq}" >expect &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 63e04277f9..900f7de05a 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -200,6 +200,26 @@ test_expect_success '--amend --edit of empty message' '
test_cmp expect msg
'
+test_expect_success '--amend to set message to empty' '
+ echo bata >file &&
+ git add file &&
+ git commit -m "unamended" &&
+ git commit --amend --allow-empty-message -m "" &&
+ git diff-tree -s --format=%s HEAD >msg &&
+ echo "" >expect &&
+ test_cmp expect msg
+'
+
+test_expect_success '--amend to set empty message needs --allow-empty-message' '
+ echo conga >file &&
+ git add file &&
+ git commit -m "unamended" &&
+ test_must_fail git commit --amend -m "" &&
+ git diff-tree -s --format=%s HEAD >msg &&
+ echo "unamended" >expect &&
+ test_cmp expect msg
+'
+
test_expect_success '-m --edit' '
echo amended >expect &&
git commit --allow-empty -m buffer &&
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/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh
index 6568429753..a9b266f0d3 100755
--- a/t/t8003-blame-corner-cases.sh
+++ b/t/t8003-blame-corner-cases.sh
@@ -212,4 +212,18 @@ test_expect_success 'blame file with CRLF attributes text' '
grep "A U Thor" actual
'
+test_expect_success 'blame file with CRLF core.autocrlf=true' '
+ git config core.autocrlf false &&
+ printf "testcase\r\n" >crlfinrepo &&
+ >.gitattributes &&
+ git add crlfinrepo &&
+ git commit -m "add crlfinrepo" &&
+ git config core.autocrlf true &&
+ mv crlfinrepo tmp &&
+ git checkout crlfinrepo &&
+ rm tmp &&
+ git blame crlfinrepo >actual &&
+ grep "A U Thor" actual
+'
+
test_done
diff --git a/t/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/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.sh b/t/test-lib.sh
index 79afa8748e..286c5f33d1 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -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/wrapper.c b/wrapper.c
index 9afc1a021c..9009f8bd3d 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
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)