Merge branch 'sb/submodule-path-misc-bugs'
authorJunio C Hamano <gitster@pobox.com>
Mon, 25 Apr 2016 22:17:16 +0000 (15:17 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 25 Apr 2016 22:17:16 +0000 (15:17 -0700)
"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.

* sb/submodule-path-misc-bugs:
t7407: make expectation as clear as possible
submodule update: test recursive path reporting from subdirectory
submodule update: align reporting path for custom command execution
submodule status: correct path handling in recursive submodules
submodule update --init: correct path handling in recursive submodules
submodule foreach: correct path display in recursive submodules

196 files changed:
Documentation/RelNotes/2.8.1.txt [new file with mode: 0644]
Documentation/RelNotes/2.8.2.txt [new file with mode: 0644]
Documentation/RelNotes/2.9.0.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/diff-config.txt
Documentation/diff-options.txt
Documentation/git-apply.txt
Documentation/git-clone.txt
Documentation/git-config.txt
Documentation/git-fetch-pack.txt
Documentation/git-for-each-ref.txt
Documentation/git-merge.txt
Documentation/git-p4.txt
Documentation/git-pull.txt
Documentation/git-rebase.txt
Documentation/git-submodule.txt
Documentation/git-worktree.txt
Documentation/git.txt
Documentation/gitcredentials.txt
Documentation/pretty-options.txt
Documentation/technical/api-config.txt
Documentation/technical/api-parse-options.txt
Documentation/technical/api-trace.txt
GIT-VERSION-GEN
Makefile
README.md
RelNotes
abspath.c
attr.c
branch.c
branch.h
builtin/apply.c
builtin/blame.c
builtin/branch.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/config.c
builtin/diff.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/fsck.c
builtin/grep.c
builtin/index-pack.c
builtin/init-db.c
builtin/log.c
builtin/merge.c
builtin/notes.c
builtin/pull.c
builtin/receive-pack.c
builtin/rev-parse.c
builtin/send-pack.c
builtin/submodule--helper.c
builtin/tag.c
builtin/worktree.c
bundle.c
cache.h
commit.h
compat/apple-common-crypto.h
compat/mingw.c
compat/mingw.h
compat/snprintf.c
compat/vcbuild/include/unistd.h
config.c
config.mak.uname
configure.ac
contrib/completion/git-completion.bash
credential-cache--daemon.c
credential-cache.c
credential.c
diff.c
diff.h
diffcore-rename.c
dir.c
dir.h
environment.c
git-add--interactive.perl
git-compat-util.h
git-merge-octopus.sh
git-mergetool--lib.sh
git-mergetool.sh
git-p4.py
git-rebase--interactive.sh
git-rebase.sh
git-send-email.perl
git-submodule.sh
git.c
git.spec.in
http-backend.c
http.c
imap-send.c
lockfile.c
log-tree.c
mailmap.c
mergetools/examdiff [new file with mode: 0644]
mergetools/winmerge
path.c
pretty.c
quote.c
quote.h
refs.c
refs.h
refs/files-backend.c
refs/refs-internal.h
remote.c
rerere.c
rerere.h
revision.c
revision.h
run-command.c
run-command.h
setup.c
sha1_name.c
strbuf.c
strbuf.h
submodule-config.c
submodule-config.h
submodule.c
submodule.h
t/lib-gpg.sh
t/lib-httpd/apache.conf
t/t0300-credentials.sh
t/t1020-subdirectory.sh
t/t1300-repo-config.sh
t/t1410-reflog.sh
t/t1430-bad-ref-name.sh
t/t1506-rev-parse-diagnosis.sh
t/t1515-rev-parse-outside-repo.sh [new file with mode: 0755]
t/t2025-worktree-add.sh
t/t3033-merge-toplevel.sh
t/t3200-branch.sh
t/t3203-branch-output.sh
t/t3404-rebase-interactive.sh
t/t3412-rebase-root.sh
t/t4001-diff-rename.sh
t/t4013-diff-various.sh
t/t4014-format-patch.sh
t/t4047-diff-dirstat.sh
t/t4200-rerere.sh
t/t4201-shortlog.sh
t/t4202-log.sh
t/t4213-log-tabexpand.sh [new file with mode: 0755]
t/t5300-pack-object.sh
t/t5400-send-pack.sh
t/t5500-fetch-pack.sh
t/t5520-pull.sh
t/t5526-fetch-submodules.sh
t/t5532-fetch-proxy.sh
t/t5550-http-fetch-dumb.sh
t/t5604-clone-reference.sh [new file with mode: 0755]
t/t5605-clone-local.sh [new file with mode: 0755]
t/t5606-clone-options.sh [new file with mode: 0755]
t/t5607-clone-bundle.sh [new file with mode: 0755]
t/t5608-clone-2gb.sh [new file with mode: 0755]
t/t5609-clone-branch.sh [new file with mode: 0755]
t/t5610-clone-detached.sh [new file with mode: 0755]
t/t5611-clone-config.sh [new file with mode: 0755]
t/t5612-clone-refspec.sh [new file with mode: 0755]
t/t5613-info-alternate.sh [new file with mode: 0755]
t/t5700-clone-reference.sh [deleted file]
t/t5701-clone-local.sh [deleted file]
t/t5702-clone-options.sh [deleted file]
t/t5704-bundle.sh [deleted file]
t/t5705-clone-2gb.sh [deleted file]
t/t5706-clone-branch.sh [deleted file]
t/t5707-clone-detached.sh [deleted file]
t/t5708-clone-config.sh [deleted file]
t/t5709-clone-refspec.sh [deleted file]
t/t5710-info-alternate.sh [deleted file]
t/t6009-rev-list-parent.sh
t/t6010-merge-base.sh
t/t6012-rev-list-simplify.sh
t/t6026-merge-attr.sh
t/t6029-merge-subtree.sh
t/t6044-merge-unrelated-index-changes.sh [new file with mode: 0755]
t/t6101-rev-parse-parents.sh
t/t6302-for-each-ref-filter.sh
t/t7004-tag.sh
t/t7400-submodule-basic.sh
t/t7406-submodule-update.sh
t/t7412-submodule--helper.sh [new file with mode: 0755]
t/t7501-commit.sh
t/t7502-commit.sh
t/t7600-merge.sh
t/t7605-merge-resolve.sh
t/t7610-mergetool.sh
t/t7810-grep.sh
t/t8003-blame-corner-cases.sh
t/t9300-fast-import.sh
t/t9400-git-cvsserver-server.sh
t/t9828-git-p4-map-user.sh [new file with mode: 0755]
test-match-trees.c
test-revision-walking.c
upload-pack.c
wt-status.c
xdiff/xprepare.c
diff --git a/Documentation/RelNotes/2.8.1.txt b/Documentation/RelNotes/2.8.1.txt
new file mode 100644 (file)
index 0000000..ef6d80b
--- /dev/null
@@ -0,0 +1,9 @@
+Git v2.8.1 Release Notes
+========================
+
+Fixes since v2.8
+----------------
+
+ * "make rpmbuild" target was broken as its input, git.spec.in, was
+   not updated to match a file it describes that has been renamed
+   recently.  This has been fixed.
diff --git a/Documentation/RelNotes/2.8.2.txt b/Documentation/RelNotes/2.8.2.txt
new file mode 100644 (file)
index 0000000..3db67f4
--- /dev/null
@@ -0,0 +1,55 @@
+Git v2.8.2 Release Notes
+========================
+
+Fixes since v2.8.1
+------------------
+
+ * 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.
+
+ * Bunch of tests on "git clone" has been renumbered for better
+   organization.
+
+ * The tests that involve running httpd leaked the system-wide
+   configuration in /etc/gitconfig to the tested environment.
+
+ * "index-pack --keep=<msg>" was broken since v2.1.0 timeframe.
+
+ * "git config --get-urlmatch", unlike other variants of the "git
+   config --get" family, did not signal error with its exit status
+   when there was no matching configuration.
+
+ * The "--local-env-vars" and "--resolve-git-dir" options of "git
+   rev-parse" failed to work outside a repository when the command's
+   option parsing was rewritten in 1.8.5 era.
+
+ * Fetching of history by naming a commit object name directly didn't
+   work across remote-curl transport.
+
+ * A small memory leak in an error codepath has been plugged in xdiff
+   code.
+
+ * strbuf_getwholeline() did not NUL-terminate the buffer on certain
+   corner cases in its error codepath.
+
+ * 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.
+
+ * "git mergetool" did not work well with conflicts that both sides
+   deleted.
+
+ * "git send-email" had trouble parsing alias file in mailrc format
+   when lines in it had trailing whitespaces on them.
+
+ * When "git merge --squash" stopped due to conflict, the concluding
+   "git commit" failed to read in the SQUASH_MSG that shows the log
+   messages from all the squashed commits.
+
+ * "git merge FETCH_HEAD" dereferenced NULL pointer when merging
+   nothing into an unborn history (which is arguably unusual usage,
+   which perhaps was the reason why nobody noticed it).
+
+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 (file)
index 0000000..6e10b8d
--- /dev/null
@@ -0,0 +1,276 @@
+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 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).
+
+
+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).
+
+
+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).
+
+ * 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).
index 2cd6bdd7d2bc2816c1a9aed1c6a26bbd3285777c..42d2b50477b2863c2d0d93b1e80d70cf85bc5451 100644 (file)
@@ -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
index 6eaa45271c9ff2d1200e4299c3040d52952a2ec4..edba56522bce6e868eb04ad03987d8b078ac907e 100644 (file)
@@ -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
index 306b7e360409255a7aafaa3860d6d6c0412fb870..4b0318e2ac159eb56ff4fbafe2f3c2ebfa374392 100644 (file)
@@ -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>::
@@ -286,8 +286,8 @@ endif::git-format-patch[]
 
 ifndef::git-format-patch[]
 --check::
-       Warn if changes introduce whitespace errors.  What are
-       considered whitespace errors is controlled by `core.whitespace`
+       Warn if changes introduce conflict markers or whitespace errors.
+       What are considered whitespace errors is controlled by `core.whitespace`
        configuration.  By default, trailing whitespaces (including
        lines that solely consist of whitespaces) and a space character
        that is immediately followed by a tab character inside the
index d9ed6a1a4ea6dc0068144eac4c5e0f5ef33a04cb..8ddb207409df99825b561be42234e9c23707287d 100644 (file)
@@ -13,7 +13,7 @@ SYNOPSIS
          [--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
          [--allow-binary-replacement | --binary] [--reject] [-z]
          [-p<n>] [-C<n>] [--inaccurate-eof] [--recount] [--cached]
-         [--ignore-space-change | --ignore-whitespace ]
+         [--ignore-space-change | --ignore-whitespace]
          [--whitespace=(nowarn|warn|fix|error|error-all)]
          [--exclude=<path>] [--include=<path>] [--directory=<root>]
          [--verbose] [--unsafe-paths] [<patch>...]
@@ -21,6 +21,8 @@ SYNOPSIS
 DESCRIPTION
 -----------
 Reads the supplied diff output (i.e. "a patch") and applies it to files.
+When running from a subdirectory in a repository, patched paths
+outside the directory are ignored.
 With the `--index` option the patch is also applied to the index, and
 with the `--cached` option the patch is only applied to the index.
 Without these options, the command applies the patch only to files,
index b7c467a001ad47de0bebd8c985df769fe4e63adc..45d74be29705bae9ecd3028f4dff5755638741f7 100644 (file)
@@ -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
index 153b2d89b551b5c249c6a56604726f8175b0b7c6..6fc08e3d890007238528e60f843b6e01cda4f1fa 100644 (file)
@@ -58,13 +58,13 @@ that location (you can say '--local' but that is the default).
 This command will fail with non-zero status upon error.  Some exit
 codes are:
 
-. The config file is invalid (ret=3),
-. can not write to the config file (ret=4),
-. no section or name was provided (ret=2),
-. the section or key is invalid (ret=1),
-. you try to unset an option which does not exist (ret=5),
-. you try to unset/set an option for which multiple lines match (ret=5), or
-. you try to use an invalid regexp (ret=6).
+- The config file is invalid (ret=3),
+- can not write to the config file (ret=4),
+- no section or name was provided (ret=2),
+- the section or key is invalid (ret=1),
+- you try to unset an option which does not exist (ret=5),
+- you try to unset/set an option for which multiple lines match (ret=5), or
+- you try to use an invalid regexp (ret=6).
 
 On success, the command returns the exit code 0.
 
@@ -86,8 +86,7 @@ OPTIONS
        found and the last value if multiple key values were found.
 
 --get-all::
-       Like get, but does not fail if the number of values for the key
-       is not exactly one.
+       Like get, but returns all values for a multi-valued key.
 
 --get-regexp::
        Like --get-all, but interprets the name as a regular expression and
@@ -102,7 +101,7 @@ OPTIONS
        given URL is returned (if no such key exists, the value for
        section.key is used as a fallback).  When given just the
        section as name, do so for all the keys in the section and
-       list them.
+       list them.  Returns error code 1 if no value is found.
 
 --global::
        For writing options: write to global `~/.gitconfig` file
index 8680f45f8d635253576499453588308618afdc5f..239623cc24f11eb30a896f1e306d489a857109fa 100644 (file)
@@ -104,6 +104,10 @@ be in a separate packet, and the list must end with a flush packet.
        The remote heads to update from. This is relative to
        $GIT_DIR (e.g. "HEAD", "refs/heads/master").  When
        unspecified, update from all heads the remote side has.
++
+If the remote has enabled the options `uploadpack.allowTipSHA1InWant` or
+`uploadpack.allowReachableSHA1InWant`, they may alternatively be 40-hex
+sha1s present on the remote.
 
 SEE ALSO
 --------
index 012e8f9a080d2dc386d2f878c2bc9812ba6113f8..c52578bb87ccfec1cf37defd4c3be4afbb16259b 100644 (file)
@@ -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
index 07f7295ec8b603fcbd907e780d4ed1109ac5b358..689aa4c57cca66c7cd92235248eee2b32ff20451 100644 (file)
@@ -98,6 +98,19 @@ commit or stash your changes before running 'git merge'.
 'git merge --abort' is equivalent to 'git reset --merge' when
 `MERGE_HEAD` is present.
 
+--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, and the list
+       of options at the top of this documentation does not mention
+       this option.  Also `git pull` does not pass this option down
+       to `git merge` (instead, you `git fetch` first, examine what
+       you will be merging and then `git merge` locally with this
+       option).
+
 <commit>...::
        Commits, usually other branch heads, to merge into our branch.
        Specifying more than one commit will create a merge with
index 35e31709181d42f2e20bb2e818601628c03b7168..88ba42b4550a1ac7b579c48445f5933aaf13a20a 100644 (file)
@@ -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::
index a62a2a615d0e0146d538213571d7f41bd02488cf..d033b258e5ee7c6e16c7d96c6339282a849b1b02 100644 (file)
@@ -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
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
index 6ed610a031eeec7320adbbb4a627dc4997499e56..0387b40e0a7301c1e8c89ac6d451aac9fc01ced7 100644 (file)
@@ -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
index 1572f058f59ad1f75c4b5ed6e34fae9110414bad..13adebf7b75f2ab122c4d23708493ef098d3548d 100644 (file)
@@ -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
index 62c76c1c8945512009c6d0ca1179cf71ec50d04e..c62234538ba6be8c82b9a5b86c362606b80cde2a 100644 (file)
@@ -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
index adc940bf7591069c74c9b47aa5e5686e0438d606..8afe349781d57527083fdb75511959fd25a4239b 100644 (file)
@@ -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.0/git.html[documentation for release 2.8]
+* link:v2.8.1/git.html[documentation for release 2.8.1]
 
 * release notes for
+  link:RelNotes/2.8.1.txt[2.8.1].
   link:RelNotes/2.8.0.txt[2.8].
 
 * link:v2.7.3/git.html[documentation for release 2.7.3]
index 1c75be080368a5975b609cd8b902d224eabc9bd8..f3a75d1ce1c5a2709b452faca76f5f6f1cdb7de0 100644 (file)
@@ -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
 -------------------
index 54b88b6dcaaa5df06fc8dad2e5f5172682269bc9..6c67182728c08309c6b6281c59d67264e0c69c49 100644 (file)
@@ -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
index 0d8b99b368aea13a322382314d88229acda0b1db..20741f345e459f41d357039496f7d6be30edab06 100644 (file)
@@ -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
 ----------------------
 
index 5f0757dcc965fc89b96f18e31732390b1afeb84e..695bd4bf43255e42ef26d29a5c6dff35217f220d 100644 (file)
@@ -231,6 +231,13 @@ There are some macros to easily define options:
        pass the command-line option, which can be specified multiple times,
        to another command.
 
+`OPT_CMDMODE(short, long, &int_var, description, enum_val)`::
+       Define an "operation mode" option, only one of which in the same
+       group of "operating mode" options that share the same `int_var`
+       can be given by the user. `enum_val` is set to `int_var` when the
+       option is used, but an error is reported if other "operating mode"
+       option has already set its value to the same `int_var`.
+
 
 The last element of the array must be `OPT_END()`.
 
index 097a651d9680ea31c1a5a0e522a77ccec96c6b69..fadb5979c48b6c128f71db1588412bf0a8204677 100644 (file)
@@ -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);
 }
 ------------
 +
@@ -95,3 +95,46 @@ for (;;) {
 }
 trace_performance(t, "frotz");
 ------------
+
+Bugs & Caveats
+--------------
+
+GIT_TRACE_* environment variables can be used to tell Git to show
+trace output to its standard error stream. Git can often spawn a pager
+internally to run its subcommand and send its standard output and
+standard error to it.
+
+Because GIT_TRACE_PERFORMANCE trace is generated only at the very end
+of the program with atexit(), which happens after the pager exits, it
+would not work well if you send its log to the standard error output
+and let Git spawn the pager at the same time.
+
+As a work around, you can for example use '--no-pager', or set
+GIT_TRACE_PERFORMANCE to another file descriptor which is redirected
+to stderr, or set GIT_TRACE_PERFORMANCE to a file specified by its
+absolute path.
+
+For example instead of the following command which by default may not
+print any performance information:
+
+------------
+GIT_TRACE_PERFORMANCE=2 git log -1
+------------
+
+you may want to use:
+
+------------
+GIT_TRACE_PERFORMANCE=2 git --no-pager log -1
+------------
+
+or:
+
+------------
+GIT_TRACE_PERFORMANCE=3 3>&2 git log -1
+------------
+
+or:
+
+------------
+GIT_TRACE_PERFORMANCE=/path/to/log/file git log -1
+------------
index 4e9450b3ae0c403820f0166435c52c4ea74e7451..655b49011fa5ce2aa1ea2c169aad2eb241dc5027 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.8.0
+DEF_VER=v2.8.0.GIT
 
 LF='
 '
index 2742a6977c6ad871897bc758ea2a7d76359b1eee..a83e322f468a2bd3cad4058a7dffffbda822033f 100644 (file)
--- 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:
 #
@@ -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
@@ -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
 
index d1ffbb61702ee1c51f38ee7f65800147d514d084..2087748f0cbae3865e0a2ad8470e0ba6b7df5a6a 100644 (file)
--- 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
index 7db30403c3471e15f4f15a5e68016d7926b3e3de..66606735cbf4b890b8faf5be7604508bc37c9256 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.8.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.9.0.txt
\ No newline at end of file
index 5edb4e78162ca6646ef42ad2c8abc36872cd75a5..2825de85912fc730d9a40fa66f4f83b6250a4171 100644 (file)
--- 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/attr.c b/attr.c
index 6537a433da201e866208ab23bf32bcafee037683..eec5d7d15a48fb9ca2fdd43d94ae515191d573f1 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -122,7 +122,7 @@ struct pattern {
        const char *pattern;
        int patternlen;
        int nowildcardlen;
-       int flags;              /* EXC_FLAG_* */
+       unsigned flags;         /* EXC_FLAG_* */
 };
 
 /*
index c50ea42172ceadd2a76d12833631301223607067..416244370783adcd7648ae7c03c7d0d8cf778b12 100644 (file)
--- 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;
+}
index 78ad4387cd326ca01f228b109b446e2049f29ee7..d69163daf793f92f3dab92e2fb9bc5217b2dfd6b 100644 (file)
--- 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
index 42c610e2ec180e789fdae4bc637dd96f533d1e10..8e4da2e1bdaf02590289f54750a9602281f4a365 100644 (file)
@@ -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++;
                }
index e982fb81379f57152e34eeda706a57fa1ea4c143..21f42b0b62b81b637f1cc9589cd6c0306a93d05e 100644 (file)
@@ -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;
index 7b45b6bd6b80613de9f894cfbe8e9d2dcae77b09..0adba629d2ae35a4db74fb9d1450b54bb11e51e3 100644 (file)
@@ -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);
index efcbd8f6b5e0b7d551c88c02b6d7d3069a47944d..ea2fe1cf3fc251dcab2f2d70e737a0f8f5000a21 100644 (file)
@@ -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);
index 661639255c564acf3f811e20a0ca3141805cde5d..6576ecf34309db4135c7a41ad6d0ef70e4a21f24 100644 (file)
@@ -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;
 }
index b3bd2d41813f80d848afc199c2fae92cae6b6c11..391126e58d12f4683e131a310d94d6de4571c7e6 100644 (file)
@@ -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, "-")) {
@@ -726,9 +727,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                                      &sb, &ctx);
                hook_arg1 = "message";
        } else if (!stat(git_path_merge_msg(), &statbuf)) {
+               /*
+                * prepend SQUASH_MSG here if it exists and a
+                * "merge --squash" was originally performed
+                */
+               if (!stat(git_path_squash_msg(), &statbuf)) {
+                       if (strbuf_read_file(&sb, git_path_squash_msg(), 0) < 0)
+                               die_errno(_("could not read SQUASH_MSG"));
+                       hook_arg1 = "squash";
+               } else
+                       hook_arg1 = "merge";
                if (strbuf_read_file(&sb, git_path_merge_msg(), 0) < 0)
                        die_errno(_("could not read MERGE_MSG"));
-               hook_arg1 = "merge";
        } else if (!stat(git_path_squash_msg(), &statbuf)) {
                if (strbuf_read_file(&sb, git_path_squash_msg(), 0) < 0)
                        die_errno(_("could not read SQUASH_MSG"));
@@ -1162,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;
index ca9f834ae648177a67e1c9fea9481dfe58ff3972..1d7c6ef558bf7adcfb94ea454966667f987f7701 100644 (file)
@@ -417,6 +417,7 @@ static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
 
 static int get_urlmatch(const char *var, const char *url)
 {
+       int ret;
        char *section_tail;
        struct string_list_item *item;
        struct urlmatch_config config = { STRING_LIST_INIT_DUP };
@@ -443,6 +444,8 @@ static int get_urlmatch(const char *var, const char *url)
        git_config_with_options(urlmatch_config_entry, &config,
                                &given_config_source, respect_includes);
 
+       ret = !values.nr;
+
        for_each_string_list_item(item, &values) {
                struct urlmatch_current_candidate_value *matched = item->util;
                struct strbuf buf = STRBUF_INIT;
@@ -459,7 +462,7 @@ static int get_urlmatch(const char *var, const char *url)
        free(config.url.url);
 
        free((void *)config.section);
-       return 0;
+       return ret;
 }
 
 static char *default_user_config(void)
index 52c98a9217c60a60f0012dbdafbb193e78ec8a33..343c6b8f2522a295a6148434572e77a3a3c5a16b 100644 (file)
@@ -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);
index 79a611fda1f8b344ce619dd9318695a69eebb695..bfd0be44a91f66034c98361dc713cc0883693a69 100644 (file)
@@ -16,10 +16,20 @@ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
        struct ref *ref;
        struct object_id oid;
 
-       if (!get_oid_hex(name, &oid) && name[GIT_SHA1_HEXSZ] == ' ')
-               name += GIT_SHA1_HEXSZ + 1;
-       else
+       if (!get_oid_hex(name, &oid)) {
+               if (name[GIT_SHA1_HEXSZ] == ' ') {
+                       /* <sha1> <ref>, find refname */
+                       name += GIT_SHA1_HEXSZ + 1;
+               } else if (name[GIT_SHA1_HEXSZ] == '\0') {
+                       ; /* <sha1>, leave sha1 as name */
+               } else {
+                       /* <ref>, clear cruft from oid */
+                       oidclr(&oid);
+               }
+       } else {
+               /* <ref>, clear cruft from get_oid_hex */
                oidclr(&oid);
+       }
 
        ref = alloc_ref(name);
        oidcpy(&ref->old_oid, &oid);
index e4639d8eb1d5fda586520f10271c05a0897f2ea5..f8455bde7a84e110da182d56f62ac3f89026f55c 100644 (file)
@@ -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;
index 55eac756f7c83e496daae7cb8874552caaa58ff5..3f27456883ede5efa2a57cfa9e2dcc889e952338 100644 (file)
@@ -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");
index aa7435f380e95d87982cd1ae71e91bc53e77af50..111b6f6cf1a61b0fb481101fcdc5a9f3f48c4650 100644 (file)
@@ -522,12 +522,14 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
 }
 
 static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
-                         int exc_std)
+                         int exc_std, int use_index)
 {
        struct dir_struct dir;
        int i, hit = 0;
 
        memset(&dir, 0, sizeof(dir));
+       if (!use_index)
+               dir.flags |= DIR_NO_GITLINKS;
        if (exc_std)
                setup_standard_excludes(&dir);
 
@@ -902,7 +904,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
                if (list.nr)
                        die(_("--no-index or --untracked cannot be used with revs."));
-               hit = grep_directory(&opt, &pathspec, use_exclude);
+               hit = grep_directory(&opt, &pathspec, use_exclude, use_index);
        } else if (0 <= opt_exclude) {
                die(_("--[no-]exclude-standard cannot be used for tracked contents."));
        } else if (!list.nr) {
index 45245199aebafc7fdb8eb96197dacd86c422c413..2d1eb8bb8a433e6bda50e93e3001ae6d43db093f 100644 (file)
@@ -1599,6 +1599,18 @@ static void show_pack_info(int stat_only)
        }
 }
 
+static const char *derive_filename(const char *pack_name, const char *suffix,
+                                  struct strbuf *buf)
+{
+       size_t len;
+       if (!strip_suffix(pack_name, ".pack", &len))
+               die(_("packfile name '%s' does not end with '.pack'"),
+                   pack_name);
+       strbuf_add(buf, pack_name, len);
+       strbuf_addstr(buf, suffix);
+       return buf->buf;
+}
+
 int cmd_index_pack(int argc, const char **argv, const char *prefix)
 {
        int i, fix_thin_pack = 0, verify = 0, stat_only = 0;
@@ -1707,24 +1719,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                usage(index_pack_usage);
        if (fix_thin_pack && !from_stdin)
                die(_("--fix-thin cannot be used without --stdin"));
-       if (!index_name && pack_name) {
-               size_t len;
-               if (!strip_suffix(pack_name, ".pack", &len))
-                       die(_("packfile name '%s' does not end with '.pack'"),
-                           pack_name);
-               strbuf_add(&index_name_buf, pack_name, len);
-               strbuf_addstr(&index_name_buf, ".idx");
-               index_name = index_name_buf.buf;
-       }
-       if (keep_msg && !keep_name && pack_name) {
-               size_t len;
-               if (!strip_suffix(pack_name, ".pack", &len))
-                       die(_("packfile name '%s' does not end with '.pack'"),
-                           pack_name);
-               strbuf_add(&keep_name_buf, pack_name, len);
-               strbuf_addstr(&keep_name_buf, ".idx");
-               keep_name = keep_name_buf.buf;
-       }
+       if (!index_name && pack_name)
+               index_name = derive_filename(pack_name, ".idx", &index_name_buf);
+       if (keep_msg && !keep_name && pack_name)
+               keep_name = derive_filename(pack_name, ".keep", &keep_name_buf);
+
        if (verify) {
                if (!index_name)
                        die(_("--verify with no packfile name given"));
index 6223b7d46af346b0d96870ee0c647ab9a5440b33..b2d8d40a6750e5794dd9bfbfbcb31924a7eec4ba 100644 (file)
@@ -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"));
@@ -322,6 +325,7 @@ int set_git_dir_init(const char *git_dir, const char *real_git_dir,
                set_git_dir(real_path(git_dir));
                git_link = NULL;
        }
+       startup_info->have_repository = 1;
        return 0;
 }
 
@@ -369,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
@@ -377,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");
@@ -398,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] != '/' ? "/" : "");
        }
 
@@ -493,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:
@@ -506,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;
@@ -524,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
index 0d738d6ddc0d6335e15392dd90e4118e2cb58b88..dff3fbbb437c462e51544dbf1d4781c550a14d1a 100644 (file)
@@ -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;
index 101ffeff4c942636e0ca688357a4b8ec8aa2a431..b555a1bf9cd46ea57bcbe1c8d8b7ac146fe9f81c 100644 (file)
@@ -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)
@@ -1257,12 +1269,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        builtin_merge_options);
 
        if (!head_commit) {
-               struct commit *remote_head;
                /*
                 * If the merged head is a valid one there is no reason
                 * to forbid "git merge" into a branch yet to be born.
                 * We do the same for "git pull".
                 */
+               unsigned char *remote_head_sha1;
                if (squash)
                        die(_("Squash commit into empty head not supported yet"));
                if (fast_forward == FF_NO)
@@ -1270,13 +1282,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                            "an empty head"));
                remoteheads = collect_parents(head_commit, &head_subsumed,
                                              argc, argv, NULL);
-               remote_head = remoteheads->item;
-               if (!remote_head)
+               if (!remoteheads)
                        die(_("%s - not something we can merge"), argv[0]);
                if (remoteheads->next)
                        die(_("Can merge only exactly one commit into empty head"));
-               read_empty(remote_head->object.oid.hash, 0);
-               update_ref("initial pull", "HEAD", remote_head->object.oid.hash,
+               remote_head_sha1 = remoteheads->item->object.oid.hash;
+               read_empty(remote_head_sha1, 0);
+               update_ref("initial pull", "HEAD", remote_head_sha1,
                           NULL, 0, UPDATE_REFS_DIE_ON_ERR);
                goto done;
        }
@@ -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)) {
                /*
index ed6f2222f4bf9c54535a7a23971c7749ca8d05d5..6fd058de9272631a3d5135ccbbd79bf1b4ff135c 100644 (file)
@@ -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;
 }
 
index 10eff03967e0b9cb473d262b443949c55681fd1b..d98f481d31535fd63773fd987b5a252901ca2ac0 100644 (file)
@@ -86,6 +86,8 @@ 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;
@@ -149,6 +151,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),
@@ -305,6 +309,18 @@ static enum rebase_type config_get_rebase(void)
        return REBASE_FALSE;
 }
 
+/**
+ * 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.
  */
@@ -789,6 +805,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 +843,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 +854,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);
 
index c8e32b297c4be68bb0d33a6a88c15470821ae4ea..a744437b5876171250d4731d181f7b30e5444187 100644 (file)
 #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();
 
index cf8487b3b95fcca9a8a96a1562646ee8ddd5b354..c961b74c5aaae41153b89f4e877437ba7f0d70c7 100644 (file)
@@ -505,6 +505,7 @@ N_("git rev-parse --parseopt [<options>] -- [<args>...]\n"
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
        int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+       int did_repo_setup = 0;
        int has_dashdash = 0;
        int output_prefix = 0;
        unsigned char sha1[20];
@@ -528,11 +529,40 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                }
        }
 
-       prefix = setup_git_directory();
-       git_config(git_default_config, NULL);
+       /* No options; just report on whether we're in a git repo or not. */
+       if (argc == 1) {
+               setup_git_directory();
+               git_config(git_default_config, NULL);
+               return 0;
+       }
+
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
 
+               if (!strcmp(arg, "--local-env-vars")) {
+                       int i;
+                       for (i = 0; local_repo_env[i]; i++)
+                               printf("%s\n", local_repo_env[i]);
+                       continue;
+               }
+               if (!strcmp(arg, "--resolve-git-dir")) {
+                       const char *gitdir = argv[++i];
+                       if (!gitdir)
+                               die("--resolve-git-dir requires an argument");
+                       gitdir = resolve_gitdir(gitdir);
+                       if (!gitdir)
+                               die("not a gitdir '%s'", argv[i]);
+                       puts(gitdir);
+                       continue;
+               }
+
+               /* The rest of the options require a git repository. */
+               if (!did_repo_setup) {
+                       prefix = setup_git_directory();
+                       git_config(git_default_config, NULL);
+                       did_repo_setup = 1;
+               }
+
                if (!strcmp(arg, "--git-path")) {
                        if (!argv[i + 1])
                                die("--git-path requires an argument");
@@ -706,12 +736,6 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                add_ref_exclusion(&ref_excludes, arg + 10);
                                continue;
                        }
-                       if (!strcmp(arg, "--local-env-vars")) {
-                               int i;
-                               for (i = 0; local_repo_env[i]; i++)
-                                       printf("%s\n", local_repo_env[i]);
-                               continue;
-                       }
                        if (!strcmp(arg, "--show-toplevel")) {
                                const char *work_tree = get_git_work_tree();
                                if (work_tree)
@@ -767,16 +791,6 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                puts(prefix_filename(pfx, strlen(pfx), get_git_common_dir()));
                                continue;
                        }
-                       if (!strcmp(arg, "--resolve-git-dir")) {
-                               const char *gitdir = argv[++i];
-                               if (!gitdir)
-                                       die("--resolve-git-dir requires an argument");
-                               gitdir = resolve_gitdir(gitdir);
-                               if (!gitdir)
-                                       die("not a gitdir '%s'", argv[i]);
-                               puts(gitdir);
-                               continue;
-                       }
                        if (!strcmp(arg, "--is-inside-git-dir")) {
                                printf("%s\n", is_inside_git_dir() ? "true"
                                                : "false");
index 5b9dd6a9d8ebe888c2fc912af3590b92737e7a99..1ff5a6753803f8c2ccb5e66dcb926582bd25fd37 100644 (file)
@@ -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);
 
index 5295b727d4609fa33a08c31b2d051ce3f98244fb..3bd6883eff842ee139a3d24475401d75f65fe53e 100644 (file)
@@ -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]);
 }
index 1705c9466546c7de52b12d3fbafbbe7b6d43cf4e..528a1bab69a6166877847dd3dbda5d5f724da3e9 100644 (file)
@@ -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)
 {
@@ -166,6 +167,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 +333,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 +391,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 +437,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 +479,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 ||
index 38b56096bd3b914d1128e0bebb5c964a3cdd1b74..d8e3795dc44a0dc047cac77af92282361227111a 100644 (file)
@@ -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"));
index 506ac49691bb7d53b07baf2a1d2581bf6f7ca82a..bbf4efa0a0a38ac8f13ee1c2178eff1d2127c802 100644 (file)
--- 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 b829410f6da0afc14353b4621d2fdf874181a9f7..2711048cad7d5c7bba04c5737aa6142caf49d023 100644 (file)
--- 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
@@ -1526,7 +1558,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 +1581,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);
@@ -1771,7 +1801,7 @@ int split_cmdline(char *cmdline, const char ***argv);
 /* Takes a negative value returned by split_cmdline */
 const char *split_cmdline_strerror(int cmdline_errno);
 
-/* git.c */
+/* setup.c */
 struct startup_info {
        int have_repository;
        const char *prefix;
index 5d58be0017a8eb26cd3f58c1e3b0b610f684beba..b06db4d5d9004e329a2a04de3d9af71a3578ef9d 100644 (file)
--- 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;
index d3fb2641813404a95709d2b219b9ab1fa1434296..11727f3e1ed7f6d51e334734c19e9eafbf034783 100644 (file)
@@ -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>
index 54c82ecf201dde2c317efe6862173c1b40f2552d..0413d5c3cdd128ca721c75b588dc557bdbb1f949 100644 (file)
@@ -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) */
index c008694639a654e6a04e541af70e90a5fa4da9e0..1de70ffd62a63070ca79d27ad0b323dfbcacb5aa 100644 (file)
@@ -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
index 42ea1ac110813bbd16e77cfbc36f16e6a5e9ddb2..0b1168853778810d385c84625ad643587537913b 100644 (file)
@@ -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
index c65c2cd566b51473da12786f28074040c410f343..3a959d124ca794446800653e9c033b883d8d7b8f 100644 (file)
@@ -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
index 9ba40bc1b039b9b65425dc4fa1bd9c7f1fcb0868..10b5c957ae714e850c9029ac23f88be407db3173 100644 (file)
--- 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,
index fe8096f8a65241760cfdc3dabd4cf63ee412e9ca..40d6b29eeef9a9b89b02100bb087fe9f37a3a7d6 100644 (file)
@@ -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
index 0cd9f4680b84bccd0a98193c401b13749b05b833..c279025747349c19039b0e061954ae67b91b735a 100644 (file)
@@ -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],
index e3918c87e3adf32a9d7a4f0320c92c497376b9b5..34024754d9296b56c194a7959afb7b7f28402457 100644 (file)
@@ -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=
index caef21e4fc91898f209709f723de1df5afc66a66..291c0fd5e935b5abedc629697d44e5a9f57727bd 100644 (file)
@@ -126,8 +126,17 @@ static void serve_one_client(FILE *in, FILE *out)
                        fprintf(out, "password=%s\n", e->item.password);
                }
        }
-       else if (!strcmp(action.buf, "exit"))
+       else if (!strcmp(action.buf, "exit")) {
+               /*
+                * It's important that we clean up our socket first, and then
+                * signal the client only once we have finished the cleanup.
+                * Calling exit() directly does this, because we clean up in
+                * our atexit() handler, and then signal the client when our
+                * process actually ends, which closes the socket and gives
+                * them EOF.
+                */
                exit(0);
+       }
        else if (!strcmp(action.buf, "erase"))
                remove_credential(&c);
        else if (!strcmp(action.buf, "store")) {
index f4afdc6988c32cd95769dee488f8495b8a6ce5b6..86e21de49be4d48defd3e9da5cde170291bca600 100644 (file)
@@ -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;
 }
 
index 7d6501d190a529933d501e7f7006a70c4897282b..aa996669fc40021e0c8cb01fa8021da7064e2781 100644 (file)
@@ -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 059123c5dcef4129763895b0f2ad5a54728b0c07..4dfe6609d059b56c5ab4dd31c8d2139c7fa90abb 100644 (file)
--- 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 e7d68edaf9d4744ce3c48356e1835c5506d5322d..125447be09ebf1d79beb7312785c2e89654fbdb0 100644 (file)
--- 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 *);
index 3b3c1ed535e7c8b17947d69dbe0bd5c8f115cf2d..7f03eb5a0404d4b44f292fba76433dfec74dcd44 100644 (file)
@@ -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 a4a9d9fae154849fe38b153af9b16e00ef401f60..996653b0d3b6d81b15a3185d8d63314de647c53d 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -457,7 +457,7 @@ int no_wildcard(const char *string)
 
 void parse_exclude_pattern(const char **pattern,
                           int *patternlen,
-                          int *flags,
+                          unsigned *flags,
                           int *nowildcardlen)
 {
        const char *p = *pattern;
@@ -498,7 +498,7 @@ void add_exclude(const char *string, const char *base,
 {
        struct exclude *x;
        int patternlen;
-       int flags;
+       unsigned flags;
        int nowildcardlen;
 
        parse_exclude_pattern(&string, &patternlen, &flags, &nowildcardlen);
@@ -798,7 +798,7 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname)
 
 int match_basename(const char *basename, int basenamelen,
                   const char *pattern, int prefix, int patternlen,
-                  int flags)
+                  unsigned flags)
 {
        if (prefix == patternlen) {
                if (patternlen == basenamelen &&
@@ -823,7 +823,7 @@ int match_basename(const char *basename, int basenamelen,
 int match_pathname(const char *pathname, int pathlen,
                   const char *base, int baselen,
                   const char *pattern, int prefix, int patternlen,
-                  int flags)
+                  unsigned flags)
 {
        const char *name;
        int namelen;
diff --git a/dir.h b/dir.h
index cd46f30017ce239720926afdad4301b2ac402ccf..301b737a37236c1d61d109b29fa556143c28df46 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -27,7 +27,7 @@ struct exclude {
        int nowildcardlen;
        const char *base;
        int baselen;
-       int flags;
+       unsigned flags;         /* EXC_FLAG_* */
 
        /*
         * Counting starts from 1 for line numbers in ignore files,
@@ -226,10 +226,10 @@ struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname,
  * attr.c:path_matches()
  */
 extern int match_basename(const char *, int,
-                         const char *, int, int, int);
+                         const char *, int, int, unsigned);
 extern int match_pathname(const char *, int,
                          const char *, int,
-                         const char *, int, int, int);
+                         const char *, int, int, unsigned);
 
 extern struct exclude *last_exclude_matching(struct dir_struct *dir,
                                             const char *name, int *dtype);
@@ -241,7 +241,7 @@ extern struct exclude_list *add_exclude_list(struct dir_struct *dir,
 extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,
                                          struct exclude_list *el, int check_index);
 extern void add_excludes_from_file(struct dir_struct *, const char *fname);
-extern void parse_exclude_pattern(const char **string, int *patternlen, int *flags, int *nowildcardlen);
+extern void parse_exclude_pattern(const char **string, int *patternlen, unsigned *flags, int *nowildcardlen);
 extern void add_exclude(const char *string, const char *base,
                        int baselen, struct exclude_list *el, int srcpos);
 extern void clear_exclude_list(struct exclude_list *el);
index 6dec9d0403f11579a7ab316db87a1dbbc58e71b2..57acb2fe2aee79a30c4c956dcececd6a7122aeb6 100644 (file)
@@ -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;
@@ -64,7 +62,6 @@ int grafts_replace_parents = 1;
 int core_apply_sparse_checkout;
 int merge_log_config = -1;
 int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
-struct startup_info *startup_info;
 unsigned long pack_size_limit_cfg;
 
 #ifndef PROTECT_HFS_DEFAULT
@@ -325,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;
+}
index 77876d433a1ba2384a690155b51dac8118fa27b4..822f857038e9b807fc6737952cc52b1782dc7947 100755 (executable)
@@ -45,6 +45,7 @@
 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;
 }
index 474395471f623d6a02de9814a5490f3b1df3a0f1..1f8b5f3b1f1ac17716681fee2d72b9c124a9b99c 100644 (file)
@@ -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
index 8643f74cb09f278c37851c418e839c7d160f36ca..dc2fd1b5a47b3d759aa251549d2c158713ccfa4e 100755 (executable)
@@ -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
index 54ac8e484674710ca21ea0038d07dcbefbe2fb1e..302c56de5b6ceb96e1eb075f9e208a655dcb9c9e 100644 (file)
@@ -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"
+}
index 9f77e3a8bb0b1197f860912b6d0e56b40b607cb9..f67bab55e85d00b06601fa488ba01975eb865359 100755 (executable)
@@ -126,7 +126,12 @@ resolve_deleted_merge () {
                case "$ans" in
                [mMcC]*)
                        git add -- "$MERGED"
-                       cleanup_temp_files --save-backup
+                       if test "$merge_keep_backup" = "true"
+                       then
+                               cleanup_temp_files --save-backup
+                       else
+                               cleanup_temp_files
+                       fi
                        return 0
                        ;;
                [dD]*)
@@ -135,6 +140,10 @@ resolve_deleted_merge () {
                        return 0
                        ;;
                [aA]*)
+                       if test "$merge_keep_temporaries" = "false"
+                       then
+                               cleanup_temp_files
+                       fi
                        return 1
                        ;;
                esac
@@ -282,8 +291,14 @@ merge_file () {
                return
        fi
 
-       mv -- "$MERGED" "$BACKUP"
-       cp -- "$BACKUP" "$MERGED"
+       if test -f "$MERGED"
+       then
+               mv -- "$MERGED" "$BACKUP"
+               cp -- "$BACKUP" "$MERGED"
+       fi
+       # Create a parent directory to handle delete/delete conflicts
+       # where the base's directory no longer exists.
+       mkdir -p "$(dirname "$MERGED")"
 
        checkout_staged_file 1 "$MERGED" "$BASE"
        checkout_staged_file 2 "$MERGED" "$LOCAL"
@@ -295,7 +310,9 @@ merge_file () {
                describe_file "$local_mode" "local" "$LOCAL"
                describe_file "$remote_mode" "remote" "$REMOTE"
                resolve_deleted_merge
-               return
+               status=$?
+               rmdir -p "$(dirname "$MERGED")" 2>/dev/null
+               return $status
        fi
 
        if is_symlink "$local_mode" || is_symlink "$remote_mode"
index 825b9f32d5540ab1f8d373fff2d3bf79caabee00..527d44bd202745d29680fe74833c29cc7fb122c1 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -1160,6 +1160,15 @@ def getUserMapFromPerforceServer(self):
             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():
index 4cde685b43a4463bac5f96bcb4f8ed8b3e9347be..9ea30756f13349c96784f0091da4c3c667166485 100644 (file)
@@ -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)
index cf60c4390870ef28cff08b6a173a435d41e2d470..0bf41ee72b79953dbb8fdab44e275c027174f5b7 100755 (executable)
@@ -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?")"
index d356901348042a7f2ec67211857e4d551d169d52..1406f64d780a481cfc8be0e09a66f0c76887badd 100755 (executable)
 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);
@@ -533,7 +533,7 @@ sub parse_sendmail_aliases {
                        $aliases{$alias} = \@addr
                }}},
        mailrc => sub { my $fh = shift; while (<$fh>) {
-               if (/^alias\s+(\S+)\s+(.*)$/) {
+               if (/^alias\s+(\S+)\s+(.*?)\s*$/) {
                        # spaces delimit multiple addresses
                        $aliases{$1} = [ quotewords('\s+', 0, $2) ];
                }}},
@@ -949,7 +949,7 @@ sub validate_address_list {
 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
 }
index 753a90d3071d1917495ca725fe15a944f84b6f60..2a84d7e66a0e6c2bbd0bee1c3e796e4580feaea9 100755 (executable)
@@ -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
@@ -418,7 +428,7 @@ cmd_foreach()
                        name=$(git submodule--helper name "$sm_path")
                        (
                                prefix="$prefix$sm_path/"
-                               clear_local_git_env
+                               sanitize_submodule_env
                                cd "$sm_path" &&
                                sm_path=$(relative_path "$sm_path") &&
                                # we make $path available to scripts ...
@@ -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)
@@ -810,7 +808,7 @@ Maybe you want to use 'update --init'?")"
                                die "$(eval_gettext "Invalid update mode '$update_module' for submodule '$name'")"
                        esac
 
-                       if (clear_local_git_env; cd "$sm_path" && $command "$sha1")
+                       if (sanitize_submodule_env; cd "$sm_path" && $command "$sha1")
                        then
                                say "$say_msg"
                        elif test -n "$must_die_on_failure"
@@ -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,7 @@ cmd_status()
                then
                        (
                                prefix="$displaypath/"
-                               clear_local_git_env
+                               sanitize_submodule_env
                                wt_prefix=
                                cd "$sm_path" &&
                                eval cmd_status
@@ -1233,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/git.c b/git.c
index 6cc0c077f9761f9b56e5fb7e666722b437c4fae4..968a8a464588f10c5c1564440e06d5e5afe8d37a 100644 (file)
--- a/git.c
+++ b/git.c
@@ -15,7 +15,6 @@ const char git_more_info_string[] =
           "concept guides. See 'git help <command>' or 'git help <concept>'\n"
           "to read about a specific subcommand or concept.");
 
-static struct startup_info git_startup_info;
 static int use_pager = -1;
 static char *orig_cwd;
 static const char *env_names[] = {
@@ -637,8 +636,6 @@ int main(int argc, char **av)
        const char *cmd;
        int done_help = 0;
 
-       startup_info = &git_startup_info;
-
        cmd = git_extract_argv0_path(argv[0]);
        if (!cmd)
                cmd = "git-help";
index d61d537ef0cbdfa9d4a719ca622e148bbe436fd1..bfd1cfb63fa4c883af60d5086f66709965d4ec67 100644 (file)
@@ -146,7 +146,7 @@ rm -rf $RPM_BUILD_ROOT
 %files -f bin-man-doc-files
 %defattr(-,root,root)
 %{_datadir}/git-core/
-%doc README COPYING Documentation/*.txt
+%doc README.md COPYING Documentation/*.txt
 %{!?_without_docs: %doc Documentation/*.html Documentation/howto}
 %{!?_without_docs: %doc Documentation/technical}
 %{_sysconfdir}/bash_completion.d
index 8870a2681eb375505e4273b03a0d3e034e59a48e..214881459d828101fa0927321c5a8facb3a540f0 100644 (file)
@@ -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 69da4454d8f754598d0316d0e1cb34870aba2b8e..4304b80ad3ac9d8ae249bc0bc007074bc5aa6181 100644 (file)
--- 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"))
index 2c52027c84455819740bed9f53970e175b9ca133..938c6915858b93b7c860e49e906c45e0e2ea5d03 100644 (file)
@@ -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");
index 80d056d2ede0a5279c0b5c05c35b46336f564533..9268cdf325f3881104d6d12ef31639536b15dcb2 100644 (file)
@@ -149,13 +149,15 @@ static int lock_file_timeout(struct lock_file *lk, const char *path,
 void unable_to_lock_message(const char *path, int err, struct strbuf *buf)
 {
        if (err == EEXIST) {
-               strbuf_addf(buf, "Unable to create '%s.lock': %s.\n\n"
-                   "If no other git process is currently running, this probably means a\n"
-                   "git process crashed in this repository earlier. Make sure no other git\n"
-                   "process is running and remove the file manually to continue.",
+               strbuf_addf(buf, _("Unable to create '%s.lock': %s.\n\n"
+                   "Another git process seems to be running in this repository, e.g.\n"
+                   "an editor opened by 'git commit'. Please make sure all processes\n"
+                   "are terminated then try again. If it still fails, a git process\n"
+                   "may have crashed in this repository earlier:\n"
+                   "remove the file manually to continue."),
                            absolute_path(path), strerror(err));
        } else
-               strbuf_addf(buf, "Unable to create '%s.lock': %s",
+               strbuf_addf(buf, _("Unable to create '%s.lock': %s"),
                            absolute_path(path), strerror(err));
 }
 
index 60f983934d5ea2c8e276d2c493d78caf56e9a39a..78a5381d0eaebb9092a8bb456fa656462ec65170 100644 (file)
@@ -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;
index f4a0f1cf27bf0a10626cc5b1fd628e12cbda3a7e..972623709fdac5503328f7e252d5334dfcb461c6 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -250,7 +250,8 @@ int read_mailmap(struct string_list *map, char **repo_abbrev)
                git_mailmap_blob = "HEAD:.mailmap";
 
        err |= read_mailmap_file(map, ".mailmap", repo_abbrev);
-       err |= read_mailmap_blob(map, git_mailmap_blob, repo_abbrev);
+       if (startup_info->have_repository)
+               err |= read_mailmap_blob(map, git_mailmap_blob, repo_abbrev);
        err |= read_mailmap_file(map, git_mailmap_file, repo_abbrev);
        return err;
 }
diff --git a/mergetools/examdiff b/mergetools/examdiff
new file mode 100644 (file)
index 0000000..7b524d4
--- /dev/null
@@ -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"
+}
index 74a66d4e8dae9529d14908aeb99e09d58f13724e..f3819d316a2c05d9fe1f850d6ac20b04d9df0417 100644 (file)
@@ -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 969b494d72dff1439150e7bb945339be07c4d475..bbaea5ab0bf73056dfa74063474f62685fda1d5e 100644 (file)
--- 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;
index 92b2870a7eabb6a99ffbbcfc222d2ff1c1186fa0..87c44971a1d2070e4d256818e96fccc29472ec4a 100644 (file)
--- 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 fe884d24521f91a0f1f30b3cf5f08734bab7bd31..b281a8fe454e39728ce112915c7a5efb0f9c8bc0 100644 (file)
--- 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 99e04d34bf223a0147e65623322ef2c2fb01e01d..6c53a2cc66c44aaa12b787a5c16f739449753388 100644 (file)
--- 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 b0e6ece6f43437bb7d59490d64802baa3b277090..87dc82f1d87d6c0969b97f94035233bec0bc3729 100644 (file)
--- 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 2f3decb432cfeda97fd5da7f16dd4e5b725b59bc..9230d4714205f810c7c749559a20e1379197043b 100644 (file)
--- 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,
index 81f68f846b69af65badfbe8c25d4a03601fb23c8..1f38076411dc62b82792b677d95fe775effc79cd 100644 (file)
@@ -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);
index c7dded35f47e0c30131839b3378100e5a778476c..3a4f634cb4f234806ed61e7977fc57b04baddf7c 100644 (file)
@@ -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 */
index fc02698587c61d230200272ce4704403e1a6741f..28fd676acbbf4985422d74c9f1987dd0a5765039 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -455,7 +455,6 @@ static void read_config(void)
 {
        static int loaded;
        struct object_id oid;
-       const char *head_ref;
        int flag;
 
        if (loaded)
@@ -463,10 +462,12 @@ static void read_config(void)
        loaded = 1;
 
        current_branch = NULL;
-       head_ref = resolve_ref_unsafe("HEAD", 0, oid.hash, &flag);
-       if (head_ref && (flag & REF_ISSYMREF) &&
-           skip_prefix(head_ref, "refs/heads/", &head_ref)) {
-               current_branch = make_branch(head_ref, 0);
+       if (startup_info->have_repository) {
+               const char *head_ref = resolve_ref_unsafe("HEAD", 0, oid.hash, &flag);
+               if (head_ref && (flag & REF_ISSYMREF) &&
+                   skip_prefix(head_ref, "refs/heads/", &head_ref)) {
+                       current_branch = make_branch(head_ref, 0);
+               }
        }
        git_config(handle_config, NULL);
        alias_all_urls();
index 587b7e2717b14748b92a5960f79947b5fba088f0..c8b9f407872f431399a4c5c321742c32cded5711 100644 (file)
--- 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");
 
@@ -364,103 +514,6 @@ static int handle_file(const char *path, unsigned char *sha1, const char *output
        return hunk_no;
 }
 
-/*
- * 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
@@ -568,6 +621,33 @@ int rerere_remaining(struct string_list *merge_rr)
        return 0;
 }
 
+/*
+ * 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
@@ -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);
index 1222e91921882b41ac9c1648960eca9305b27d8d..c2961feaaa8a8297d59fb13b120c1a57c8a2868a 100644 (file)
--- 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);
index 8b2dfe3160784f9780cf541a674fe89100d38a93..b683476b9c44b90a6dc2acd5cf1f725f741fbaa3 100644 (file)
@@ -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;
 }
 
index dca0d381715cf5bc6888587feaec989012f51252..9fac1a607de6470ab0c9b7e5079e8d36c8d68c75 100644 (file)
@@ -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;
index c72601056cf5ae7be2593ae89af4effc26a1b043..8c7115ade496e5bbaf1b584199da8f3ab21fa087 100644 (file)
@@ -902,7 +902,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 +910,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 +994,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 +1079,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 +1117,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);
 
                        /*
index 3d1e59e26e33d062a10698fc139f7fc0f4ae14ec..de1727efab9d7e57a41626ac8bd89e5149412dc8 100644 (file)
@@ -140,7 +140,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 +149,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 +159,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 +167,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 +178,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/setup.c b/setup.c
index de1a2a7ea5973fef256328a26730117466c15172..c86bf5c9fabeab4b60b0696b9ae9b2f0f2bb720e 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -5,7 +5,9 @@
 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;
 
 /*
  * The input parameter must contain an absolute path, and it must already be
@@ -100,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)) {
@@ -370,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,
@@ -387,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;
 }
@@ -397,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;
 }
 
 /*
@@ -905,10 +940,9 @@ const char *setup_git_directory_gently(int *nongit_ok)
        else
                setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
 
-       if (startup_info) {
-               startup_info->have_repository = !nongit_ok || !*nongit_ok;
-               startup_info->prefix = prefix;
-       }
+       startup_info->have_repository = !nongit_ok || !*nongit_ok;
+       startup_info->prefix = prefix;
+
        return prefix;
 }
 
@@ -963,28 +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)
 {
-       return check_repository_format_gently(get_git_dir(), NULL);
+       check_repository_format_gently(get_git_dir(), NULL);
+       startup_info->have_repository = 1;
 }
 
 /*
index 3acf221f92f7a0857f17c0210044cacc325c8ce0..776101e8d709b924033a141efa38e3f734837c79 100644 (file)
@@ -1353,9 +1353,6 @@ static char *resolve_relative_path(const char *rel)
        if (!starts_with(rel, "./") && !starts_with(rel, "../"))
                return NULL;
 
-       if (!startup_info)
-               die("BUG: startup_info struct is not initialized.");
-
        if (!is_inside_work_tree())
                die("relative path syntax can't be used outside working tree.");
 
index f60e2ee72ba86cbd6c66622366af4a7faf25e1a0..1ba600bd780733f7a985c88f797efdd93c1e49fa 100644 (file)
--- 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)
@@ -481,9 +487,15 @@ int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
        if (errno == ENOMEM)
                die("Out of memory, getdelim failed");
 
-       /* Restore slopbuf that we moved out of the way before */
+       /*
+        * Restore strbuf invariants; if getdelim left us with a NULL pointer,
+        * we can just re-init, but otherwise we should make sure that our
+        * length is empty, and that the result is NUL-terminated.
+        */
        if (!sb->buf)
                strbuf_init(sb, 0);
+       else
+               strbuf_reset(sb);
        return EOF;
 }
 #else
index f72fd14c2eaded0399b779150ea1565edd7bf47a..7987405313de3a8779e338129af62f9286c9985c 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -386,6 +386,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
index 92502b594d055bbf99fd203175d7dffaf8dfb732..8ac5031ade2e71b2ac1a70a9130b3eef25eebc35 100644 (file)
@@ -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;
index 9bfa65af034fd39cb5bda1cdc1460f6d9b7394df..e4857f53a87d4b8316d53c5f90bc47286179d2c5 100644 (file)
@@ -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];
 };
index 62c4356c50d4a41381336559f4e9af27e520ad0d..90825e17fab5872d2e4c94a3cab84c696eb69248 100644 (file)
@@ -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;
+}
index e06eaa5ebb30e825fd0721c76e7d194b0b854706..7ef3775184e1a54bbbebc5b940977c2b61cb914f 100644 (file)
@@ -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
index db2ef22e8f76a54c851424dd19c1232f6061392c..ec2aa8f6879d68078caeccaff25d89178b9fb902 100755 (executable)
@@ -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.
index f667e7ce2f33dba0c10764f2f62101685d74cf3d..9317ba08582d2a5d06ef5a2a8d8e63c72a146b3b 100644 (file)
@@ -74,6 +74,7 @@ PassEnv GIT_VALGRIND_OPTIONS
 PassEnv GNUPGHOME
 PassEnv ASAN_OPTIONS
 PassEnv GIT_TRACE
+PassEnv GIT_CONFIG_NOSYSTEM
 
 Alias /dumb/ www/
 Alias /auth/dumb/ www/auth/dumb/
index d7ef44b4a261a588874df3b8359d809655286a43..03bd31e9f22a1964551cb07d76c45ce90a3cd17e 100755 (executable)
@@ -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
index 8e22b03cdd132bd54f9db44d362c15d129415651..df3183ea1ab36a46b914333e834093bd50847265 100755 (executable)
@@ -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
        ) &&
index 6767da87cba9589222b033c80b605b832f708baf..d934a2441724332d9e8cebd7679788ee3a660fce 100755 (executable)
@@ -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 &&
@@ -1144,6 +1158,9 @@ test_expect_success 'urlmatch' '
                cookieFile = /tmp/cookie.txt
        EOF
 
+       test_expect_code 1 git config --bool --get-urlmatch doesnt.exist https://good.example.com >actual &&
+       test_must_be_empty actual &&
+
        echo true >expect &&
        git config --bool --get-urlmatch http.SSLverify https://good.example.com >actual &&
        test_cmp expect actual &&
index c623824b4d0c2bfde08a69c242e4c5340b97abd3..9cf91dc6d217f8d92dea0125e37b03f6a6938124 100755 (executable)
@@ -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
index c465abe8e34936db135c136a6831975b2763e623..25ddab4e984877cb9361919a893fce391d2f066a 100755 (executable)
@@ -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
 '
 
index 613d9bfe1bbb153d1c2e647aee26a60e07a6af86..86c2ff255d43d644caf81e6258d29dcea1db8e53 100755 (executable)
@@ -166,11 +166,6 @@ test_expect_success 'relative path when cwd is outside worktree' '
        grep "relative path syntax can.t be used outside working tree." error
 '
 
-test_expect_success 'relative path when startup_info is NULL' '
-       test_must_fail test-match-trees HEAD:./file.txt HEAD:./file.txt 2>error &&
-       grep "BUG: startup_info struct is not initialized." error
-'
-
 test_expect_success '<commit>:file correctly diagnosed after a pathname' '
        test_must_fail git rev-parse file.txt HEAD:file.txt 1>actual 2>error &&
        test_i18ngrep ! "exists on disk" error &&
diff --git a/t/t1515-rev-parse-outside-repo.sh b/t/t1515-rev-parse-outside-repo.sh
new file mode 100755 (executable)
index 0000000..3ec2971
--- /dev/null
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='check that certain rev-parse options work outside repo'
+. ./test-lib.sh
+
+test_expect_success 'set up non-repo directory' '
+       GIT_CEILING_DIRECTORIES=$(pwd) &&
+       export GIT_CEILING_DIRECTORIES &&
+       mkdir non-repo &&
+       cd non-repo &&
+       # confirm that git does not find a repo
+       test_must_fail git rev-parse --git-dir
+'
+
+# Rather than directly test the output of sq-quote directly,
+# make sure the shell can read back a tricky case, since
+# that's what we really care about anyway.
+tricky="really tricky with \\ and \" and '"
+dump_args () {
+       for i in "$@"; do
+               echo "arg: $i"
+       done
+}
+test_expect_success 'rev-parse --sq-quote' '
+       dump_args "$tricky" easy >expect &&
+       eval "dump_args $(git rev-parse --sq-quote "$tricky" easy)" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rev-parse --local-env-vars' '
+       git rev-parse --local-env-vars >actual &&
+       # we do not want to depend on the complete list here,
+       # so just look for something plausible
+       grep ^GIT_DIR actual
+'
+
+test_expect_success 'rev-parse --resolve-git-dir' '
+       git init --separate-git-dir repo dir &&
+       test_must_fail git rev-parse --resolve-git-dir . &&
+       echo "$(pwd)/repo" >expect &&
+       git rev-parse --resolve-git-dir dir/.git >actual &&
+       test_cmp expect actual
+'
+
+test_done
index cbfa41ec61b9f893b4df441e0289a5313ccaf413..3acb9926f2ff7fbf2b367a089b294a4a88e2ea31 100755 (executable)
@@ -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
index 46aadc410bc470d9279ed1fd3ee72583d0963d30..c1379b00c2efc99a700c75f5ce13b2248706cf4b 100755 (executable)
@@ -19,6 +19,8 @@ test_expect_success setup '
        test_commit three &&
        git checkout right &&
        test_commit four &&
+       git checkout --orphan five &&
+       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
index a897248490650ebef1a33a60a1aa9cf4431f06ff..f3e3b6cf2eabf0d57f64d794dba9a6195e8da246 100755 (executable)
@@ -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)" &&
index 4261403cf62542e82d40487e47733f586e106a75..c6a3ccba1b992cff7412bcf8139da7557981de94 100755 (executable)
@@ -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
index 544f9ad50831e29095a9f143bf414e08fed46d9e..d96d0e4c9b517ecb712d643cdd13e08bff066036 100755 (executable)
@@ -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 &&
index 0b521057283bf106da8ce55f25f61c7ec7e3ad35..73a39f2923aa7536ce65ea3ee514805122be79b1 100755 (executable)
@@ -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'
index 2f327b749588af7eda57187cba4d365233a25ef9..0d1fa45d25e59ea100864a76b105b149c6dfcb85 100755 (executable)
@@ -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;
index 6ec607211803d2685b109b7fc0d926d206bbf1d4..94ef5000e787f0898ff6ccb57661c972c765db52 100755 (executable)
@@ -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
 '
 
index 3b99434e3e6f534efe44850fef3d1d77c04f5856..eed2981b96df71c6acdc0f90f8b477561241a9a3 100755 (executable)
@@ -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 &&
index 3b8b7921d60c75c7fa8670cef7049564d92c77b6..447a8ffa3a111dcc6ddec8b6081097f4127d2e59 100755 (executable)
@@ -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
index ed9c91e25b584c2241c238f9c6ffad9a3a327d2c..1a080e782371e2d7ad489c706a54fce1553b9579 100755 (executable)
@@ -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
index f5e63670faa0c5acc38adc17d32ae66b59d66b64..a9773658f09e92578b489b1b5b65fc87599cb8ae 100755 (executable)
@@ -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
 '
index cb82eb7e66b4feb24086407019a812f46b715a34..128ba935371fe7a35b4fb8b80ba361a086be01f7 100755 (executable)
@@ -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 (executable)
index 0000000..e01a8f6
--- /dev/null
@@ -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
index fc2be63e02d85682cd24457ee646c8c626ca967a..899e52d50f0d73e4a755c4f7991364f34912b336 100755 (executable)
@@ -284,6 +284,12 @@ test_expect_success \
      git index-pack test-3.pack &&
      cmp test-3.idx test-3-${packname_3}.idx &&
 
+     cat test-1-${packname_1}.pack >test-4.pack &&
+     rm -f test-4.keep &&
+     git index-pack --keep=why test-4.pack &&
+     cmp test-1-${packname_1}.idx test-4.idx &&
+     test -f test-4.keep &&
+
      :'
 
 test_expect_success 'unpacking with --strict' '
index 04cea97f87784bfb9a9a04fdb7341478e64fd5b6..305ca7a9308685c7d040ac254fd34cebbbc86cc8 100755 (executable)
@@ -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 &&
        (
index e5f83bf5e412bc3304009408a58663d7b94dac02..91a69fc33a590d22cf80e101f9b58b9a0b2d9d48 100755 (executable)
@@ -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' '
@@ -531,6 +533,20 @@ test_expect_success 'shallow fetch with tags does not break the repository' '
                git fsck
        )
 '
+
+test_expect_success 'fetch-pack can fetch a raw sha1' '
+       git init hidden &&
+       (
+               cd hidden &&
+               test_commit 1 &&
+               test_commit 2 &&
+               git update-ref refs/hidden/one HEAD^ &&
+               git config transfer.hiderefs refs/hidden &&
+               git config uploadpack.allowtipsha1inwant true
+       ) &&
+       git fetch-pack hidden $(git -C hidden rev-parse refs/hidden/one)
+'
+
 check_prot_path () {
        cat >expected <<-EOF &&
        Diag: url=$1
index c952d5ef5c4e4d4f3be8759f7aa326a70c36c421..739c089d500f76cbf726f0083062420a51874fc1 100755 (executable)
@@ -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 &&
index 1241146227aead97022ade343152347bc911444c..954d0e43f52375b1ed86a4edb9d274714d3b596d 100755 (executable)
@@ -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
index d75ef0ea2b6eb5596ee97b92529d9800411f543c..51c9669398a01bab0b59174ec6ec1be92c3f21fe 100755 (executable)
@@ -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
index 64146352ae20e9a5abdcda77be514d0f3264c3b8..48e2ab62da7a4293dd647524f75f3590783bcb02 100755 (executable)
@@ -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/t5604-clone-reference.sh b/t/t5604-clone-reference.sh
new file mode 100755 (executable)
index 0000000..4320082
--- /dev/null
@@ -0,0 +1,224 @@
+#!/bin/sh
+#
+# Copyright (C) 2006 Martin Waitz <tali@admingilde.org>
+#
+
+test_description='test clone --reference'
+. ./test-lib.sh
+
+base_dir=$(pwd)
+
+U=$base_dir/UPLOAD_LOG
+
+# create a commit in repo $1 with name $2
+commit_in () {
+       (
+               cd "$1" &&
+               echo "$2" >"$2" &&
+               git add "$2" &&
+               git commit -m "$2"
+       )
+}
+
+# check that there are $2 loose objects in repo $1
+test_objcount () {
+       echo "$2" >expect &&
+       git -C "$1" count-objects >actual.raw &&
+       cut -d' ' -f1 <actual.raw >actual &&
+       test_cmp expect actual
+}
+
+test_expect_success 'preparing first repository' '
+       test_create_repo A &&
+       commit_in A file1
+'
+
+test_expect_success 'preparing second repository' '
+       git clone A B &&
+       commit_in B file2 &&
+       git -C B repack -ad &&
+       git -C B prune
+'
+
+test_expect_success 'cloning with reference (-l -s)' '
+       git clone -l -s --reference B A C
+'
+
+test_expect_success 'existence of info/alternates' '
+       test_line_count = 2 C/.git/objects/info/alternates
+'
+
+test_expect_success 'pulling from reference' '
+       git -C C pull ../B master
+'
+
+test_expect_success 'that reference gets used' '
+       test_objcount C 0
+'
+
+test_expect_success 'cloning with reference (no -l -s)' '
+       GIT_TRACE_PACKET=$U.D git clone --reference B "file://$(pwd)/A" D
+'
+
+test_expect_success 'fetched no objects' '
+       test -s "$U.D" &&
+       ! grep " want" "$U.D"
+'
+
+test_expect_success 'existence of info/alternates' '
+       test_line_count = 1 D/.git/objects/info/alternates
+'
+
+test_expect_success 'pulling from reference' '
+       git -C D pull ../B master
+'
+
+test_expect_success 'that reference gets used' '
+       test_objcount D 0
+'
+
+test_expect_success 'updating origin' '
+       commit_in A file3 &&
+       git -C A repack -ad &&
+       git -C A prune
+'
+
+test_expect_success 'pulling changes from origin' '
+       git -C C pull origin
+'
+
+# the 2 local objects are commit and tree from the merge
+test_expect_success 'that alternate to origin gets used' '
+       test_objcount C 2
+'
+
+test_expect_success 'pulling changes from origin' '
+       git -C D pull origin
+'
+
+# the 5 local objects are expected; file3 blob, commit in A to add it
+# and its tree, and 2 are our tree and the merge commit.
+test_expect_success 'check objects expected to exist locally' '
+       test_objcount D 5
+'
+
+test_expect_success 'preparing alternate repository #1' '
+       test_create_repo F &&
+       commit_in F file1
+'
+
+test_expect_success 'cloning alternate repo #2 and adding changes to repo #1' '
+       git clone F G &&
+       commit_in F file2
+'
+
+test_expect_success 'cloning alternate repo #1, using #2 as reference' '
+       git clone --reference G F H
+'
+
+test_expect_success 'cloning with reference being subset of source (-l -s)' '
+       git clone -l -s --reference A B E
+'
+
+test_expect_success 'cloning with multiple references drops duplicates' '
+       git clone -s --reference B --reference A --reference B A dups &&
+       test_line_count = 2 dups/.git/objects/info/alternates
+'
+
+test_expect_success 'clone with reference from a tagged repository' '
+       (
+               cd A && git tag -a -m tagged HEAD
+       ) &&
+       git clone --reference=A A I
+'
+
+test_expect_success 'prepare branched repository' '
+       git clone A J &&
+       (
+               cd J &&
+               git checkout -b other master^ &&
+               echo other >otherfile &&
+               git add otherfile &&
+               git commit -m other &&
+               git checkout master
+       )
+'
+
+test_expect_success 'fetch with incomplete alternates' '
+       git init K &&
+       echo "$base_dir/A/.git/objects" >K/.git/objects/info/alternates &&
+       (
+               cd K &&
+               git remote add J "file://$base_dir/J" &&
+               GIT_TRACE_PACKET=$U.K git fetch J
+       ) &&
+       master_object=$(cd A && git for-each-ref --format="%(objectname)" refs/heads/master) &&
+       test -s "$U.K" &&
+       ! grep " want $master_object" "$U.K" &&
+       tag_object=$(cd A && git for-each-ref --format="%(objectname)" refs/tags/HEAD) &&
+       ! grep " want $tag_object" "$U.K"
+'
+
+test_expect_success 'clone using repo with gitfile as a reference' '
+       git clone --separate-git-dir=L A M &&
+       git clone --reference=M A N &&
+       echo "$base_dir/L/objects" >expected &&
+       test_cmp expected "$base_dir/N/.git/objects/info/alternates"
+'
+
+test_expect_success 'clone using repo pointed at by gitfile as reference' '
+       git clone --reference=M/.git A O &&
+       echo "$base_dir/L/objects" >expected &&
+       test_cmp expected "$base_dir/O/.git/objects/info/alternates"
+'
+
+test_expect_success 'clone and dissociate from reference' '
+       git init P &&
+       (
+               cd P && test_commit one
+       ) &&
+       git clone P Q &&
+       (
+               cd Q && test_commit two
+       ) &&
+       git clone --no-local --reference=P Q R &&
+       git clone --no-local --reference=P --dissociate Q S &&
+       # removing the reference P would corrupt R but not S
+       rm -fr P &&
+       test_must_fail git -C R fsck &&
+       git -C S fsck
+'
+test_expect_success 'clone, dissociate from partial reference and repack' '
+       rm -fr P Q R &&
+       git init P &&
+       (
+               cd P &&
+               test_commit one &&
+               git repack &&
+               test_commit two &&
+               git repack
+       ) &&
+       git clone --bare P Q &&
+       (
+               cd P &&
+               git checkout -b second &&
+               test_commit three &&
+               git repack
+       ) &&
+       git clone --bare --dissociate --reference=P Q R &&
+       ls R/objects/pack/*.pack >packs.txt &&
+       test_line_count = 1 packs.txt
+'
+
+test_expect_success 'clone, dissociate from alternates' '
+       rm -fr A B C &&
+       test_create_repo A &&
+       commit_in A file1 &&
+       git clone --reference=A A B &&
+       test_line_count = 1 B/.git/objects/info/alternates &&
+       git clone --local --dissociate B C &&
+       ! test -f C/.git/objects/info/alternates &&
+       ( cd C && git fsck )
+'
+
+test_done
diff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh
new file mode 100755 (executable)
index 0000000..3c087e9
--- /dev/null
@@ -0,0 +1,141 @@
+#!/bin/sh
+
+test_description='test local clone'
+. ./test-lib.sh
+
+repo_is_hardlinked() {
+       find "$1/objects" -type f -links 1 >output &&
+       test_line_count = 0 output
+}
+
+test_expect_success 'preparing origin repository' '
+       : >file && git add . && git commit -m1 &&
+       git clone --bare . a.git &&
+       git clone --bare . x &&
+       test "$(cd a.git && git config --bool core.bare)" = true &&
+       test "$(cd x && git config --bool core.bare)" = true &&
+       git bundle create b1.bundle --all &&
+       git bundle create b2.bundle master &&
+       mkdir dir &&
+       cp b1.bundle dir/b3 &&
+       cp b1.bundle b4
+'
+
+test_expect_success 'local clone without .git suffix' '
+       git clone -l -s a b &&
+       (cd b &&
+       test "$(git config --bool core.bare)" = false &&
+       git fetch)
+'
+
+test_expect_success 'local clone with .git suffix' '
+       git clone -l -s a.git c &&
+       (cd c && git fetch)
+'
+
+test_expect_success 'local clone from x' '
+       git clone -l -s x y &&
+       (cd y && git fetch)
+'
+
+test_expect_success 'local clone from x.git that does not exist' '
+       test_must_fail git clone -l -s x.git z
+'
+
+test_expect_success 'With -no-hardlinks, local will make a copy' '
+       git clone --bare --no-hardlinks x w &&
+       ! repo_is_hardlinked w
+'
+
+test_expect_success 'Even without -l, local will make a hardlink' '
+       rm -fr w &&
+       git clone -l --bare x w &&
+       repo_is_hardlinked w
+'
+
+test_expect_success 'local clone of repo with nonexistent ref in HEAD' '
+       echo "ref: refs/heads/nonexistent" > a.git/HEAD &&
+       git clone a d &&
+       (cd d &&
+       git fetch &&
+       test ! -e .git/refs/remotes/origin/HEAD)
+'
+
+test_expect_success 'bundle clone without .bundle suffix' '
+       git clone dir/b3 &&
+       (cd b3 && git fetch)
+'
+
+test_expect_success 'bundle clone with .bundle suffix' '
+       git clone b1.bundle &&
+       (cd b1 && git fetch)
+'
+
+test_expect_success 'bundle clone from b4' '
+       git clone b4 bdl &&
+       (cd bdl && git fetch)
+'
+
+test_expect_success 'bundle clone from b4.bundle that does not exist' '
+       test_must_fail git clone b4.bundle bb
+'
+
+test_expect_success 'bundle clone with nonexistent HEAD' '
+       git clone b2.bundle b2 &&
+       (cd b2 &&
+       git fetch &&
+       test_must_fail git rev-parse --verify refs/heads/master)
+'
+
+test_expect_success 'clone empty repository' '
+       mkdir empty &&
+       (cd empty &&
+        git init &&
+        git config receive.denyCurrentBranch warn) &&
+       git clone empty empty-clone &&
+       test_tick &&
+       (cd empty-clone
+        echo "content" >> foo &&
+        git add foo &&
+        git commit -m "Initial commit" &&
+        git push origin master &&
+        expected=$(git rev-parse master) &&
+        actual=$(git --git-dir=../empty/.git rev-parse master) &&
+        test $actual = $expected)
+'
+
+test_expect_success 'clone empty repository, and then push should not segfault.' '
+       rm -fr empty/ empty-clone/ &&
+       mkdir empty &&
+       (cd empty && git init) &&
+       git clone empty empty-clone &&
+       (cd empty-clone &&
+       test_must_fail git push)
+'
+
+test_expect_success 'cloning non-existent directory fails' '
+       rm -rf does-not-exist &&
+       test_must_fail git clone does-not-exist
+'
+
+test_expect_success 'cloning non-git directory fails' '
+       rm -rf not-a-git-repo not-a-git-repo-clone &&
+       mkdir not-a-git-repo &&
+       test_must_fail git clone not-a-git-repo not-a-git-repo-clone
+'
+
+test_expect_success 'cloning file:// does not hardlink' '
+       git clone --bare file://"$(pwd)"/a non-local &&
+       ! repo_is_hardlinked non-local
+'
+
+test_expect_success 'cloning a local path with --no-local does not hardlink' '
+       git clone --bare --no-local a force-nonlocal &&
+       ! repo_is_hardlinked force-nonlocal
+'
+
+test_expect_success 'cloning locally respects "-u" for fetching refs' '
+       test_must_fail git clone --bare -u false a should_not_work.git
+'
+
+test_done
diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh
new file mode 100755 (executable)
index 0000000..9e24ec8
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='basic clone options'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+       mkdir parent &&
+       (cd parent && git init &&
+        echo one >file && git add file &&
+        git commit -m one)
+
+'
+
+test_expect_success 'clone -o' '
+
+       git clone -o foo parent clone-o &&
+       (cd clone-o && git rev-parse --verify refs/remotes/foo/master)
+
+'
+
+test_expect_success 'redirected clone does not show progress' '
+
+       git clone "file://$(pwd)/parent" clone-redirected >out 2>err &&
+       ! grep % err &&
+       test_i18ngrep ! "Checking connectivity" err
+
+'
+
+test_expect_success 'redirected clone -v does show progress' '
+
+       git clone --progress "file://$(pwd)/parent" clone-redirected-progress \
+               >out 2>err &&
+       grep % err
+
+'
+
+test_done
diff --git a/t/t5607-clone-bundle.sh b/t/t5607-clone-bundle.sh
new file mode 100755 (executable)
index 0000000..348d9b3
--- /dev/null
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+test_description='some bundle related tests'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit initial &&
+       test_tick &&
+       git tag -m tag tag &&
+       test_commit second &&
+       test_commit third &&
+       git tag -d initial &&
+       git tag -d second &&
+       git tag -d third
+'
+
+test_expect_success 'annotated tags can be excluded by rev-list options' '
+       git bundle create bundle --all --since=7.Apr.2005.15:14:00.-0700 &&
+       git ls-remote bundle > output &&
+       grep tag output &&
+       git bundle create bundle --all --since=7.Apr.2005.15:16:00.-0700 &&
+       git ls-remote bundle > output &&
+       ! grep tag output
+'
+
+test_expect_success 'die if bundle file cannot be created' '
+       mkdir adir &&
+       test_must_fail git bundle create adir --all
+'
+
+test_expect_failure 'bundle --stdin' '
+       echo master | git bundle create stdin-bundle.bdl --stdin &&
+       git ls-remote stdin-bundle.bdl >output &&
+       grep master output
+'
+
+test_expect_failure 'bundle --stdin <rev-list options>' '
+       echo master | git bundle create hybrid-bundle.bdl --stdin tag &&
+       git ls-remote hybrid-bundle.bdl >output &&
+       grep master output
+'
+
+test_expect_success 'empty bundle file is rejected' '
+       : >empty-bundle &&
+       test_must_fail git fetch empty-bundle
+'
+
+# This triggers a bug in older versions where the resulting line (with
+# --pretty=oneline) was longer than a 1024-char buffer.
+test_expect_success 'ridiculously long subject in boundary' '
+       : >file4 &&
+       test_tick &&
+       git add file4 &&
+       printf "%01200d\n" 0 | git commit -F - &&
+       test_commit fifth &&
+       git bundle create long-subject-bundle.bdl HEAD^..HEAD &&
+       git bundle list-heads long-subject-bundle.bdl >heads &&
+       test -s heads &&
+       git fetch long-subject-bundle.bdl &&
+       sed -n "/^-/{p;q;}" long-subject-bundle.bdl >boundary &&
+       grep "^-[0-9a-f]\\{40\\} " boundary
+'
+
+test_expect_success 'prerequisites with an empty commit message' '
+       : >file1 &&
+       git add file1 &&
+       test_tick &&
+       git commit --allow-empty-message -m "" &&
+       test_commit file2 &&
+       git bundle create bundle HEAD^.. &&
+       git bundle verify bundle
+'
+
+test_done
diff --git a/t/t5608-clone-2gb.sh b/t/t5608-clone-2gb.sh
new file mode 100755 (executable)
index 0000000..191d6d3
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+test_description='Test cloning a repository larger than 2 gigabyte'
+. ./test-lib.sh
+
+if test -z "$GIT_TEST_CLONE_2GB"
+then
+       say 'Skipping expensive 2GB clone test; enable it with GIT_TEST_CLONE_2GB=t'
+else
+       test_set_prereq CLONE_2GB
+fi
+
+test_expect_success CLONE_2GB 'setup' '
+
+       git config pack.compression 0 &&
+       git config pack.depth 0 &&
+       blobsize=$((100*1024*1024)) &&
+       blobcount=$((2*1024*1024*1024/$blobsize+1)) &&
+       i=1 &&
+       (while test $i -le $blobcount
+        do
+               printf "Generating blob $i/$blobcount\r" >&2 &&
+               printf "blob\nmark :$i\ndata $blobsize\n" &&
+               #test-genrandom $i $blobsize &&
+               printf "%-${blobsize}s" $i &&
+               echo "M 100644 :$i $i" >> commit
+               i=$(($i+1)) ||
+               echo $? > exit-status
+        done &&
+        echo "commit refs/heads/master" &&
+        echo "author A U Thor <author@email.com> 123456789 +0000" &&
+        echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
+        echo "data 5" &&
+        echo ">2gb" &&
+        cat commit) |
+       git fast-import --big-file-threshold=2 &&
+       test ! -f exit-status
+
+'
+
+test_expect_success CLONE_2GB 'clone - bare' '
+
+       git clone --bare --no-hardlinks . clone-bare
+
+'
+
+test_expect_success CLONE_2GB 'clone - with worktree, file:// protocol' '
+
+       git clone "file://$(pwd)" clone-wt
+
+'
+
+test_done
diff --git a/t/t5609-clone-branch.sh b/t/t5609-clone-branch.sh
new file mode 100755 (executable)
index 0000000..6e7a7be
--- /dev/null
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='clone --branch option'
+. ./test-lib.sh
+
+check_HEAD() {
+       echo refs/heads/"$1" >expect &&
+       git symbolic-ref HEAD >actual &&
+       test_cmp expect actual
+}
+
+check_file() {
+       echo "$1" >expect &&
+       test_cmp expect file
+}
+
+test_expect_success 'setup' '
+       mkdir parent &&
+       (cd parent && git init &&
+        echo one >file && git add file && git commit -m one &&
+        git checkout -b two &&
+        echo two >file && git add file && git commit -m two &&
+        git checkout master) &&
+       mkdir empty &&
+       (cd empty && git init)
+'
+
+test_expect_success 'vanilla clone chooses HEAD' '
+       git clone parent clone &&
+       (cd clone &&
+        check_HEAD master &&
+        check_file one
+       )
+'
+
+test_expect_success 'clone -b chooses specified branch' '
+       git clone -b two parent clone-two &&
+       (cd clone-two &&
+        check_HEAD two &&
+        check_file two
+       )
+'
+
+test_expect_success 'clone -b sets up tracking' '
+       (cd clone-two &&
+        echo origin >expect &&
+        git config branch.two.remote >actual &&
+        echo refs/heads/two >>expect &&
+        git config branch.two.merge >>actual &&
+        test_cmp expect actual
+       )
+'
+
+test_expect_success 'clone -b does not munge remotes/origin/HEAD' '
+       (cd clone-two &&
+        echo refs/remotes/origin/master >expect &&
+        git symbolic-ref refs/remotes/origin/HEAD >actual &&
+        test_cmp expect actual
+       )
+'
+
+test_expect_success 'clone -b with bogus branch' '
+       test_must_fail git clone -b bogus parent clone-bogus
+'
+
+test_expect_success 'clone -b not allowed with empty repos' '
+       test_must_fail git clone -b branch empty clone-branch-empty
+'
+
+test_done
diff --git a/t/t5610-clone-detached.sh b/t/t5610-clone-detached.sh
new file mode 100755 (executable)
index 0000000..8b0d607
--- /dev/null
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+test_description='test cloning a repository with detached HEAD'
+. ./test-lib.sh
+
+head_is_detached() {
+       git --git-dir=$1/.git rev-parse --verify HEAD &&
+       test_must_fail git --git-dir=$1/.git symbolic-ref HEAD
+}
+
+test_expect_success 'setup' '
+       echo one >file &&
+       git add file &&
+       git commit -m one &&
+       echo two >file &&
+       git commit -a -m two &&
+       git tag two &&
+       echo three >file &&
+       git commit -a -m three
+'
+
+test_expect_success 'clone repo (detached HEAD points to branch)' '
+       git checkout master^0 &&
+       git clone "file://$PWD" detached-branch
+'
+test_expect_success 'cloned HEAD matches' '
+       echo three >expect &&
+       git --git-dir=detached-branch/.git log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+test_expect_failure 'cloned HEAD is detached' '
+       head_is_detached detached-branch
+'
+
+test_expect_success 'clone repo (detached HEAD points to tag)' '
+       git checkout two^0 &&
+       git clone "file://$PWD" detached-tag
+'
+test_expect_success 'cloned HEAD matches' '
+       echo two >expect &&
+       git --git-dir=detached-tag/.git log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+test_expect_success 'cloned HEAD is detached' '
+       head_is_detached detached-tag
+'
+
+test_expect_success 'clone repo (detached HEAD points to history)' '
+       git checkout two^ &&
+       git clone "file://$PWD" detached-history
+'
+test_expect_success 'cloned HEAD matches' '
+       echo one >expect &&
+       git --git-dir=detached-history/.git log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+test_expect_success 'cloned HEAD is detached' '
+       head_is_detached detached-history
+'
+
+test_expect_success 'clone repo (orphan detached HEAD)' '
+       git checkout master^0 &&
+       echo four >file &&
+       git commit -a -m four &&
+       git clone "file://$PWD" detached-orphan
+'
+test_expect_success 'cloned HEAD matches' '
+       echo four >expect &&
+       git --git-dir=detached-orphan/.git log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+test_expect_success 'cloned HEAD is detached' '
+       head_is_detached detached-orphan
+'
+
+test_done
diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh
new file mode 100755 (executable)
index 0000000..27d730c
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+test_description='tests for git clone -c key=value'
+. ./test-lib.sh
+
+test_expect_success 'clone -c sets config in cloned repo' '
+       rm -rf child &&
+       git clone -c core.foo=bar . child &&
+       echo bar >expect &&
+       git --git-dir=child/.git config core.foo >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone -c can set multi-keys' '
+       rm -rf child &&
+       git clone -c core.foo=bar -c core.foo=baz . child &&
+       { echo bar; echo baz; } >expect &&
+       git --git-dir=child/.git config --get-all core.foo >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone -c without a value is boolean true' '
+       rm -rf child &&
+       git clone -c core.foo . child &&
+       echo true >expect &&
+       git --git-dir=child/.git config --bool core.foo >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone -c config is available during clone' '
+       echo content >file &&
+       git add file &&
+       git commit -m one &&
+       rm -rf child &&
+       git clone -c core.autocrlf . child &&
+       printf "content\\r\\n" >expect &&
+       test_cmp expect child/file
+'
+
+test_done
diff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh
new file mode 100755 (executable)
index 0000000..7ace253
--- /dev/null
@@ -0,0 +1,156 @@
+#!/bin/sh
+
+test_description='test refspec written by clone-command'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       # Make two branches, "master" and "side"
+       echo one >file &&
+       git add file &&
+       git commit -m one &&
+       echo two >file &&
+       git commit -a -m two &&
+       git tag two &&
+       echo three >file &&
+       git commit -a -m three &&
+       git checkout -b side &&
+       echo four >file &&
+       git commit -a -m four &&
+       git checkout master &&
+
+       # default clone
+       git clone . dir_all &&
+
+       # default --single that follows HEAD=master
+       git clone --single-branch . dir_master &&
+
+       # default --single that follows HEAD=side
+       git checkout side &&
+       git clone --single-branch . dir_side &&
+
+       # explicit --single that follows side
+       git checkout master &&
+       git clone --single-branch --branch side . dir_side2 &&
+
+       # default --single with --mirror
+       git clone --single-branch --mirror . dir_mirror &&
+
+       # default --single with --branch and --mirror
+       git clone --single-branch --mirror --branch side . dir_mirror_side &&
+
+       # --single that does not know what branch to follow
+       git checkout two^ &&
+       git clone --single-branch . dir_detached &&
+
+       # explicit --single with tag
+       git clone --single-branch --branch two . dir_tag &&
+
+       # advance both "master" and "side" branches
+       git checkout side &&
+       echo five >file &&
+       git commit -a -m five &&
+       git checkout master &&
+       echo six >file &&
+       git commit -a -m six &&
+
+       # update tag
+       git tag -d two && git tag two
+'
+
+test_expect_success 'by default all branches will be kept updated' '
+       (
+               cd dir_all && git fetch &&
+               git for-each-ref refs/remotes/origin |
+               sed -e "/HEAD$/d" \
+                   -e "s|/remotes/origin/|/heads/|" >../actual
+       ) &&
+       # follow both master and side
+       git for-each-ref refs/heads >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'by default no tags will be kept updated' '
+       (
+               cd dir_all && git fetch &&
+               git for-each-ref refs/tags >../actual
+       ) &&
+       git for-each-ref refs/tags >expect &&
+       test_must_fail test_cmp expect actual
+'
+
+test_expect_success '--single-branch while HEAD pointing at master' '
+       (
+               cd dir_master && git fetch &&
+               git for-each-ref refs/remotes/origin |
+               sed -e "/HEAD$/d" \
+                   -e "s|/remotes/origin/|/heads/|" >../actual
+       ) &&
+       # only follow master
+       git for-each-ref refs/heads/master >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success '--single-branch while HEAD pointing at side' '
+       (
+               cd dir_side && git fetch &&
+               git for-each-ref refs/remotes/origin |
+               sed -e "/HEAD$/d" \
+                   -e "s|/remotes/origin/|/heads/|" >../actual
+       ) &&
+       # only follow side
+       git for-each-ref refs/heads/side >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success '--single-branch with explicit --branch side' '
+       (
+               cd dir_side2 && git fetch &&
+               git for-each-ref refs/remotes/origin |
+               sed -e "/HEAD$/d" \
+                   -e "s|/remotes/origin/|/heads/|" >../actual
+       ) &&
+       # only follow side
+       git for-each-ref refs/heads/side >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success '--single-branch with explicit --branch with tag fetches updated tag' '
+       (
+               cd dir_tag && git fetch &&
+               git for-each-ref refs/tags >../actual
+       ) &&
+       git for-each-ref refs/tags >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success '--single-branch with --mirror' '
+       (
+               cd dir_mirror && git fetch &&
+               git for-each-ref refs > ../actual
+       ) &&
+       git for-each-ref refs >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success '--single-branch with explicit --branch and --mirror' '
+       (
+               cd dir_mirror_side && git fetch &&
+               git for-each-ref refs > ../actual
+       ) &&
+       git for-each-ref refs >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success '--single-branch with detached' '
+       (
+               cd dir_detached && git fetch &&
+               git for-each-ref refs/remotes/origin |
+               sed -e "/HEAD$/d" \
+                   -e "s|/remotes/origin/|/heads/|" >../actual
+       ) &&
+       # nothing
+       >expect &&
+       test_cmp expect actual
+'
+
+test_done
diff --git a/t/t5613-info-alternate.sh b/t/t5613-info-alternate.sh
new file mode 100755 (executable)
index 0000000..9cd2626
--- /dev/null
@@ -0,0 +1,112 @@
+#!/bin/sh
+#
+# Copyright (C) 2006 Martin Waitz <tali@admingilde.org>
+#
+
+test_description='test transitive info/alternate entries'
+. ./test-lib.sh
+
+# test that a file is not reachable in the current repository
+# but that it is after creating a info/alternate entry
+reachable_via() {
+       alternate="$1"
+       file="$2"
+       if git cat-file -e "HEAD:$file"; then return 1; fi
+       echo "$alternate" >> .git/objects/info/alternate
+       git cat-file -e "HEAD:$file"
+}
+
+test_valid_repo() {
+       git fsck --full > fsck.log &&
+       test_line_count = 0 fsck.log
+}
+
+base_dir=$(pwd)
+
+test_expect_success 'preparing first repository' \
+'test_create_repo A && cd A &&
+echo "Hello World" > file1 &&
+git add file1 &&
+git commit -m "Initial commit" file1 &&
+git repack -a -d &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_success 'preparing second repository' \
+'git clone -l -s A B && cd B &&
+echo "foo bar" > file2 &&
+git add file2 &&
+git commit -m "next commit" file2 &&
+git repack -a -d -l &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_success 'preparing third repository' \
+'git clone -l -s B C && cd C &&
+echo "Goodbye, cruel world" > file3 &&
+git add file3 &&
+git commit -m "one more" file3 &&
+git repack -a -d -l &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_success 'creating too deep nesting' \
+'git clone -l -s C D &&
+git clone -l -s D E &&
+git clone -l -s E F &&
+git clone -l -s F G &&
+git clone --bare -l -s G H'
+
+test_expect_success 'invalidity of deepest repository' \
+'cd H && {
+       test_valid_repo
+       test $? -ne 0
+}'
+
+cd "$base_dir"
+
+test_expect_success 'validity of third repository' \
+'cd C &&
+test_valid_repo'
+
+cd "$base_dir"
+
+test_expect_success 'validity of fourth repository' \
+'cd D &&
+test_valid_repo'
+
+cd "$base_dir"
+
+test_expect_success 'breaking of loops' \
+'echo "$base_dir"/B/.git/objects >> "$base_dir"/A/.git/objects/info/alternates&&
+cd C &&
+test_valid_repo'
+
+cd "$base_dir"
+
+test_expect_success 'that info/alternates is necessary' \
+'cd C &&
+rm -f .git/objects/info/alternates &&
+! (test_valid_repo)'
+
+cd "$base_dir"
+
+test_expect_success 'that relative alternate is possible for current dir' \
+'cd C &&
+echo "../../../B/.git/objects" > .git/objects/info/alternates &&
+test_valid_repo'
+
+cd "$base_dir"
+
+test_expect_success \
+    'that relative alternate is only possible for current dir' '
+    cd D &&
+    ! (test_valid_repo)
+'
+
+cd "$base_dir"
+
+test_done
diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh
deleted file mode 100755 (executable)
index 4320082..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-#!/bin/sh
-#
-# Copyright (C) 2006 Martin Waitz <tali@admingilde.org>
-#
-
-test_description='test clone --reference'
-. ./test-lib.sh
-
-base_dir=$(pwd)
-
-U=$base_dir/UPLOAD_LOG
-
-# create a commit in repo $1 with name $2
-commit_in () {
-       (
-               cd "$1" &&
-               echo "$2" >"$2" &&
-               git add "$2" &&
-               git commit -m "$2"
-       )
-}
-
-# check that there are $2 loose objects in repo $1
-test_objcount () {
-       echo "$2" >expect &&
-       git -C "$1" count-objects >actual.raw &&
-       cut -d' ' -f1 <actual.raw >actual &&
-       test_cmp expect actual
-}
-
-test_expect_success 'preparing first repository' '
-       test_create_repo A &&
-       commit_in A file1
-'
-
-test_expect_success 'preparing second repository' '
-       git clone A B &&
-       commit_in B file2 &&
-       git -C B repack -ad &&
-       git -C B prune
-'
-
-test_expect_success 'cloning with reference (-l -s)' '
-       git clone -l -s --reference B A C
-'
-
-test_expect_success 'existence of info/alternates' '
-       test_line_count = 2 C/.git/objects/info/alternates
-'
-
-test_expect_success 'pulling from reference' '
-       git -C C pull ../B master
-'
-
-test_expect_success 'that reference gets used' '
-       test_objcount C 0
-'
-
-test_expect_success 'cloning with reference (no -l -s)' '
-       GIT_TRACE_PACKET=$U.D git clone --reference B "file://$(pwd)/A" D
-'
-
-test_expect_success 'fetched no objects' '
-       test -s "$U.D" &&
-       ! grep " want" "$U.D"
-'
-
-test_expect_success 'existence of info/alternates' '
-       test_line_count = 1 D/.git/objects/info/alternates
-'
-
-test_expect_success 'pulling from reference' '
-       git -C D pull ../B master
-'
-
-test_expect_success 'that reference gets used' '
-       test_objcount D 0
-'
-
-test_expect_success 'updating origin' '
-       commit_in A file3 &&
-       git -C A repack -ad &&
-       git -C A prune
-'
-
-test_expect_success 'pulling changes from origin' '
-       git -C C pull origin
-'
-
-# the 2 local objects are commit and tree from the merge
-test_expect_success 'that alternate to origin gets used' '
-       test_objcount C 2
-'
-
-test_expect_success 'pulling changes from origin' '
-       git -C D pull origin
-'
-
-# the 5 local objects are expected; file3 blob, commit in A to add it
-# and its tree, and 2 are our tree and the merge commit.
-test_expect_success 'check objects expected to exist locally' '
-       test_objcount D 5
-'
-
-test_expect_success 'preparing alternate repository #1' '
-       test_create_repo F &&
-       commit_in F file1
-'
-
-test_expect_success 'cloning alternate repo #2 and adding changes to repo #1' '
-       git clone F G &&
-       commit_in F file2
-'
-
-test_expect_success 'cloning alternate repo #1, using #2 as reference' '
-       git clone --reference G F H
-'
-
-test_expect_success 'cloning with reference being subset of source (-l -s)' '
-       git clone -l -s --reference A B E
-'
-
-test_expect_success 'cloning with multiple references drops duplicates' '
-       git clone -s --reference B --reference A --reference B A dups &&
-       test_line_count = 2 dups/.git/objects/info/alternates
-'
-
-test_expect_success 'clone with reference from a tagged repository' '
-       (
-               cd A && git tag -a -m tagged HEAD
-       ) &&
-       git clone --reference=A A I
-'
-
-test_expect_success 'prepare branched repository' '
-       git clone A J &&
-       (
-               cd J &&
-               git checkout -b other master^ &&
-               echo other >otherfile &&
-               git add otherfile &&
-               git commit -m other &&
-               git checkout master
-       )
-'
-
-test_expect_success 'fetch with incomplete alternates' '
-       git init K &&
-       echo "$base_dir/A/.git/objects" >K/.git/objects/info/alternates &&
-       (
-               cd K &&
-               git remote add J "file://$base_dir/J" &&
-               GIT_TRACE_PACKET=$U.K git fetch J
-       ) &&
-       master_object=$(cd A && git for-each-ref --format="%(objectname)" refs/heads/master) &&
-       test -s "$U.K" &&
-       ! grep " want $master_object" "$U.K" &&
-       tag_object=$(cd A && git for-each-ref --format="%(objectname)" refs/tags/HEAD) &&
-       ! grep " want $tag_object" "$U.K"
-'
-
-test_expect_success 'clone using repo with gitfile as a reference' '
-       git clone --separate-git-dir=L A M &&
-       git clone --reference=M A N &&
-       echo "$base_dir/L/objects" >expected &&
-       test_cmp expected "$base_dir/N/.git/objects/info/alternates"
-'
-
-test_expect_success 'clone using repo pointed at by gitfile as reference' '
-       git clone --reference=M/.git A O &&
-       echo "$base_dir/L/objects" >expected &&
-       test_cmp expected "$base_dir/O/.git/objects/info/alternates"
-'
-
-test_expect_success 'clone and dissociate from reference' '
-       git init P &&
-       (
-               cd P && test_commit one
-       ) &&
-       git clone P Q &&
-       (
-               cd Q && test_commit two
-       ) &&
-       git clone --no-local --reference=P Q R &&
-       git clone --no-local --reference=P --dissociate Q S &&
-       # removing the reference P would corrupt R but not S
-       rm -fr P &&
-       test_must_fail git -C R fsck &&
-       git -C S fsck
-'
-test_expect_success 'clone, dissociate from partial reference and repack' '
-       rm -fr P Q R &&
-       git init P &&
-       (
-               cd P &&
-               test_commit one &&
-               git repack &&
-               test_commit two &&
-               git repack
-       ) &&
-       git clone --bare P Q &&
-       (
-               cd P &&
-               git checkout -b second &&
-               test_commit three &&
-               git repack
-       ) &&
-       git clone --bare --dissociate --reference=P Q R &&
-       ls R/objects/pack/*.pack >packs.txt &&
-       test_line_count = 1 packs.txt
-'
-
-test_expect_success 'clone, dissociate from alternates' '
-       rm -fr A B C &&
-       test_create_repo A &&
-       commit_in A file1 &&
-       git clone --reference=A A B &&
-       test_line_count = 1 B/.git/objects/info/alternates &&
-       git clone --local --dissociate B C &&
-       ! test -f C/.git/objects/info/alternates &&
-       ( cd C && git fsck )
-'
-
-test_done
diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh
deleted file mode 100755 (executable)
index 3c087e9..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-#!/bin/sh
-
-test_description='test local clone'
-. ./test-lib.sh
-
-repo_is_hardlinked() {
-       find "$1/objects" -type f -links 1 >output &&
-       test_line_count = 0 output
-}
-
-test_expect_success 'preparing origin repository' '
-       : >file && git add . && git commit -m1 &&
-       git clone --bare . a.git &&
-       git clone --bare . x &&
-       test "$(cd a.git && git config --bool core.bare)" = true &&
-       test "$(cd x && git config --bool core.bare)" = true &&
-       git bundle create b1.bundle --all &&
-       git bundle create b2.bundle master &&
-       mkdir dir &&
-       cp b1.bundle dir/b3 &&
-       cp b1.bundle b4
-'
-
-test_expect_success 'local clone without .git suffix' '
-       git clone -l -s a b &&
-       (cd b &&
-       test "$(git config --bool core.bare)" = false &&
-       git fetch)
-'
-
-test_expect_success 'local clone with .git suffix' '
-       git clone -l -s a.git c &&
-       (cd c && git fetch)
-'
-
-test_expect_success 'local clone from x' '
-       git clone -l -s x y &&
-       (cd y && git fetch)
-'
-
-test_expect_success 'local clone from x.git that does not exist' '
-       test_must_fail git clone -l -s x.git z
-'
-
-test_expect_success 'With -no-hardlinks, local will make a copy' '
-       git clone --bare --no-hardlinks x w &&
-       ! repo_is_hardlinked w
-'
-
-test_expect_success 'Even without -l, local will make a hardlink' '
-       rm -fr w &&
-       git clone -l --bare x w &&
-       repo_is_hardlinked w
-'
-
-test_expect_success 'local clone of repo with nonexistent ref in HEAD' '
-       echo "ref: refs/heads/nonexistent" > a.git/HEAD &&
-       git clone a d &&
-       (cd d &&
-       git fetch &&
-       test ! -e .git/refs/remotes/origin/HEAD)
-'
-
-test_expect_success 'bundle clone without .bundle suffix' '
-       git clone dir/b3 &&
-       (cd b3 && git fetch)
-'
-
-test_expect_success 'bundle clone with .bundle suffix' '
-       git clone b1.bundle &&
-       (cd b1 && git fetch)
-'
-
-test_expect_success 'bundle clone from b4' '
-       git clone b4 bdl &&
-       (cd bdl && git fetch)
-'
-
-test_expect_success 'bundle clone from b4.bundle that does not exist' '
-       test_must_fail git clone b4.bundle bb
-'
-
-test_expect_success 'bundle clone with nonexistent HEAD' '
-       git clone b2.bundle b2 &&
-       (cd b2 &&
-       git fetch &&
-       test_must_fail git rev-parse --verify refs/heads/master)
-'
-
-test_expect_success 'clone empty repository' '
-       mkdir empty &&
-       (cd empty &&
-        git init &&
-        git config receive.denyCurrentBranch warn) &&
-       git clone empty empty-clone &&
-       test_tick &&
-       (cd empty-clone
-        echo "content" >> foo &&
-        git add foo &&
-        git commit -m "Initial commit" &&
-        git push origin master &&
-        expected=$(git rev-parse master) &&
-        actual=$(git --git-dir=../empty/.git rev-parse master) &&
-        test $actual = $expected)
-'
-
-test_expect_success 'clone empty repository, and then push should not segfault.' '
-       rm -fr empty/ empty-clone/ &&
-       mkdir empty &&
-       (cd empty && git init) &&
-       git clone empty empty-clone &&
-       (cd empty-clone &&
-       test_must_fail git push)
-'
-
-test_expect_success 'cloning non-existent directory fails' '
-       rm -rf does-not-exist &&
-       test_must_fail git clone does-not-exist
-'
-
-test_expect_success 'cloning non-git directory fails' '
-       rm -rf not-a-git-repo not-a-git-repo-clone &&
-       mkdir not-a-git-repo &&
-       test_must_fail git clone not-a-git-repo not-a-git-repo-clone
-'
-
-test_expect_success 'cloning file:// does not hardlink' '
-       git clone --bare file://"$(pwd)"/a non-local &&
-       ! repo_is_hardlinked non-local
-'
-
-test_expect_success 'cloning a local path with --no-local does not hardlink' '
-       git clone --bare --no-local a force-nonlocal &&
-       ! repo_is_hardlinked force-nonlocal
-'
-
-test_expect_success 'cloning locally respects "-u" for fetching refs' '
-       test_must_fail git clone --bare -u false a should_not_work.git
-'
-
-test_done
diff --git a/t/t5702-clone-options.sh b/t/t5702-clone-options.sh
deleted file mode 100755 (executable)
index 9e24ec8..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/sh
-
-test_description='basic clone options'
-. ./test-lib.sh
-
-test_expect_success 'setup' '
-
-       mkdir parent &&
-       (cd parent && git init &&
-        echo one >file && git add file &&
-        git commit -m one)
-
-'
-
-test_expect_success 'clone -o' '
-
-       git clone -o foo parent clone-o &&
-       (cd clone-o && git rev-parse --verify refs/remotes/foo/master)
-
-'
-
-test_expect_success 'redirected clone does not show progress' '
-
-       git clone "file://$(pwd)/parent" clone-redirected >out 2>err &&
-       ! grep % err &&
-       test_i18ngrep ! "Checking connectivity" err
-
-'
-
-test_expect_success 'redirected clone -v does show progress' '
-
-       git clone --progress "file://$(pwd)/parent" clone-redirected-progress \
-               >out 2>err &&
-       grep % err
-
-'
-
-test_done
diff --git a/t/t5704-bundle.sh b/t/t5704-bundle.sh
deleted file mode 100755 (executable)
index 348d9b3..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/bin/sh
-
-test_description='some bundle related tests'
-. ./test-lib.sh
-
-test_expect_success 'setup' '
-       test_commit initial &&
-       test_tick &&
-       git tag -m tag tag &&
-       test_commit second &&
-       test_commit third &&
-       git tag -d initial &&
-       git tag -d second &&
-       git tag -d third
-'
-
-test_expect_success 'annotated tags can be excluded by rev-list options' '
-       git bundle create bundle --all --since=7.Apr.2005.15:14:00.-0700 &&
-       git ls-remote bundle > output &&
-       grep tag output &&
-       git bundle create bundle --all --since=7.Apr.2005.15:16:00.-0700 &&
-       git ls-remote bundle > output &&
-       ! grep tag output
-'
-
-test_expect_success 'die if bundle file cannot be created' '
-       mkdir adir &&
-       test_must_fail git bundle create adir --all
-'
-
-test_expect_failure 'bundle --stdin' '
-       echo master | git bundle create stdin-bundle.bdl --stdin &&
-       git ls-remote stdin-bundle.bdl >output &&
-       grep master output
-'
-
-test_expect_failure 'bundle --stdin <rev-list options>' '
-       echo master | git bundle create hybrid-bundle.bdl --stdin tag &&
-       git ls-remote hybrid-bundle.bdl >output &&
-       grep master output
-'
-
-test_expect_success 'empty bundle file is rejected' '
-       : >empty-bundle &&
-       test_must_fail git fetch empty-bundle
-'
-
-# This triggers a bug in older versions where the resulting line (with
-# --pretty=oneline) was longer than a 1024-char buffer.
-test_expect_success 'ridiculously long subject in boundary' '
-       : >file4 &&
-       test_tick &&
-       git add file4 &&
-       printf "%01200d\n" 0 | git commit -F - &&
-       test_commit fifth &&
-       git bundle create long-subject-bundle.bdl HEAD^..HEAD &&
-       git bundle list-heads long-subject-bundle.bdl >heads &&
-       test -s heads &&
-       git fetch long-subject-bundle.bdl &&
-       sed -n "/^-/{p;q;}" long-subject-bundle.bdl >boundary &&
-       grep "^-[0-9a-f]\\{40\\} " boundary
-'
-
-test_expect_success 'prerequisites with an empty commit message' '
-       : >file1 &&
-       git add file1 &&
-       test_tick &&
-       git commit --allow-empty-message -m "" &&
-       test_commit file2 &&
-       git bundle create bundle HEAD^.. &&
-       git bundle verify bundle
-'
-
-test_done
diff --git a/t/t5705-clone-2gb.sh b/t/t5705-clone-2gb.sh
deleted file mode 100755 (executable)
index 191d6d3..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/bin/sh
-
-test_description='Test cloning a repository larger than 2 gigabyte'
-. ./test-lib.sh
-
-if test -z "$GIT_TEST_CLONE_2GB"
-then
-       say 'Skipping expensive 2GB clone test; enable it with GIT_TEST_CLONE_2GB=t'
-else
-       test_set_prereq CLONE_2GB
-fi
-
-test_expect_success CLONE_2GB 'setup' '
-
-       git config pack.compression 0 &&
-       git config pack.depth 0 &&
-       blobsize=$((100*1024*1024)) &&
-       blobcount=$((2*1024*1024*1024/$blobsize+1)) &&
-       i=1 &&
-       (while test $i -le $blobcount
-        do
-               printf "Generating blob $i/$blobcount\r" >&2 &&
-               printf "blob\nmark :$i\ndata $blobsize\n" &&
-               #test-genrandom $i $blobsize &&
-               printf "%-${blobsize}s" $i &&
-               echo "M 100644 :$i $i" >> commit
-               i=$(($i+1)) ||
-               echo $? > exit-status
-        done &&
-        echo "commit refs/heads/master" &&
-        echo "author A U Thor <author@email.com> 123456789 +0000" &&
-        echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
-        echo "data 5" &&
-        echo ">2gb" &&
-        cat commit) |
-       git fast-import --big-file-threshold=2 &&
-       test ! -f exit-status
-
-'
-
-test_expect_success CLONE_2GB 'clone - bare' '
-
-       git clone --bare --no-hardlinks . clone-bare
-
-'
-
-test_expect_success CLONE_2GB 'clone - with worktree, file:// protocol' '
-
-       git clone "file://$(pwd)" clone-wt
-
-'
-
-test_done
diff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh
deleted file mode 100755 (executable)
index 6e7a7be..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/bin/sh
-
-test_description='clone --branch option'
-. ./test-lib.sh
-
-check_HEAD() {
-       echo refs/heads/"$1" >expect &&
-       git symbolic-ref HEAD >actual &&
-       test_cmp expect actual
-}
-
-check_file() {
-       echo "$1" >expect &&
-       test_cmp expect file
-}
-
-test_expect_success 'setup' '
-       mkdir parent &&
-       (cd parent && git init &&
-        echo one >file && git add file && git commit -m one &&
-        git checkout -b two &&
-        echo two >file && git add file && git commit -m two &&
-        git checkout master) &&
-       mkdir empty &&
-       (cd empty && git init)
-'
-
-test_expect_success 'vanilla clone chooses HEAD' '
-       git clone parent clone &&
-       (cd clone &&
-        check_HEAD master &&
-        check_file one
-       )
-'
-
-test_expect_success 'clone -b chooses specified branch' '
-       git clone -b two parent clone-two &&
-       (cd clone-two &&
-        check_HEAD two &&
-        check_file two
-       )
-'
-
-test_expect_success 'clone -b sets up tracking' '
-       (cd clone-two &&
-        echo origin >expect &&
-        git config branch.two.remote >actual &&
-        echo refs/heads/two >>expect &&
-        git config branch.two.merge >>actual &&
-        test_cmp expect actual
-       )
-'
-
-test_expect_success 'clone -b does not munge remotes/origin/HEAD' '
-       (cd clone-two &&
-        echo refs/remotes/origin/master >expect &&
-        git symbolic-ref refs/remotes/origin/HEAD >actual &&
-        test_cmp expect actual
-       )
-'
-
-test_expect_success 'clone -b with bogus branch' '
-       test_must_fail git clone -b bogus parent clone-bogus
-'
-
-test_expect_success 'clone -b not allowed with empty repos' '
-       test_must_fail git clone -b branch empty clone-branch-empty
-'
-
-test_done
diff --git a/t/t5707-clone-detached.sh b/t/t5707-clone-detached.sh
deleted file mode 100755 (executable)
index 8b0d607..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/bin/sh
-
-test_description='test cloning a repository with detached HEAD'
-. ./test-lib.sh
-
-head_is_detached() {
-       git --git-dir=$1/.git rev-parse --verify HEAD &&
-       test_must_fail git --git-dir=$1/.git symbolic-ref HEAD
-}
-
-test_expect_success 'setup' '
-       echo one >file &&
-       git add file &&
-       git commit -m one &&
-       echo two >file &&
-       git commit -a -m two &&
-       git tag two &&
-       echo three >file &&
-       git commit -a -m three
-'
-
-test_expect_success 'clone repo (detached HEAD points to branch)' '
-       git checkout master^0 &&
-       git clone "file://$PWD" detached-branch
-'
-test_expect_success 'cloned HEAD matches' '
-       echo three >expect &&
-       git --git-dir=detached-branch/.git log -1 --format=%s >actual &&
-       test_cmp expect actual
-'
-test_expect_failure 'cloned HEAD is detached' '
-       head_is_detached detached-branch
-'
-
-test_expect_success 'clone repo (detached HEAD points to tag)' '
-       git checkout two^0 &&
-       git clone "file://$PWD" detached-tag
-'
-test_expect_success 'cloned HEAD matches' '
-       echo two >expect &&
-       git --git-dir=detached-tag/.git log -1 --format=%s >actual &&
-       test_cmp expect actual
-'
-test_expect_success 'cloned HEAD is detached' '
-       head_is_detached detached-tag
-'
-
-test_expect_success 'clone repo (detached HEAD points to history)' '
-       git checkout two^ &&
-       git clone "file://$PWD" detached-history
-'
-test_expect_success 'cloned HEAD matches' '
-       echo one >expect &&
-       git --git-dir=detached-history/.git log -1 --format=%s >actual &&
-       test_cmp expect actual
-'
-test_expect_success 'cloned HEAD is detached' '
-       head_is_detached detached-history
-'
-
-test_expect_success 'clone repo (orphan detached HEAD)' '
-       git checkout master^0 &&
-       echo four >file &&
-       git commit -a -m four &&
-       git clone "file://$PWD" detached-orphan
-'
-test_expect_success 'cloned HEAD matches' '
-       echo four >expect &&
-       git --git-dir=detached-orphan/.git log -1 --format=%s >actual &&
-       test_cmp expect actual
-'
-test_expect_success 'cloned HEAD is detached' '
-       head_is_detached detached-orphan
-'
-
-test_done
diff --git a/t/t5708-clone-config.sh b/t/t5708-clone-config.sh
deleted file mode 100755 (executable)
index 27d730c..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/bin/sh
-
-test_description='tests for git clone -c key=value'
-. ./test-lib.sh
-
-test_expect_success 'clone -c sets config in cloned repo' '
-       rm -rf child &&
-       git clone -c core.foo=bar . child &&
-       echo bar >expect &&
-       git --git-dir=child/.git config core.foo >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'clone -c can set multi-keys' '
-       rm -rf child &&
-       git clone -c core.foo=bar -c core.foo=baz . child &&
-       { echo bar; echo baz; } >expect &&
-       git --git-dir=child/.git config --get-all core.foo >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'clone -c without a value is boolean true' '
-       rm -rf child &&
-       git clone -c core.foo . child &&
-       echo true >expect &&
-       git --git-dir=child/.git config --bool core.foo >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'clone -c config is available during clone' '
-       echo content >file &&
-       git add file &&
-       git commit -m one &&
-       rm -rf child &&
-       git clone -c core.autocrlf . child &&
-       printf "content\\r\\n" >expect &&
-       test_cmp expect child/file
-'
-
-test_done
diff --git a/t/t5709-clone-refspec.sh b/t/t5709-clone-refspec.sh
deleted file mode 100755 (executable)
index 7ace253..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-#!/bin/sh
-
-test_description='test refspec written by clone-command'
-. ./test-lib.sh
-
-test_expect_success 'setup' '
-       # Make two branches, "master" and "side"
-       echo one >file &&
-       git add file &&
-       git commit -m one &&
-       echo two >file &&
-       git commit -a -m two &&
-       git tag two &&
-       echo three >file &&
-       git commit -a -m three &&
-       git checkout -b side &&
-       echo four >file &&
-       git commit -a -m four &&
-       git checkout master &&
-
-       # default clone
-       git clone . dir_all &&
-
-       # default --single that follows HEAD=master
-       git clone --single-branch . dir_master &&
-
-       # default --single that follows HEAD=side
-       git checkout side &&
-       git clone --single-branch . dir_side &&
-
-       # explicit --single that follows side
-       git checkout master &&
-       git clone --single-branch --branch side . dir_side2 &&
-
-       # default --single with --mirror
-       git clone --single-branch --mirror . dir_mirror &&
-
-       # default --single with --branch and --mirror
-       git clone --single-branch --mirror --branch side . dir_mirror_side &&
-
-       # --single that does not know what branch to follow
-       git checkout two^ &&
-       git clone --single-branch . dir_detached &&
-
-       # explicit --single with tag
-       git clone --single-branch --branch two . dir_tag &&
-
-       # advance both "master" and "side" branches
-       git checkout side &&
-       echo five >file &&
-       git commit -a -m five &&
-       git checkout master &&
-       echo six >file &&
-       git commit -a -m six &&
-
-       # update tag
-       git tag -d two && git tag two
-'
-
-test_expect_success 'by default all branches will be kept updated' '
-       (
-               cd dir_all && git fetch &&
-               git for-each-ref refs/remotes/origin |
-               sed -e "/HEAD$/d" \
-                   -e "s|/remotes/origin/|/heads/|" >../actual
-       ) &&
-       # follow both master and side
-       git for-each-ref refs/heads >expect &&
-       test_cmp expect actual
-'
-
-test_expect_success 'by default no tags will be kept updated' '
-       (
-               cd dir_all && git fetch &&
-               git for-each-ref refs/tags >../actual
-       ) &&
-       git for-each-ref refs/tags >expect &&
-       test_must_fail test_cmp expect actual
-'
-
-test_expect_success '--single-branch while HEAD pointing at master' '
-       (
-               cd dir_master && git fetch &&
-               git for-each-ref refs/remotes/origin |
-               sed -e "/HEAD$/d" \
-                   -e "s|/remotes/origin/|/heads/|" >../actual
-       ) &&
-       # only follow master
-       git for-each-ref refs/heads/master >expect &&
-       test_cmp expect actual
-'
-
-test_expect_success '--single-branch while HEAD pointing at side' '
-       (
-               cd dir_side && git fetch &&
-               git for-each-ref refs/remotes/origin |
-               sed -e "/HEAD$/d" \
-                   -e "s|/remotes/origin/|/heads/|" >../actual
-       ) &&
-       # only follow side
-       git for-each-ref refs/heads/side >expect &&
-       test_cmp expect actual
-'
-
-test_expect_success '--single-branch with explicit --branch side' '
-       (
-               cd dir_side2 && git fetch &&
-               git for-each-ref refs/remotes/origin |
-               sed -e "/HEAD$/d" \
-                   -e "s|/remotes/origin/|/heads/|" >../actual
-       ) &&
-       # only follow side
-       git for-each-ref refs/heads/side >expect &&
-       test_cmp expect actual
-'
-
-test_expect_success '--single-branch with explicit --branch with tag fetches updated tag' '
-       (
-               cd dir_tag && git fetch &&
-               git for-each-ref refs/tags >../actual
-       ) &&
-       git for-each-ref refs/tags >expect &&
-       test_cmp expect actual
-'
-
-test_expect_success '--single-branch with --mirror' '
-       (
-               cd dir_mirror && git fetch &&
-               git for-each-ref refs > ../actual
-       ) &&
-       git for-each-ref refs >expect &&
-       test_cmp expect actual
-'
-
-test_expect_success '--single-branch with explicit --branch and --mirror' '
-       (
-               cd dir_mirror_side && git fetch &&
-               git for-each-ref refs > ../actual
-       ) &&
-       git for-each-ref refs >expect &&
-       test_cmp expect actual
-'
-
-test_expect_success '--single-branch with detached' '
-       (
-               cd dir_detached && git fetch &&
-               git for-each-ref refs/remotes/origin |
-               sed -e "/HEAD$/d" \
-                   -e "s|/remotes/origin/|/heads/|" >../actual
-       ) &&
-       # nothing
-       >expect &&
-       test_cmp expect actual
-'
-
-test_done
diff --git a/t/t5710-info-alternate.sh b/t/t5710-info-alternate.sh
deleted file mode 100755 (executable)
index 9cd2626..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-#!/bin/sh
-#
-# Copyright (C) 2006 Martin Waitz <tali@admingilde.org>
-#
-
-test_description='test transitive info/alternate entries'
-. ./test-lib.sh
-
-# test that a file is not reachable in the current repository
-# but that it is after creating a info/alternate entry
-reachable_via() {
-       alternate="$1"
-       file="$2"
-       if git cat-file -e "HEAD:$file"; then return 1; fi
-       echo "$alternate" >> .git/objects/info/alternate
-       git cat-file -e "HEAD:$file"
-}
-
-test_valid_repo() {
-       git fsck --full > fsck.log &&
-       test_line_count = 0 fsck.log
-}
-
-base_dir=$(pwd)
-
-test_expect_success 'preparing first repository' \
-'test_create_repo A && cd A &&
-echo "Hello World" > file1 &&
-git add file1 &&
-git commit -m "Initial commit" file1 &&
-git repack -a -d &&
-git prune'
-
-cd "$base_dir"
-
-test_expect_success 'preparing second repository' \
-'git clone -l -s A B && cd B &&
-echo "foo bar" > file2 &&
-git add file2 &&
-git commit -m "next commit" file2 &&
-git repack -a -d -l &&
-git prune'
-
-cd "$base_dir"
-
-test_expect_success 'preparing third repository' \
-'git clone -l -s B C && cd C &&
-echo "Goodbye, cruel world" > file3 &&
-git add file3 &&
-git commit -m "one more" file3 &&
-git repack -a -d -l &&
-git prune'
-
-cd "$base_dir"
-
-test_expect_success 'creating too deep nesting' \
-'git clone -l -s C D &&
-git clone -l -s D E &&
-git clone -l -s E F &&
-git clone -l -s F G &&
-git clone --bare -l -s G H'
-
-test_expect_success 'invalidity of deepest repository' \
-'cd H && {
-       test_valid_repo
-       test $? -ne 0
-}'
-
-cd "$base_dir"
-
-test_expect_success 'validity of third repository' \
-'cd C &&
-test_valid_repo'
-
-cd "$base_dir"
-
-test_expect_success 'validity of fourth repository' \
-'cd D &&
-test_valid_repo'
-
-cd "$base_dir"
-
-test_expect_success 'breaking of loops' \
-'echo "$base_dir"/B/.git/objects >> "$base_dir"/A/.git/objects/info/alternates&&
-cd C &&
-test_valid_repo'
-
-cd "$base_dir"
-
-test_expect_success 'that info/alternates is necessary' \
-'cd C &&
-rm -f .git/objects/info/alternates &&
-! (test_valid_repo)'
-
-cd "$base_dir"
-
-test_expect_success 'that relative alternate is possible for current dir' \
-'cd C &&
-echo "../../../B/.git/objects" > .git/objects/info/alternates &&
-test_valid_repo'
-
-cd "$base_dir"
-
-test_expect_success \
-    'that relative alternate is only possible for current dir' '
-    cd D &&
-    ! (test_valid_repo)
-'
-
-cd "$base_dir"
-
-test_done
index 66cda17ef342a2791ce7bac873eecfd4cf319faf..20e3e2554a132552b5e398a2c449cedbef6ccb1e 100755 (executable)
@@ -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 &&
index 39b3238da211957eeec36a9c7b911cd5942de6dd..e0c5f44cac0290c6e15c5360b0cc7c1f4d486614 100755 (executable)
@@ -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 &&
index b89cd6b07a3df8f10c89e97ddf5cad7338e065bc..2a0fbb87b1d63f1a07f8a3934e1546c695a73f56 100755 (executable)
@@ -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 &&
index 04c0509c476de87bfb679f1c50556ca4c3a3d615..ef0cbceafe855cfb7b0ce4d1cb5050875d587253 100755 (executable)
@@ -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
        )
 '
 
index 73fc240e8548911c65c4dffc4da0c6ff8ab4f27b..3e692454a719324a7ed3c12238416f45df080efa 100755 (executable)
@@ -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 (executable)
index 0000000..20a3ffe
--- /dev/null
@@ -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
index 10b1452766b613932f66421f6c9bfafecc093177..1c6952d04936b190c9cf42024519273b00747113 100755 (executable)
@@ -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 |
index bcf472bf51999f3fd1a043b5d44601156d5c8049..70afb44271a38024d89d0e266bafa009961508de 100755 (executable)
@@ -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
index cf3469b142d7ab015dca14f4f9898bae784a9e9a..f9b7d79af571ab2c377edeb7d46c1605078a4e02 100755 (executable)
@@ -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 &&
index e1abd1923033617540c915af4372ba42936326d0..f99f674ac795b7b55c5fc678257bd7365c71e62d 100755 (executable)
@@ -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 &&
index e5af4b497646944d0c116f68de5b05bd6dfd7445..fd741f506f4192e56348fcced49b2deabe59b9e7 100755 (executable)
@@ -850,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/t7412-submodule--helper.sh b/t/t7412-submodule--helper.sh
new file mode 100755 (executable)
index 0000000..149d428
--- /dev/null
@@ -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
index 63e04277f99a08b15e12c3392f9e128147180fad..900f7de05a67424c867e0f149e7e7a448111791c 100755 (executable)
@@ -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 &&
index b39e313ac2a7393899e2fbaa5bc860c4d95c68ee..725687d5d5f26d6535b3f26d1fa221a8c567d57c 100755 (executable)
@@ -527,11 +527,6 @@ try_commit_status_combo () {
                test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
-       test_expect_success 'commit' '
-               try_commit "" &&
-               test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
-       '
-
        test_expect_success 'commit --status' '
                try_commit --status &&
                test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
index 302e23826341bce6c3797330b5f2996877dc5baf..85248a14b61d68b61932d595eb0fa8fcaa8506b1 100755 (executable)
@@ -33,9 +33,11 @@ printf '%s\n' 1 2 3 4 5 6 7 8 9 >file
 printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >file.1
 printf '%s\n' 1 2 3 4 '5 X' 6 7 8 9 >file.5
 printf '%s\n' 1 2 3 4 5 6 7 8 '9 X' >file.9
+printf '%s\n' 1 2 3 4 5 6 7 8 '9 Y' >file.9y
 printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >result.1
 printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5
 printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9
+printf '%s\n' 1 2 3 4 5 6 7 8 '9 Z' >result.9z
 >empty
 
 create_merge_msgs () {
@@ -128,6 +130,12 @@ test_expect_success 'setup' '
        git tag c2 &&
        c2=$(git rev-parse HEAD) &&
        git reset --hard "$c0" &&
+       cp file.9y file &&
+       git add file &&
+       test_tick &&
+       git commit -m "commit 7" &&
+       git tag c7 &&
+       git reset --hard "$c0" &&
        cp file.9 file &&
        git add file &&
        test_tick &&
@@ -218,6 +226,26 @@ test_expect_success 'merge c1 with c2' '
        verify_parents $c1 $c2
 '
 
+test_expect_success 'merge --squash c3 with c7' '
+       git reset --hard c3 &&
+       test_must_fail git merge --squash c7 &&
+       cat result.9z >file &&
+       git commit --no-edit -a &&
+
+       {
+               cat <<-EOF
+               Squashed commit of the following:
+
+               $(git show -s c7)
+
+               # Conflicts:
+               #       file
+               EOF
+       } >expect &&
+       git cat-file commit HEAD | sed -e '1,/^$/d' >actual &&
+       test_cmp expect actual
+'
+
 test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 and c3' '
@@ -725,4 +753,14 @@ test_expect_success 'merge detects mod-256 conflicts (resolve)' '
        test_must_fail git merge -s resolve master
 '
 
+test_expect_success 'merge nothing into void' '
+       git init void &&
+       (
+               cd void &&
+               git remote add up .. &&
+               git fetch up &&
+               test_must_fail git merge FETCH_HEAD
+       )
+'
+
 test_done
index 0cb9d11f2171de19b260391017310f0ee792f89b..5d56c3854647b2f2bc2fdf4221b1f76f5d0b0629 100755 (executable)
@@ -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
index 6f12b235b3a9330497e814d5e8f564aec94d0c44..76306cf268a18b7c086cf871f39adf9247f07e32 100755 (executable)
@@ -243,6 +243,70 @@ test_expect_success 'mergetool takes partial path' '
        git reset --hard
 '
 
+test_expect_success 'mergetool delete/delete conflict' '
+       git checkout -b delete-base branch1 &&
+       mkdir -p a/a &&
+       (echo one; echo two; echo 3; echo 4) >a/a/file.txt &&
+       git add a/a/file.txt &&
+       git commit -m"base file" &&
+       git checkout -b move-to-b delete-base &&
+       mkdir -p b/b &&
+       git mv a/a/file.txt b/b/file.txt &&
+       (echo one; echo two; echo 4) >b/b/file.txt &&
+       git commit -a -m"move to b" &&
+       git checkout -b move-to-c delete-base &&
+       mkdir -p c/c &&
+       git mv a/a/file.txt c/c/file.txt &&
+       (echo one; echo two; echo 3) >c/c/file.txt &&
+       git commit -a -m"move to c" &&
+       test_must_fail git merge move-to-b &&
+       echo d | git mergetool a/a/file.txt &&
+       ! test -f a/a/file.txt &&
+       git reset --hard HEAD &&
+       test_must_fail git merge move-to-b &&
+       echo m | git mergetool a/a/file.txt &&
+       test -f b/b/file.txt &&
+       git reset --hard HEAD &&
+       test_must_fail git merge move-to-b &&
+       ! echo a | git mergetool a/a/file.txt &&
+       ! test -f a/a/file.txt &&
+       git reset --hard HEAD
+'
+
+test_expect_success 'mergetool produces no errors when keepBackup is used' '
+       test_config mergetool.keepBackup true &&
+       test_must_fail git merge move-to-b &&
+       : >expect &&
+       echo d | git mergetool a/a/file.txt 2>actual &&
+       test_cmp expect actual &&
+       ! test -d a &&
+       git reset --hard HEAD
+'
+
+test_expect_success 'mergetool honors tempfile config for deleted files' '
+       test_config mergetool.keepTemporaries false &&
+       test_must_fail git merge move-to-b &&
+       echo d | git mergetool a/a/file.txt &&
+       ! test -d a &&
+       git reset --hard HEAD
+'
+
+test_expect_success 'mergetool keeps tempfiles when aborting delete/delete' '
+       test_config mergetool.keepTemporaries true &&
+       test_must_fail git merge move-to-b &&
+       ! (echo a; echo n) | git mergetool a/a/file.txt &&
+       test -d a/a &&
+       cat >expect <<-\EOF &&
+       file_BASE_.txt
+       file_LOCAL_.txt
+       file_REMOTE_.txt
+       EOF
+       ls -1 a/a | sed -e "s/[0-9]*//g" >actual &&
+       test_cmp expect actual &&
+       git clean -fdx &&
+       git reset --hard HEAD
+'
+
 test_expect_success 'deleted vs modified submodule' '
        git checkout -b test6 branch1 &&
        git submodule update -N &&
index b540944408a6feb35f28ff7e18601db7a29f8635..1e72971a165efc127e6bda24fb0e4a3852d3c543 100755 (executable)
@@ -905,6 +905,33 @@ test_expect_success 'inside git repository but with --no-index' '
        )
 '
 
+test_expect_success 'grep --no-index descends into repos, but not .git' '
+       rm -fr non &&
+       mkdir -p non/git &&
+       (
+               GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
+               export GIT_CEILING_DIRECTORIES &&
+               cd non/git &&
+
+               echo magic >file &&
+               git init repo &&
+               (
+                       cd repo &&
+                       echo magic >file &&
+                       git add file &&
+                       git commit -m foo &&
+                       echo magic >.git/file
+               ) &&
+
+               cat >expect <<-\EOF &&
+               file
+               repo/file
+               EOF
+               git grep -l --no-index magic >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_expect_success 'setup double-dash tests' '
 cat >double-dash <<EOF &&
 --
index 6568429753d5f90426ad6acc0703b79e9f7a3284..a9b266f0d3d029229a346fb21c703acb84e4c37d 100755 (executable)
@@ -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
index 4c5f3c9d418bf6f85f1067b2cb1481bf941ff954..25bb60b2814320b628d3d12af88ee1d394a94217 100755 (executable)
@@ -55,6 +55,10 @@ test_expect_success 'empty stream succeeds' '
        git fast-import </dev/null
 '
 
+test_expect_success 'truncated stream complains' '
+       echo "tag foo" | test_must_fail git fast-import
+'
+
 test_expect_success 'A: create pack from stdin' '
        test_tick &&
        cat >input <<-INPUT_END &&
index d708cbf0320abe502d875ae3333eda2501208ca2..432c61d246c938192a2b31f2db35f4f9b320a788 100755 (executable)
@@ -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/t9828-git-p4-map-user.sh b/t/t9828-git-p4-map-user.sh
new file mode 100755 (executable)
index 0000000..e20395c
--- /dev/null
@@ -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
index 109f03e711803251b5b5d06bc42579784a326ba8..4dad7095f10bb324f043ff8132678488b65d4537 100644 (file)
@@ -6,6 +6,8 @@ int main(int ac, char **av)
        unsigned char hash1[20], hash2[20], shifted[20];
        struct tree *one, *two;
 
+       setup_git_directory();
+
        if (get_sha1(av[1], hash1))
                die("cannot parse %s as an object name", av[1]);
        if (get_sha1(av[2], hash2))
index 285f06b7ff262378150be80f2a09b4162ae3a564..3d0313354b3e100fe3624b58c61af7cc7e0f8e7b 100644 (file)
@@ -50,6 +50,8 @@ int main(int argc, char **argv)
        if (argc < 2)
                return 1;
 
+       setup_git_directory();
+
        if (!strcmp(argv[1], "run-twice")) {
                printf("1st\n");
                if (!run_revision_walk())
index b3f6653ffda1a3d6d319752676511307d9cd032b..dc802a07c2225463c2e4ee923b9cf144d57a00cc 100644 (file)
@@ -90,35 +90,32 @@ static void create_pack_file(void)
                "corruption on the remote side.";
        int buffered = -1;
        ssize_t sz;
-       const char *argv[13];
-       int i, arg = 0;
+       int i;
        FILE *pipe_fd;
 
        if (shallow_nr) {
-               argv[arg++] = "--shallow-file";
-               argv[arg++] = "";
+               argv_array_push(&pack_objects.args, "--shallow-file");
+               argv_array_push(&pack_objects.args, "");
        }
-       argv[arg++] = "pack-objects";
-       argv[arg++] = "--revs";
+       argv_array_push(&pack_objects.args, "pack-objects");
+       argv_array_push(&pack_objects.args, "--revs");
        if (use_thin_pack)
-               argv[arg++] = "--thin";
+               argv_array_push(&pack_objects.args, "--thin");
 
-       argv[arg++] = "--stdout";
+       argv_array_push(&pack_objects.args, "--stdout");
        if (shallow_nr)
-               argv[arg++] = "--shallow";
+               argv_array_push(&pack_objects.args, "--shallow");
        if (!no_progress)
-               argv[arg++] = "--progress";
+               argv_array_push(&pack_objects.args, "--progress");
        if (use_ofs_delta)
-               argv[arg++] = "--delta-base-offset";
+               argv_array_push(&pack_objects.args, "--delta-base-offset");
        if (use_include_tag)
-               argv[arg++] = "--include-tag";
-       argv[arg++] = NULL;
+               argv_array_push(&pack_objects.args, "--include-tag");
 
        pack_objects.in = -1;
        pack_objects.out = -1;
        pack_objects.err = -1;
        pack_objects.git_cmd = 1;
-       pack_objects.argv = argv;
 
        if (start_command(&pack_objects))
                die("git upload-pack: unable to fork git-pack-objects");
index ef7486474a1eafb7d5d1344323cd6abf012c31db..1ea2ebe4c00d23885515ac48f43844fdb03f70b9 100644 (file)
@@ -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)
index 63a22c630e521969b08c8ecb1ce9fa3e0f3ff513..13b55aba7441bc84d2c5c075110e9ef798ba18f8 100644 (file)
@@ -301,10 +301,11 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 
                xdl_free_ctx(&xe->xdf2);
                xdl_free_ctx(&xe->xdf1);
+               xdl_free_classifier(&cf);
                return -1;
        }
 
-       if (!(xpp->flags & XDF_HISTOGRAM_DIFF))
+       if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)
                xdl_free_classifier(&cf);
 
        return 0;