Merge branch 'jk/shell-portability'
authorJunio C Hamano <gitster@pobox.com>
Fri, 10 Jun 2016 22:26:04 +0000 (15:26 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 10 Jun 2016 22:26:05 +0000 (15:26 -0700)
test fixes.

* jk/shell-portability:
t5500 & t7403: lose bash-ism "local"
test-lib: add in-shell "env" replacement

325 files changed:
.gitignore
.travis.yml
Documentation/Makefile
Documentation/RelNotes/2.8.4.txt
Documentation/RelNotes/2.9.0.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/diff-config.txt
Documentation/git-cherry-pick.txt
Documentation/git-clone.txt
Documentation/git-commit-tree.txt
Documentation/git-commit.txt
Documentation/git-format-patch.txt
Documentation/git-init.txt
Documentation/git-log.txt
Documentation/git-merge.txt
Documentation/git-p4.txt
Documentation/git-pull.txt
Documentation/git-rebase.txt
Documentation/git-send-email.txt
Documentation/git-submodule.txt
Documentation/git-worktree.txt
Documentation/git.txt
Documentation/gitcredentials.txt
Documentation/githooks.txt
Documentation/lint-gitlink.perl [new file with mode: 0755]
Documentation/merge-options.txt
Documentation/pretty-options.txt
Documentation/technical/api-parse-options.txt
GIT-VERSION-GEN
Makefile
README.md
RelNotes
bisect.c
branch.c
branch.h
builtin/am.c
builtin/apply.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/clone.c
builtin/commit-tree.c
builtin/commit.c
builtin/diff-files.c
builtin/diff-index.c
builtin/diff-tree.c
builtin/diff.c
builtin/fetch.c
builtin/fsck.c
builtin/grep.c
builtin/help.c
builtin/log.c
builtin/mailsplit.c
builtin/merge-file.c
builtin/merge-tree.c
builtin/merge.c
builtin/notes.c
builtin/pack-objects.c
builtin/patch-id.c
builtin/pull.c
builtin/receive-pack.c
builtin/reflog.c
builtin/rm.c
builtin/submodule--helper.c
builtin/tag.c
builtin/update-index.c
builtin/upload-archive.c
builtin/verify-tag.c
builtin/worktree.c
cache-tree.c
cache.h
check-racy.c
combine-diff.c
commit.h
compat/win32/syslog.c
compat/winansi.c
config.c
config.mak.uname
connected.c
contrib/completion/git-completion.bash
contrib/hooks/multimail/CHANGES
contrib/hooks/multimail/README
contrib/hooks/multimail/README.Git
contrib/hooks/multimail/git_multimail.py
convert.c
copy.c
credential-cache--daemon.c
credential.c
diff-no-index.c
diff.c
diff.h
dir.c
dir.h
editor.c
entry.c
environment.c
fast-import.c
fsck.c
git-add--interactive.perl
git-compat-util.h
git-difftool.perl
git-merge-octopus.sh
git-mergetool--lib.sh
git-p4.py
git-rebase--interactive.sh
git-rebase--merge.sh
git-rebase.sh
git-stash.sh
git-submodule.sh
git.spec.in [deleted file]
gpg-interface.c
grep.c
http-backend.c
http-push.c
http.c
http.h
ident.c
list-objects.c
ll-merge.c
log-tree.c
mailmap.c
match-trees.c
merge-recursive.c
mergetools/examdiff [new file with mode: 0644]
mergetools/winmerge
notes.c
parse-options.c
patch-ids.c
patch-ids.h
path.c
perl/Git/SVN.pm
pretty.c
quote.c
quote.h
reachable.c
refs.c
refs/files-backend.c
refs/refs-internal.h
remote-curl.c
rerere.c
rerere.h
revision.c
revision.h
run-command.c
run-command.h
sequencer.c
server-info.c
sha1_file.c
sha1_name.c
strbuf.c
strbuf.h
submodule-config.c
submodule-config.h
submodule.c
submodule.h
t/helper/.gitignore [new file with mode: 0644]
t/helper/test-chmtime.c [new file with mode: 0644]
t/helper/test-config.c [new file with mode: 0644]
t/helper/test-ctype.c [new file with mode: 0644]
t/helper/test-date.c [new file with mode: 0644]
t/helper/test-delta.c [new file with mode: 0644]
t/helper/test-dump-cache-tree.c [new file with mode: 0644]
t/helper/test-dump-split-index.c [new file with mode: 0644]
t/helper/test-dump-untracked-cache.c [new file with mode: 0644]
t/helper/test-fake-ssh.c [new file with mode: 0644]
t/helper/test-genrandom.c [new file with mode: 0644]
t/helper/test-hashmap.c [new file with mode: 0644]
t/helper/test-index-version.c [new file with mode: 0644]
t/helper/test-line-buffer.c [new file with mode: 0644]
t/helper/test-match-trees.c [new file with mode: 0644]
t/helper/test-mergesort.c [new file with mode: 0644]
t/helper/test-mktemp.c [new file with mode: 0644]
t/helper/test-parse-options.c [new file with mode: 0644]
t/helper/test-path-utils.c [new file with mode: 0644]
t/helper/test-prio-queue.c [new file with mode: 0644]
t/helper/test-read-cache.c [new file with mode: 0644]
t/helper/test-regex.c [new file with mode: 0644]
t/helper/test-revision-walking.c [new file with mode: 0644]
t/helper/test-run-command.c [new file with mode: 0644]
t/helper/test-scrap-cache-tree.c [new file with mode: 0644]
t/helper/test-sha1-array.c [new file with mode: 0644]
t/helper/test-sha1.c [new file with mode: 0644]
t/helper/test-sha1.sh [new file with mode: 0755]
t/helper/test-sigchain.c [new file with mode: 0644]
t/helper/test-string-list.c [new file with mode: 0644]
t/helper/test-submodule-config.c [new file with mode: 0644]
t/helper/test-subprocess.c [new file with mode: 0644]
t/helper/test-svn-fe.c [new file with mode: 0644]
t/helper/test-urlmatch-normalization.c [new file with mode: 0644]
t/helper/test-wildmatch.c [new file with mode: 0644]
t/lib-git-p4.sh
t/lib-git-svn.sh
t/lib-gpg.sh
t/lib-httpd/apache.conf
t/perf/p3404-rebase-interactive.sh [new file with mode: 0755]
t/perf/perf-lib.sh
t/t0008-ignores.sh
t/t0027-auto-crlf.sh
t/t0040-parse-options.sh
t/t0060-path-utils.sh
t/t0300-credentials.sh
t/t1300-repo-config.sh
t/t1350-config-hooks-path.sh [new file with mode: 0755]
t/t1410-reflog.sh
t/t1430-bad-ref-name.sh
t/t1500-rev-parse.sh
t/t2025-worktree-add.sh
t/t2300-cd-to-toplevel.sh
t/t3033-merge-toplevel.sh
t/t3402-rebase-merge.sh
t/t3404-rebase-interactive.sh
t/t3412-rebase-root.sh
t/t3419-rebase-patch-id.sh
t/t3421-rebase-topology-linear.sh
t/t3910-mac-os-precompose.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/t4204-patch-id.sh
t/t4213-log-tabexpand.sh [new file with mode: 0755]
t/t5500-fetch-pack.sh
t/t5520-pull.sh
t/t5521-pull-options.sh
t/t5526-fetch-submodules.sh
t/t5550-http-fetch-dumb.sh
t/t5551-http-fetch-smart.sh
t/t5601-clone.sh
t/t5614-clone-submodules.sh [new file with mode: 0755]
t/t6009-rev-list-parent.sh
t/t6010-merge-base.sh
t/t6012-rev-list-simplify.sh
t/t6024-recursive-merge.sh
t/t6026-merge-attr.sh
t/t6029-merge-subtree.sh
t/t6036-recursive-corner-cases.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/t7030-verify-tag.sh
t/t7400-submodule-basic.sh
t/t7406-submodule-update.sh
t/t7501-commit.sh
t/t7507-commit-verbose.sh
t/t7510-signed-commit.sh
t/t7605-merge-resolve.sh
t/t7800-difftool.sh
t/t9100-git-svn-basic.sh
t/t9101-git-svn-props.sh
t/t9102-git-svn-deep-rmdir.sh
t/t9103-git-svn-tracked-directory-removed.sh
t/t9106-git-svn-commit-diff-clobber.sh
t/t9107-git-svn-migrate.sh
t/t9110-git-svn-use-svm-props.sh
t/t9111-git-svn-use-svnsync-props.sh
t/t9120-git-svn-clone-with-percent-escapes.sh
t/t9123-git-svn-rebuild-with-rewriteroot.sh
t/t9153-git-svn-rewrite-uuid.sh
t/t9300-fast-import.sh
t/t9400-git-cvsserver-server.sh
t/t9826-git-p4-keep-empty-commits.sh
t/t9828-git-p4-map-user.sh [new file with mode: 0755]
t/t9829-git-p4-jobs.sh [new file with mode: 0755]
t/test-lib.sh
tag.c
tag.h
test-chmtime.c [deleted file]
test-config.c [deleted file]
test-ctype.c [deleted file]
test-date.c [deleted file]
test-delta.c [deleted file]
test-dump-cache-tree.c [deleted file]
test-dump-split-index.c [deleted file]
test-dump-untracked-cache.c [deleted file]
test-fake-ssh.c [deleted file]
test-genrandom.c [deleted file]
test-hashmap.c [deleted file]
test-index-version.c [deleted file]
test-line-buffer.c [deleted file]
test-match-trees.c [deleted file]
test-mergesort.c [deleted file]
test-mktemp.c [deleted file]
test-parse-options.c [deleted file]
test-path-utils.c [deleted file]
test-prio-queue.c [deleted file]
test-read-cache.c [deleted file]
test-regex.c [deleted file]
test-revision-walking.c [deleted file]
test-run-command.c [deleted file]
test-scrap-cache-tree.c [deleted file]
test-sha1-array.c [deleted file]
test-sha1.c [deleted file]
test-sha1.sh [deleted file]
test-sigchain.c [deleted file]
test-string-list.c [deleted file]
test-submodule-config.c [deleted file]
test-subprocess.c [deleted file]
test-svn-fe.c [deleted file]
test-urlmatch-normalization.c [deleted file]
test-wildmatch.c [deleted file]
transport-helper.c
tree-diff.c
tree-walk.c
tree-walk.h
tree.c
unpack-trees.c
upload-pack.c
usage.c
vcs-svn/line_buffer.c
vcs-svn/sliding_window.c
vcs-svn/svndiff.c
vcs-svn/svndump.c
walker.c
worktree.c
worktree.h
wrap-for-bin.sh
wrapper.c
wt-status.c
wt-status.h
xdiff/xdiff.h
xdiff/xdiffi.c
index 5087ce1eb7e2210e7fc25a9a14ac3f499ad5ac2f..05cb58a3d4ef47295fa8ef02add44a0f0dd90d1f 100644 (file)
 /gitweb/gitweb.cgi
 /gitweb/static/gitweb.js
 /gitweb/static/gitweb.min.*
-/test-chmtime
-/test-ctype
-/test-config
-/test-date
-/test-delta
-/test-dump-cache-tree
-/test-dump-split-index
-/test-dump-untracked-cache
-/test-fake-ssh
-/test-scrap-cache-tree
-/test-genrandom
-/test-hashmap
-/test-index-version
-/test-line-buffer
-/test-match-trees
-/test-mergesort
-/test-mktemp
-/test-parse-options
-/test-path-utils
-/test-prio-queue
-/test-read-cache
-/test-regex
-/test-revision-walking
-/test-run-command
-/test-sha1
-/test-sha1-array
-/test-sigchain
-/test-string-list
-/test-submodule-config
-/test-subprocess
-/test-svn-fe
-/test-urlmatch-normalization
-/test-wildmatch
 /common-cmds.h
 *.tar.gz
 *.dsc
index adab5b89bba7b41a07a2a000dddbd4496ba7c7d3..c20ec540c8488e1d3b589a66a69ba269342159d7 100644 (file)
@@ -18,6 +18,7 @@ addons:
   apt:
     packages:
     - language-pack-is
+    - git-svn
 
 env:
   global:
index 3e39e2815baca82fe5f1fff8791de37c3c7247ac..35c1385ef7e4e5baa822e7dc75b4fc2822aea85f 100644 (file)
@@ -146,7 +146,7 @@ else
        ASCIIDOC_EXTRA += -a git-asciidoc-no-roff
        endif
 endif
-ifdef MAN_BOLD_LITERAL
+ifndef NO_MAN_BOLD_LITERAL
 XMLTO_EXTRA += -m manpage-bold-literal.xsl
 endif
 ifdef DOCBOOK_SUPPRESS_SP
@@ -204,6 +204,7 @@ ifndef V
        QUIET_DBLATEX   = @echo '   ' DBLATEX $@;
        QUIET_XSLTPROC  = @echo '   ' XSLTPROC $@;
        QUIET_GEN       = @echo '   ' GEN $@;
+       QUIET_LINT      = @echo '   ' LINT $@;
        QUIET_STDERR    = 2> /dev/null
        QUIET_SUBDIR0   = +@subdir=
        QUIET_SUBDIR1   = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
@@ -427,4 +428,7 @@ quick-install-html: require-htmlrepo
 print-man1:
        @for i in $(MAN1_TXT); do echo $$i; done
 
+lint-docs::
+       $(QUIET_LINT)$(PERL_PATH) lint-gitlink.perl
+
 .PHONY: FORCE
index 8d484b170ac7c96cfa1366d0f569ea1e2065cabe..f4e255283667e958a5b6d321bb30cc914a3cd94b 100644 (file)
@@ -52,4 +52,18 @@ Fixes since v2.8.3
    result in a strange error message in a pathological corner case.
    This has been corrected to suggest "submodule deinit --all" instead.
 
+ * Many commands normalize command line arguments from NFD to NFC
+   variant of UTF-8 on OSX, but commands in the "diff" family did
+   not, causing "git diff $path" to complain that no such path is
+   known to Git.  They have been taught to do the normalization.
+
+ * A couple of bugs around core.autocrlf have been fixed.
+
+ * "git difftool" learned to handle unmerged paths correctly in
+   dir-diff mode.
+
+ * The "are we talking with TTY, doing an interactive session?"
+   detection has been updated to work better for "Git for Windows".
+
+
 Also contains other minor documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.9.0.txt b/Documentation/RelNotes/2.9.0.txt
new file mode 100644 (file)
index 0000000..927cb9b
--- /dev/null
@@ -0,0 +1,508 @@
+Git 2.9 Release Notes
+=====================
+
+Backward compatibility notes
+----------------------------
+
+The end-user facing Porcelain level commands in the "git diff" and
+"git log" family by default enable the rename detection; you can still
+use "diff.renames" configuration variable to disable this.
+
+Merging two branches that have no common ancestor with "git merge" is
+by default forbidden now to prevent creating such an unusual merge by
+mistake.
+
+The output formats of "git log" that indents the commit log message by
+4 spaces now expands HT in the log message by default.  You can use
+the "--no-expand-tabs" option to disable this.
+
+"git commit-tree" plumbing command required the user to always sign
+its result when the user sets the commit.gpgsign configuration
+variable, which was an ancient mistake, which this release corrects.
+A script that drives commit-tree, if it relies on this mistake, now
+needs to read commit.gpgsign and pass the -S option as necessary.
+
+
+Updates since v2.8
+------------------
+
+UI, Workflows & Features
+
+ * Comes with git-multimail 1.3.1 (in contrib/).
+
+ * The end-user facing commands like "git diff" and "git log"
+   now enable 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" sessions.
+
+ * "git p4" now allows P4 author names to be mapped to Git author
+   names.
+
+ * "git rebase -x" can be used without passing "-i" option.
+
+ * "git -c credential.<var>=<value> submodule" can now be used to
+   propagate configuration variables related to credential helper
+   down to the submodules.
+
+ * "git tag" can create an annotated tag without explicitly given an
+   "-a" (or "-s") option (i.e. when a tag message is given).  A new
+   configuration variable, tag.forceSignAnnotated, can be used to tell
+   the command to create signed tag in such a situation.
+
+ * "git merge" used to allow merging two branches that have no common
+   base by default, which led to a brand new history of an existing
+   project created and then get pulled by an unsuspecting maintainer,
+   which allowed an unnecessary parallel history merged into the
+   existing project.  The command has been taught not to allow this by
+   default, with an escape hatch "--allow-unrelated-histories" option
+   to be used in a rare event that merges histories of two projects
+   that started their lives independently.
+
+ * "git pull" has been taught to pass the "--allow-unrelated-histories"
+   option to underlying "git merge".
+
+ * "git apply -v" learned to report paths in the patch that were
+   skipped via --include/--exclude mechanism or being outside the
+   current working directory.
+
+ * Shell completion (in contrib/) updates.
+
+ * The commit object name reported when "rebase -i" stops has been
+   shortened.
+
+ * "git worktree add" can be given "--no-checkout" option to only
+   create an empty worktree without checking out the files.
+
+ * "git mergetools" learned to drive ExamDiff.
+
+ * "git pull --rebase" learned "--[no-]autostash" option, so that
+   the rebase.autostash configuration variable set to true can be
+   overridden from the command line.
+
+ * When "git log" shows the log message indented by 4-spaces, the
+   remainder of a line after a HT does not align in the way the author
+   originally intended.  The command now expands tabs by default to help
+   such a case, and allows the users to override it with a new option,
+   "--no-expand-tabs".
+
+ * "git send-email" now uses a more readable timestamps when
+   formulating a message ID.
+
+ * "git rerere" can encounter two or more files with the same conflict
+   signature that have to be resolved in different ways, but there was
+   no way to record these separate resolutions.
+
+ * "git p4" learned to record P4 jobs in Git commit that imports from
+   the history in Perforce.
+
+ * "git describe --contains" often made a hard-to-justify choice of
+   tag to name a given commit, because it tried to come up
+   with a name with smallest number of hops from a tag, causing an old
+   commit whose close descendant that is recently tagged were not
+   described with respect to an old tag but with a newer tag.  It did
+   not help that its computation of "hop" count was further tweaked to
+   penalize being on a side branch of a merge.  The logic has been
+   updated to favor using the tag with the oldest tagger date, which
+   is a lot easier to explain to the end users: "We describe a commit
+   in terms of the (chronologically) oldest tag that contains the
+   commit."
+
+ * "git clone" learned the "--shallow-submodules" option.
+
+ * HTTP transport clients learned to throw extra HTTP headers at the
+   server, specified via http.extraHeader configuration variable.
+
+ * Patch output from "git diff" and friends has been tweaked to be
+   more readable by using a blank line as a strong hint that the
+   contents before and after it belong to logically separate units.
+
+ * A new configuration variable core.hooksPath allows customizing
+   where the hook directory is.
+
+ * An earlier addition of "sanitize_submodule_env" with 14111fc4 (git:
+   submodule honor -c credential.* from command line, 2016-02-29)
+   turned out to be a convoluted no-op; implement what it wanted to do
+   correctly, and stop filtering settings given via "git -c var=val".
+
+ * "git commit --dry-run" reported "No, no, you cannot commit." in one
+   case where "git commit" would have allowed you to commit, and this
+   improves it a little bit ("git commit --dry-run --short" still does
+   not give you the correct answer, for example).  This is a stop-gap
+   measure in that "commit --short --dry-run" still gives an incorrect
+   result.
+
+ * The experimental "multiple worktree" feature gains more safety to
+   forbid operations on a branch that is checked out or being actively
+   worked on elsewhere, by noticing that e.g. it is being rebased.
+
+ * "git format-patch" learned a new "--base" option to record what
+   (public, well-known) commit the original series was built on in
+   its output.
+
+ * "git commit" learned to pay attention to the "commit.verbose"
+   configuration variable and act as if the "--verbose" option
+   was given from the command line.
+
+ * Updated documentation gives hints to GMail users with two-factor
+   auth enabled that they need app-specific-password when using
+   "git send-email".
+
+ * The manpage output of our documentation did not render well in
+   terminal; typeset literals in bold by default to make them stand
+   out more.
+
+ * The mark-up in the top-level README.md file has been updated to
+   typeset CLI command names differently from the body text.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * The embedded args argv-array in the child process is used to build
+   the command line to run pack-objects instead of using a separate
+   array of strings.
+
+ * A test for tags has been restructured so that more parts of it can
+   easily be run on a platform without a working GnuPG.
+
+ * The startup_info data, which records if we are working inside a
+   repository (among other things), are now uniformly available to Git
+   subcommand implementations, and Git avoids attempting to touch
+   references when we are not in a repository.
+
+ * The command line argument parser for "receive-pack" has been
+   rewritten to use parse-options.
+
+ * A major part of "git submodule update" has been ported to C to take
+   advantage of the recently added framework to run download tasks in
+   parallel.  Other updates to "git submodule" that move pieces of
+   logic to C continues.
+
+ * Rename bunch of tests on "git clone" for better organization.
+
+ * The tests that involve running httpd leaked the system-wide
+   configuration in /etc/gitconfig to the tested environment.
+
+ * Build updates for MSVC.
+
+ * The repository set-up sequence has been streamlined (the biggest
+   change is that there is no longer git_config_early()), so that we
+   do not attempt to look into refs/* when we know we do not have a
+   Git repository.
+
+ * Code restructuring around the "refs" API to prepare for pluggable
+   refs backends.
+
+ * Sources to many test helper binaries and the generated helpers
+   have been moved to t/helper/ subdirectory to reduce clutter at the
+   top level of the tree.
+
+ * Unify internal logic between "git tag -v" and "git verify-tag"
+   commands by making one directly call into the other.
+
+ * "merge-recursive" strategy incorrectly checked if a path that is
+   involved in its internal merge exists in the working tree.
+
+ * The test scripts for "git p4" (but not "git p4" implementation
+   itself) has been updated so that they would work even on a system
+   where the installed version of Python is python 3.
+
+ * As nobody maintains our in-tree git.spec.in and distros use their
+   own spec file, we stopped pretending that we support "make rpm".
+
+ * Move from "unsigned char[20]" to "struct object_id" continues.
+
+ * The code for warning_errno/die_errno has been refactored and a new
+   error_errno() reporting helper is introduced.
+   (merge 1da045f nd/error-errno later to maint).
+
+ * Running tests with '-x' option to trace the individual command
+   executions is a useful way to debug test scripts, but some tests
+   that capture the standard error stream and check what the command
+   said can be broken with the trace output mixed in.  When running
+   our tests under "bash", however, we can redirect the trace output
+   to another file descriptor to keep the standard error of programs
+   being tested intact.
+
+ * t0040 had too many unnecessary repetitions in its test data.  Teach
+   test-parse-options program so that a caller can tell what it
+   expects in its output, so that these repetitions can be cleaned up.
+
+ * Add perf test for "rebase -i".
+
+ * Common mistakes when writing gitlink: in our documentation are
+   found by "make check-docs".
+
+ * t9xxx series has been updated primarily for readability, while
+   fixing small bugs in it.  A few scripted Porcelain commands have
+   also been updated to fix possible bugs around their use of
+   "test -z" and "test -n".
+
+ * CI test was taught to run git-svn tests.
+
+ * "git cat-file --batch-all" has been sped up, by taking advantage
+   of the fact that it does not have to read a list of objects, in two
+   ways.
+
+ * test updates to make it more readable and maintainable.
+   (merge e6273f4 es/t1500-modernize later to maint).
+
+ * "make DEVELOPER=1" worked as expected; setting DEVELOPER=1 in
+   config.mak didn't.
+   (merge 51dd3e8 mm/makefile-developer-can-be-in-config-mak later to maint).
+
+ * The way how "submodule--helper list" signals unmatch error to its
+   callers has been updated.
+
+ * A bash-ism "local" has been removed from "git submodule" scripted
+   Porcelain.
+
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v2.8
+----------------
+
+Unless otherwise noted, all the fixes since v2.8 in the maintenance
+track are contained in this release (see the maintenance releases'
+notes for details).
+
+ * "git config --get-urlmatch", unlike other variants of the "git
+   config --get" family, did not signal error with its exit status
+   when there was no matching configuration.
+
+ * The "--local-env-vars" and "--resolve-git-dir" options of "git
+   rev-parse" failed to work outside a repository when the command's
+   option parsing was rewritten in 1.8.5 era.
+
+ * "git index-pack --keep[=<msg>] pack-$name.pack" simply did not work.
+
+ * Fetching of history by naming a commit object name directly didn't
+   work across remote-curl transport.
+
+ * A small memory leak in an error codepath has been plugged in xdiff
+   code.
+
+ * strbuf_getwholeline() did not NUL-terminate the buffer on certain
+   corner cases in its error codepath.
+
+ * "git mergetool" did not work well with conflicts that both sides
+   deleted.
+
+ * "git send-email" had trouble parsing alias file in mailrc format
+   when lines in it had trailing whitespaces on them.
+
+ * When "git merge --squash" stopped due to conflict, the concluding
+   "git commit" failed to read in the SQUASH_MSG that shows the log
+   messages from all the squashed commits.
+
+ * "git merge FETCH_HEAD" dereferenced NULL pointer when merging
+   nothing into an unborn history (which is arguably unusual usage,
+   which perhaps was the reason why nobody noticed it).
+
+ * When "git worktree" feature is in use, "git branch -d" allowed
+   deletion of a branch that is checked out in another worktree,
+   which was wrong.
+
+ * When "git worktree" feature is in use, "git branch -m" renamed a
+   branch that is checked out in another worktree without adjusting
+   the HEAD symbolic ref for the worktree.
+
+ * "git diff -M" used to work better when two originally identical
+   files A and B got renamed to X/A and X/B by pairing A to X/A and B
+   to X/B, but this was broken in the 2.0 timeframe.
+
+ * "git send-pack --all <there>" was broken when its command line
+   option parsing was written in the 2.6 timeframe.
+
+ * "git format-patch --help" showed `-s` and `--no-patch` as if these
+   are valid options to the command.  We already hide `--patch` option
+   from the documentation, because format-patch is about showing the
+   diff, and the documentation now hides these options as well.
+
+ * When running "git blame $path" with unnormalized data in the index
+   for the path, the data in the working tree was blamed, even though
+   "git add" would not have changed what is already in the index, due
+   to "safe crlf" that disables the line-end conversion.  It has been
+   corrected.
+
+ * A change back in version 2.7 to "git branch" broke display of a
+   symbolic ref in a non-standard place in the refs/ hierarchy (we
+   expect symbolic refs to appear in refs/remotes/*/HEAD to point at
+   the primary branch the remote has, and as .git/HEAD to point at the
+   branch we locally checked out).
+
+ * A partial rewrite of "git submodule" in the 2.7 timeframe changed
+   the way the gitdir: pointer in the submodules point at the real
+   repository location to use absolute paths by accident.  This has
+   been corrected.
+
+ * "git commit" misbehaved in a few minor ways when an empty message
+   is given via -m '', all of which has been corrected.
+
+ * Support for CRAM-MD5 authentication method in "git imap-send" did
+   not work well.
+
+ * Upcoming OpenSSL 1.1.0 will break compilation by updating a few API
+   elements we use in imap-send, which has been adjusted for the change.
+
+ * The socks5:// proxy support added back in 2.6.4 days was not aware
+   that socks5h:// proxies behave differently from socks5:// proxies.
+
+ * "git config" had a codepath that tried to pass a NULL to
+   printf("%s"), which nobody seems to have noticed.
+
+ * On Cygwin, object creation uses the "create a temporary and then
+   rename it to the final name" pattern, not "create a temporary,
+   hardlink it to the final name and then unlink the temporary"
+   pattern.
+
+   This is necessary to use Git on Windows shared directories, and is
+   already enabled for the MinGW and plain Windows builds.  It also
+   has been used in Cygwin packaged versions of Git for quite a while.
+   See http://thread.gmane.org/gmane.comp.version-control.git/291853
+
+ * "merge-octopus" strategy did not ensure that the index is clean
+   when merge begins.
+
+ * When "git merge" notices that the merge can be resolved purely at
+   the tree level (without having to merge blobs) and the resulting
+   tree happens to already exist in the object store, it forgot to
+   update the index, which left an inconsistent state that would
+   break later operations.
+
+ * "git submodule" reports the paths of submodules the command
+   recurses into, but these paths were incorrectly reported when
+   the command was not run from the root level of the superproject.
+
+ * The "user.useConfigOnly" configuration variable makes it an error
+   if users do not explicitly set user.name and user.email.  However,
+   its check was not done early enough and allowed another error to
+   trigger, reporting that the default value we guessed from the
+   system setting was unusable.  This was a suboptimal end-user
+   experience as we want the users to set user.name/user.email without
+   relying on the auto-detection at all.
+
+ * "git mv old new" did not adjust the path for a submodule that lives
+   as a subdirectory inside old/ directory correctly.
+
+ * "git replace -e" did not honour "core.editor" configuration.
+
+ * "git push" from a corrupt repository that attempts to push a large
+   number of refs deadlocked; the thread to relay rejection notices
+   for these ref updates blocked on writing them to the main thread,
+   after the main thread at the receiving end notices that the push
+   failed and decides not to read these notices and return a failure.
+
+ * mmap emulation on Windows has been optimized and work better without
+   consuming paging store when not needed.
+
+ * A question by "git send-email" to ask the identity of the sender
+   has been updated.
+
+ * UI consistency improvements for "git mergetool".
+
+ * "git rebase -m" could be asked to rebase an entire branch starting
+   from the root, but failed by assuming that there always is a parent
+   commit to the first commit on the branch.
+
+ * Fix a broken "p4 lfs" test.
+
+ * Recent update to Git LFS broke "git p4" by changing the output from
+   its "lfs pointer" subcommand.
+
+ * "git fetch" test t5510 was flaky while running a (forced) automagic
+   garbage collection.
+
+ * Documentation updates to help contributors setting up Travis CI
+   test for their patches.
+
+ * Some multi-byte encoding can have a backslash byte as a later part
+   of one letter, which would confuse "highlight" filter used in
+   gitweb.
+
+ * "git commit-tree" plumbing command required the user to always sign
+   its result when the user sets the commit.gpgsign configuration
+   variable, which was an ancient mistake.  Rework "git rebase" that
+   relied on this mistake so that it reads commit.gpgsign and pass (or
+   not pass) the -S option to "git commit-tree" to keep the end-user
+   expectation the same, while teaching "git commit-tree" to ignore
+   the configuration variable.  This will stop requiring the users to
+   sign commit objects used internally as an implementation detail of
+   "git stash".
+
+ * "http.cookieFile" configuration variable clearly wants a pathname,
+   but we forgot to treat it as such by e.g. applying tilde expansion.
+
+ * Consolidate description of tilde-expansion that is done to
+   configuration variables that take pathname to a single place.
+
+ * Correct faulty recommendation to use "git submodule deinit ." when
+   de-initialising all submodules, which would result in a strange
+   error message in a pathological corner case.
+
+ * Many 'linkgit:<git documentation page>' references were broken,
+   which are all fixed with this.
+
+ * "git rerere" can get confused by conflict markers deliberately left
+   by the inner merge step, because they are indistinguishable from
+   the real conflict markers left by the outermost merge which are
+   what the end user and "rerere" need to look at.  This was fixed by
+   making the conflict markers left by the inner merges a bit longer.
+   (merge 0f9fd5c jc/ll-merge-internal later to maint).
+
+ * CI test was taught to build documentation pages.
+
+ * "git fsck" learned to catch NUL byte in a commit object as
+   potential error and warn.
+
+ * Portability enhancement for "rebase -i" to help platforms whose
+   shell does not like "for i in <empty>" (which is not POSIX-kosher).
+
+ * On Windows, .git and optionally any files whose name starts with a
+   dot are now marked as hidden, with a core.hideDotFiles knob to
+   customize this behaviour.
+
+ * Documentation for "git merge --verify-signatures" has been updated
+   to clarify that the signature of only the commit at the tip is
+   verified.  Also the phrasing used for signature and key validity is
+   adjusted to align with that used by OpenPGP.
+
+ * A couple of bugs around core.autocrlf have been fixed.
+
+ * Many commands normalize command line arguments from NFD to NFC
+   variant of UTF-8 on OSX, but commands in the "diff" family did
+   not, causing "git diff $path" to complain that no such path is
+   known to Git.  They have been taught to do the normalization.
+
+ * "git difftool" learned to handle unmerged paths correctly in
+   dir-diff mode.
+
+ * The "are we talking with TTY, doing an interactive session?"
+   detection has been updated to work better for "Git for Windows".
+
+ * We forgot to add "git log --decorate=auto" to documentation when we
+   added the feature back in v2.1.0 timeframe.
+   (merge 462cbb4 rj/log-decorate-auto later to maint).
+
+ * "git fast-import --export-marks" would overwrite the existing marks
+   file even when it makes a dump from its custom die routine.
+   Prevent it from doing so when we have an import-marks file but
+   haven't finished reading it.
+   (merge f4beed6 fc/fast-import-broken-marks-file later to maint).
+
+ * "git rebase -i", after it fails to auto-resolve the conflict, had
+   an unnecessary call to "git rerere" from its very early days, which
+   was spotted recently; the call has been removed.
+   (merge 7063693 js/rebase-i-dedup-call-to-rerere later to maint).
+
+ * Other minor clean-ups and documentation updates
+   (merge cd82b7a pa/cherry-pick-doc-typo later to maint).
+   (merge 2bb73ae rs/patch-id-use-skip-prefix later to maint).
+   (merge aa20cbc rs/apply-name-terminate later to maint).
index b945d67bc00d9b6fea7ae805c7d0a9e1f5962b1a..2e1b2e486e615981c14f7e591f3f32aa06cdee7a 100644 (file)
@@ -353,9 +353,9 @@ core.quotePath::
 
 core.eol::
        Sets the line ending type to use in the working directory for
-       files that have the `text` property set.  Alternatives are
-       'lf', 'crlf' and 'native', which uses the platform's native
-       line ending.  The default value is `native`.  See
+       files that have the `text` property set when core.autocrlf is false.
+       Alternatives are 'lf', 'crlf' and 'native', which uses the platform's
+       native line ending.  The default value is `native`.  See
        linkgit:gitattributes[5] for more information on end-of-line
        conversion.
 
@@ -633,6 +633,23 @@ core.attributesFile::
        `$XDG_CONFIG_HOME/git/attributes`. If `$XDG_CONFIG_HOME` is either not
        set or empty, `$HOME/.config/git/attributes` is used instead.
 
+core.hooksPath::
+       By default Git will look for your hooks in the
+       '$GIT_DIR/hooks' directory. Set this to different path,
+       e.g. '/etc/git/hooks', and Git will try to find your hooks in
+       that directory, e.g. '/etc/git/hooks/pre-receive' instead of
+       in '$GIT_DIR/hooks/pre-receive'.
++
+The path can be either absolute or relative. A relative path is
+taken as relative to the directory where the hooks are run (see
+the "DESCRIPTION" section of linkgit:githooks[5]).
++
+This configuration variable is useful in cases where you'd like to
+centrally configure your Git hooks instead of configuring them on a
+per-repository basis, or as a more flexible and centralized
+alternative to having an `init.templateDir` where you've changed
+default hooks.
+
 core.editor::
        Commands such as `commit` and `tag` that lets you edit
        messages by launching an editor uses the value of this
@@ -1124,11 +1141,16 @@ commit.template::
        Specify the pathname of a file to use as the template for
        new commit messages.
 
+commit.verbose::
+       A boolean or int to specify the level of verbose with `git commit`.
+       See linkgit:git-commit[1].
+
 credential.helper::
        Specify an external helper to be called when a username or
        password credential is needed; the helper may consult external
-       storage to avoid prompting the user for the credentials. See
-       linkgit:gitcredentials[7] for details.
+       storage to avoid prompting the user for the credentials. Note
+       that multiple helpers may be defined. See linkgit:gitcredentials[7]
+       for details.
 
 credential.useHttpPath::
        When acquiring credentials, consider the "path" component of an http
@@ -1272,6 +1294,10 @@ format.outputDirectory::
        Set a custom directory to store the resulting files instead of the
        current working directory.
 
+format.useAutoBase::
+       A boolean value which lets you enable the `--base=auto` option of
+       format-patch by default.
+
 filter.<driver>.clean::
        The command which is used to convert the content of a worktree
        file to a blob upon checkin.  See linkgit:gitattributes[5] for
@@ -1668,6 +1694,12 @@ http.emptyAuth::
        a username in the URL, as libcurl normally requires a username for
        authentication.
 
+http.extraHeader::
+       Pass an additional HTTP header when communicating with a server.  If
+       more than one such entry exists, all of them are added as extra
+       headers.  To allow overriding the settings inherited from the system
+       config, an empty value will reset the extra headers to the empty list.
+
 http.cookieFile::
        The pathname of a file containing previously stored cookie lines,
        which should be used
@@ -1901,6 +1933,14 @@ interactive.singleKey::
        setting is silently ignored if portable keystroke input
        is not available; requires the Perl module Term::ReadKey.
 
+interactive.diffFilter::
+       When an interactive command (such as `git add --patch`) shows
+       a colorized diff, git will pipe the diff through the shell
+       command defined by this configuration variable. The command may
+       mark up the diff further for human consumption, provided that it
+       retains a one-to-one correspondence with the lines in the
+       original diff. Defaults to disabled (no filtering).
+
 log.abbrevCommit::
        If true, makes linkgit:git-log[1], linkgit:git-show[1], and
        linkgit:git-whatchanged[1] assume `--abbrev-commit`. You may
@@ -1916,7 +1956,10 @@ log.decorate::
        command. If 'short' is specified, the ref name prefixes 'refs/heads/',
        'refs/tags/' and 'refs/remotes/' will not be printed. If 'full' is
        specified, the full ref name (including prefix) will be printed.
-       This is the same as the log commands '--decorate' option.
+       If 'auto' is specified, then if the output is going to a terminal,
+       the ref names are shown as if 'short' were given, otherwise no ref
+       names are shown. This is the same as the '--decorate' option
+       of the `git log`.
 
 log.follow::
        If `true`, `git log` will act as if the `--follow` option was used when
@@ -2748,6 +2791,17 @@ submodule.<name>.ignore::
        "--ignore-submodules" option. The 'git submodule' commands are not
        affected by this setting.
 
+submodule.fetchJobs::
+       Specifies how many submodules are fetched/cloned at the same time.
+       A positive integer allows up to that number of submodules fetched
+       in parallel. A value of 0 will give some reasonable default.
+       If unset, it defaults to 1.
+
+tag.forceSignAnnotated::
+       A boolean to specify whether annotated tags created should be GPG signed.
+       If `--annotate` is specified on the command line, it takes
+       precedence over this option.
+
 tag.sort::
        This variable controls the sort ordering of tags when displayed by
        linkgit:git-tag[1]. Without the "--sort=<value>" option provided, the
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 6154e57238143b968b10deacf3ef6fb9d3ef1b86..c104a594afee2dea57db9d9c88b463513558c455 100644 (file)
@@ -128,7 +128,7 @@ effect to your index in a row.
 
 --allow-empty-message::
        By default, cherry-picking a commit with an empty message will fail.
-       This option overrides that behaviour, allowing commits with empty
+       This option overrides that behavior, allowing commits with empty
        messages to be cherry picked.
 
 --keep-redundant-commits::
index b7c467a001ad47de0bebd8c985df769fe4e63adc..1b15cd7b16620e588d21e1c69a01e2e5d880729e 100644 (file)
@@ -14,8 +14,8 @@ SYNOPSIS
          [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
          [--dissociate] [--separate-git-dir <git dir>]
          [--depth <depth>] [--[no-]single-branch]
-         [--recursive | --recurse-submodules] [--] <repository>
-         [<directory>]
+         [--recursive | --recurse-submodules] [--[no-]shallow-submodules]
+         [--jobs <n>] [--] <repository> [<directory>]
 
 DESCRIPTION
 -----------
@@ -191,7 +191,9 @@ objects from the source repository into a pack in the cloned repository.
        Create a 'shallow' clone with a history truncated to the
        specified number of commits. Implies `--single-branch` unless
        `--no-single-branch` is given to fetch the histories near the
-       tips of all branches.
+       tips of all branches. This implies `--shallow-submodules`. If
+       you want to have a shallow superproject clone, but full submodules,
+       also pass `--no-shallow-submodules`.
 
 --[no-]single-branch::
        Clone only the history leading to the tip of a single branch,
@@ -212,6 +214,9 @@ objects from the source repository into a pack in the cloned repository.
        repository does not have a worktree/checkout (i.e. if any of
        `--no-checkout`/`-n`, `--bare`, or `--mirror` is given)
 
+--[no-]shallow-submodules::
+       All submodules which are cloned will be shallow with a depth of 1.
+
 --separate-git-dir=<git dir>::
        Instead of placing the cloned repository where it is supposed
        to be, place the cloned repository at the specified directory,
@@ -219,6 +224,10 @@ objects from the source repository into a pack in the cloned repository.
        The result is Git repository can be separated from working
        tree.
 
+-j <n>::
+--jobs <n>::
+       The number of submodules fetched at the same time.
+       Defaults to the `submodule.fetchJobs` option.
 
 <repository>::
        The (possibly remote) repository to clone from.  See the
index 48c33d7ed7323c7fc4776ffeb98aad9b41b23744..cb69faab686285050d121b1bd64f509a0b561f3e 100644 (file)
@@ -61,8 +61,8 @@ OPTIONS
        stuck to the option without a space.
 
 --no-gpg-sign::
-       Countermand `commit.gpgSign` configuration variable that is
-       set to force each and every commit to be signed.
+       Do not GPG-sign commit, to countermand a `--gpg-sign` option
+       given earlier on the command line.
 
 
 Commit Information
index 9ec6b3cc17fd776b16f6410cf88c708831d4bca3..d474226eb79b45604884587f400b072c08f4ea2d 100644 (file)
@@ -290,7 +290,8 @@ configuration variable documented in linkgit:git-config[1].
        what changes the commit has.
        Note that this diff output doesn't have its
        lines prefixed with '#'. This diff will not be a part
-       of the commit message.
+       of the commit message. See the `commit.verbose` configuration
+       variable in linkgit:git-config[1].
 +
 If specified twice, show in addition the unified diff between
 what would be committed and the worktree files, i.e. the unstaged
index 6821441d7d7beedac1f5b322248c3012d23bb103..bdeecd59e002b074f32960db2d19d745e21539c4 100644 (file)
@@ -265,6 +265,11 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.
   Output an all-zero hash in each patch's From header instead
   of the hash of the commit.
 
+--base=<commit>::
+       Record the base tree information to identify the state the
+       patch series applies to.  See the BASE TREE INFORMATION section
+       below for details.
+
 --root::
        Treat the revision argument as a <revision range>, even if it
        is just a single commit (that would normally be treated as a
@@ -520,6 +525,61 @@ This should help you to submit patches inline using KMail.
 5. Back in the compose window: add whatever other text you wish to the
    message, complete the addressing and subject fields, and press send.
 
+BASE TREE INFORMATION
+---------------------
+
+The base tree information block is used for maintainers or third party
+testers to know the exact state the patch series applies to. It consists
+of the 'base commit', which is a well-known commit that is part of the
+stable part of the project history everybody else works off of, and zero
+or more 'prerequisite patches', which are well-known patches in flight
+that is not yet part of the 'base commit' that need to be applied on top
+of 'base commit' in topological order before the patches can be applied.
+
+The 'base commit' is shown as "base-commit: " followed by the 40-hex of
+the commit object name.  A 'prerequisite patch' is shown as
+"prerequisite-patch-id: " followed by the 40-hex 'patch id', which can
+be obtained by passing the patch through the `git patch-id --stable`
+command.
+
+Imagine that on top of the public commit P, you applied well-known
+patches X, Y and Z from somebody else, and then built your three-patch
+series A, B, C, the history would be like:
+
+................................................
+---P---X---Y---Z---A---B---C
+................................................
+
+With `git format-patch --base=P -3 C` (or variants thereof, e.g. with
+`--cover-letter` of using `Z..C` instead of `-3 C` to specify the
+range), the base tree information block is shown at the end of the
+first message the command outputs (either the first patch, or the
+cover letter), like this:
+
+------------
+base-commit: P
+prerequisite-patch-id: X
+prerequisite-patch-id: Y
+prerequisite-patch-id: Z
+------------
+
+For non-linear topology, such as
+
+................................................
+---P---X---A---M---C
+    \         /
+     Y---Z---B
+................................................
+
+You can also use `git format-patch --base=P -3 C` to generate patches
+for A, B and C, and the identifiers for P, X, Y, Z are appended at the
+end of the first message.
+
+If set `--base=auto` in cmdline, it will track base commit automatically,
+the base commit will be the merge base of tip commit of the remote-tracking
+branch and revision-range specified in cmdline.
+For a local branch, you need to track a remote branch by `git branch
+--set-upstream-to` before using this option.
 
 EXAMPLES
 --------
index 8174d27efdc13b3f611b4aedda70086f39867d73..6364e5dc45b16dd00737321848b792388af7233a 100644 (file)
@@ -130,7 +130,12 @@ The template directory will be one of the following (in order):
  - the default template directory: `/usr/share/git-core/templates`.
 
 The default template directory includes some directory structure, suggested
-"exclude patterns" (see linkgit:gitignore[5]), and sample hook files (see linkgit:githooks[5]).
+"exclude patterns" (see linkgit:gitignore[5]), and sample hook files.
+
+The sample hooks are all disabled by default, To enable one of the
+sample hooks rename it by removing its `.sample` suffix.
+
+See linkgit:githooks[5] for more general info on hook execution.
 
 EXAMPLES
 --------
index 03f958029ad7a35ec29f111217cd76ba21de9b5e..dec379b3e2bd58c74dba61a2a5207a38e702278c 100644 (file)
@@ -29,12 +29,14 @@ OPTIONS
        (works only for a single file).
 
 --no-decorate::
---decorate[=short|full|no]::
+--decorate[=short|full|auto|no]::
        Print out the ref names of any commits that are shown. If 'short' is
        specified, the ref name prefixes 'refs/heads/', 'refs/tags/' and
        'refs/remotes/' will not be printed. If 'full' is specified, the
-       full ref name (including prefix) will be printed. The default option
-       is 'short'.
+       full ref name (including prefix) will be printed. If 'auto' is
+       specified, then if the output is going to a terminal, the ref names
+       are shown as if 'short' were given, otherwise no ref names are
+       shown. The default option is 'short'.
 
 --source::
        Print out the ref name given on the command line by which each
index 07f7295ec8b603fcbd907e780d4ed1109ac5b358..b758d5556caaeeb0a439d67c46100e5fa0d34cd4 100644 (file)
@@ -11,6 +11,7 @@ SYNOPSIS
 [verse]
 'git merge' [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
        [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
+       [--[no-]allow-unrelated-histories]
        [--[no-]rerere-autoupdate] [-m <msg>] [<commit>...]
 'git merge' <msg> HEAD <commit>...
 'git merge' --abort
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 771a7b5b09157cc1ac19d1f998600f96f1f9a470..a88d18604a5b79b27745ffd0552899e7487acebc 100644 (file)
@@ -450,6 +450,19 @@ edit ~/.gitconfig to specify your account settings:
                smtpUser = yourname@gmail.com
                smtpServerPort = 587
 
+If you have multifactor authentication setup on your gmail account, you will
+need to generate an app-specific password for use with 'git send-email'. Visit
+https://security.google.com/settings/security/apppasswords to setup an
+app-specific password.  Once setup, you can store it with the credentials
+helper:
+
+       $ git credential fill
+       protocol=smtp
+       host=smtp.gmail.com
+       username=youname@gmail.com
+       password=app-password
+
+
 Once your commits are ready to be sent to the mailing list, run the
 following commands:
 
index ad851838cc96df3e6b920c83f62a02061b8cae80..9226c4380c6c147108af9d38d7f7101c732a2095 100644 (file)
@@ -16,7 +16,7 @@ SYNOPSIS
 'git submodule' [--quiet] deinit [-f|--force] (--all|[--] <path>...)
 'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch]
              [-f|--force] [--rebase|--merge] [--reference <repository>]
-             [--depth <depth>] [--recursive] [--] [<path>...]
+             [--depth <depth>] [--recursive] [--jobs <n>] [--] [<path>...]
 'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]
              [commit] [--] [<path>...]
 'git submodule' [--quiet] foreach [--recursive] <command>
@@ -384,6 +384,11 @@ for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully.
        clone with a history truncated to the specified number of revisions.
        See linkgit:git-clone[1]
 
+-j <n>::
+--jobs <n>::
+       This option is only valid for the update command.
+       Clone new submodules in parallel with as many jobs.
+       Defaults to the `submodule.fetchJobs` option.
 
 <path>...::
        Paths to submodule(s). When specified this will restrict the command
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 dd6dbf7dd909bb14300706bfdc7ea83a164998d2..5a10e9b4a06cbf34ff3d15032fe7f7d13ed0c3ba 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.3/git.html[documentation for release 2.8.3]
+* link:v2.8.4/git.html[documentation for release 2.8.4]
 
 * release notes for
+  link:RelNotes/2.8.4.txt[2.8.4],
   link:RelNotes/2.8.3.txt[2.8.3],
   link:RelNotes/2.8.2.txt[2.8.2],
   link:RelNotes/2.8.1.txt[2.8.1],
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 a2f59b194c85f5dee4eaa530a7ab6d7533f126c6..d82e912e550ee0032c20a46c4238f7ce10003657 100644 (file)
@@ -7,24 +7,35 @@ githooks - Hooks used by Git
 
 SYNOPSIS
 --------
-$GIT_DIR/hooks/*
+$GIT_DIR/hooks/* (or \`git config core.hooksPath`/*)
 
 
 DESCRIPTION
 -----------
 
-Hooks are little scripts you can place in `$GIT_DIR/hooks`
-directory to trigger action at certain points.  When
-'git init' is run, a handful of example hooks are copied into the
-`hooks` directory of the new repository, but by default they are
-all disabled.  To enable a hook, rename it by removing its `.sample`
-suffix.
+Hooks are programs you can place in a hooks directory to trigger
+actions at certain points in git's execution. Hooks that don't have
+the executable bit set are ignored.
 
-NOTE: It is also a requirement for a given hook to be executable.
-However - in a freshly initialized repository - the `.sample` files are
-executable by default.
+By default the hooks directory is `$GIT_DIR/hooks`, but that can be
+changed via the `core.hooksPath` configuration variable (see
+linkgit:git-config[1]).
 
-This document describes the currently defined hooks.
+Before Git invokes a hook, it changes its working directory to either
+the root of the working tree in a non-bare repository, or to the
+$GIT_DIR in a bare repository.
+
+Hooks can get their arguments via the environment, command-line
+arguments, and stdin. See the documentation for each hook below for
+details.
+
+'git init' may copy hooks to the new repository, depending on its
+configuration. See the "TEMPLATE DIRECTORY" section in
+linkgit:git-init[1] for details. When the rest of this document refers
+to "default hooks" it's talking about the default template shipped
+with Git.
+
+The currently supported hooks are described below.
 
 HOOKS
 -----
@@ -32,15 +43,15 @@ HOOKS
 applypatch-msg
 ~~~~~~~~~~~~~~
 
-This hook is invoked by 'git am' script.  It takes a single
+This hook is invoked by 'git am'.  It takes a single
 parameter, the name of the file that holds the proposed commit
-log message.  Exiting with non-zero status causes
-'git am' to abort before applying the patch.
+log message.  Exiting with a non-zero status causes 'git am' to abort
+before applying the patch.
 
 The hook is allowed to edit the message file in place, and can
 be used to normalize the message into some project standard
-format (if the project has one). It can also be used to refuse
-the commit after inspecting the message file.
+format. It can also be used to refuse the commit after inspecting
+the message file.
 
 The default 'applypatch-msg' hook, when enabled, runs the
 'commit-msg' hook, if the latter is enabled.
@@ -73,10 +84,10 @@ pre-commit
 ~~~~~~~~~~
 
 This hook is invoked by 'git commit', and can be bypassed
-with `--no-verify` option.  It takes no parameter, and is
+with the `--no-verify` option.  It takes no parameters, and is
 invoked before obtaining the proposed commit log message and
-making a commit.  Exiting with non-zero status from this script
-causes the 'git commit' to abort.
+making a commit.  Exiting with non-zero status from this script
+causes the 'git commit' command to abort before creating a commit.
 
 The default 'pre-commit' hook, when enabled, catches introduction
 of lines with trailing whitespaces and aborts the commit when
@@ -115,15 +126,15 @@ commit-msg
 ~~~~~~~~~~
 
 This hook is invoked by 'git commit', and can be bypassed
-with `--no-verify` option.  It takes a single parameter, the
+with the `--no-verify` option.  It takes a single parameter, the
 name of the file that holds the proposed commit log message.
-Exiting with non-zero status causes the 'git commit' to
+Exiting with non-zero status causes the 'git commit' to
 abort.
 
-The hook is allowed to edit the message file in place, and can
-be used to normalize the message into some project standard
-format (if the project has one). It can also be used to refuse
-the commit after inspecting the message file.
+The hook is allowed to edit the message file in place, and can be used
+to normalize the message into some project standard format. It
+can also be used to refuse the commit after inspecting the message
+file.
 
 The default 'commit-msg' hook, when enabled, detects duplicate
 "Signed-off-by" lines, and aborts the commit if one is found.
@@ -131,8 +142,8 @@ The default 'commit-msg' hook, when enabled, detects duplicate
 post-commit
 ~~~~~~~~~~~
 
-This hook is invoked by 'git commit'.  It takes no
-parameter, and is invoked after a commit is made.
+This hook is invoked by 'git commit'. It takes no parameters, and is
+invoked after a commit is made.
 
 This hook is meant primarily for notification, and cannot affect
 the outcome of 'git commit'.
@@ -267,9 +278,11 @@ does not know the entire set of branches, so it would end up
 firing one e-mail per ref when used naively, though.  The
 <<post-receive,'post-receive'>> hook is more suited to that.
 
-Another use suggested on the mailing list is to use this hook to
-implement access control which is finer grained than the one
-based on filesystem group.
+In an environment that restricts the users' access only to git
+commands over the wire, this hook can be used to implement access
+control without relying on filesystem ownership and group
+membership. See linkgit:git-shell[1] for how you might use the login
+shell to restrict the user's access to only git commands.
 
 Both standard output and standard error output are forwarded to
 'git send-pack' on the other end, so you can simply `echo` messages
diff --git a/Documentation/lint-gitlink.perl b/Documentation/lint-gitlink.perl
new file mode 100755 (executable)
index 0000000..476cc30
--- /dev/null
@@ -0,0 +1,71 @@
+#!/usr/bin/perl
+
+use File::Find;
+use Getopt::Long;
+
+my $basedir = ".";
+GetOptions("basedir=s" => \$basedir)
+       or die("Cannot parse command line arguments\n");
+
+my $found_errors = 0;
+
+sub report {
+       my ($where, $what, $error) = @_;
+       print "$where: $error: $what\n";
+       $found_errors = 1;
+}
+
+sub grab_section {
+       my ($page) = @_;
+       open my $fh, "<", "$basedir/$page.txt";
+       my $firstline = <$fh>;
+       chomp $firstline;
+       close $fh;
+       my ($section) = ($firstline =~ /.*\((\d)\)$/);
+       return $section;
+}
+
+sub lint {
+       my ($file) = @_;
+       open my $fh, "<", $file
+               or return;
+       while (<$fh>) {
+               my $where = "$file:$.";
+               while (s/linkgit:((.*?)\[(\d)\])//) {
+                       my ($target, $page, $section) = ($1, $2, $3);
+
+                       # De-AsciiDoc
+                       $page =~ s/{litdd}/--/g;
+
+                       if ($page !~ /^git/) {
+                               report($where, $target, "nongit link");
+                               next;
+                       }
+                       if (! -f "$basedir/$page.txt") {
+                               report($where, $target, "no such source");
+                               next;
+                       }
+                       $real_section = grab_section($page);
+                       if ($real_section != $section) {
+                               report($where, $target,
+                                       "wrong section (should be $real_section)");
+                               next;
+                       }
+               }
+       }
+       close $fh;
+}
+
+sub lint_it {
+       lint($File::Find::name) if -f && /\.txt$/;
+}
+
+if (!@ARGV) {
+       find({ wanted => \&lint_it, no_chdir => 1 }, $basedir);
+} else {
+       for (@ARGV) {
+               lint($_);
+       }
+}
+
+exit $found_errors;
index 30808a01e7657164618c18182704768e2df0d37e..5b4a62e93624bc8289c835631aab5d5f13dff708 100644 (file)
@@ -117,3 +117,11 @@ ifndef::git-pull[]
        reporting.
 
 endif::git-pull[]
+
+--allow-unrelated-histories::
+       By default, `git merge` command refuses to merge histories
+       that do not share a common ancestor.  This option can be
+       used to override this safety when merging histories of two
+       projects that started their lives independently. As that is
+       a very rare occasion, no configuration variable to enable
+       this by default exists and will not be added.
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 695bd4bf43255e42ef26d29a5c6dff35217f220d..27bd701c0d6862f38c700fbfc02c24b135bd6c0e 100644 (file)
@@ -144,8 +144,12 @@ There are some macros to easily define options:
 
 `OPT_COUNTUP(short, long, &int_var, description)`::
        Introduce a count-up option.
-       `int_var` is incremented on each use of `--option`, and
-       reset to zero with `--no-option`.
+       Each use of `--option` increments `int_var`, starting from zero
+       (even if initially negative), and `--no-option` resets it to
+       zero. To determine if `--option` or `--no-option` was encountered at
+       all, initialize `int_var` to a negative value, and if it is still
+       negative after parse_options(), then neither `--option` nor
+       `--no-option` was seen.
 
 `OPT_BIT(short, long, &int_var, description, mask)`::
        Introduce a boolean option.
index 596965a4de6c3d965a6967a2ed94eb79eb762b5f..e1d8231c7e0e9ad924afbebdecac9b6cd9095d77 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.8.3
+DEF_VER=v2.9.0-rc2
 
 LF='
 '
index e11e626d05213f4734815f03f8077ffae6793a51..de5a0302565899a90482631e2267f3c413f347d4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -375,13 +375,7 @@ GIT-VERSION-FILE: FORCE
 # CFLAGS and LDFLAGS are for the users to override from the command line.
 
 CFLAGS = -g -O2 -Wall
-LDFLAGS =
-ALL_CFLAGS = $(CPPFLAGS) $(CFLAGS)
-ALL_LDFLAGS = $(LDFLAGS)
-STRIP ?= strip
-
-ifdef DEVELOPER
-CFLAGS += -Werror \
+DEVELOPER_CFLAGS = -Werror \
        -Wdeclaration-after-statement \
        -Wno-format-zero-length \
        -Wold-style-definition \
@@ -390,7 +384,10 @@ CFLAGS += -Werror \
        -Wstrict-prototypes \
        -Wunused \
        -Wvla
-endif
+LDFLAGS =
+ALL_CFLAGS = $(CPPFLAGS) $(CFLAGS)
+ALL_LDFLAGS = $(LDFLAGS)
+STRIP ?= strip
 
 # Create as necessary, replace existing, make ranlib unneeded.
 ARFLAGS = rcs
@@ -440,7 +437,6 @@ DIFF = diff
 TAR = tar
 FIND = find
 INSTALL = install
-RPMBUILD = rpmbuild
 TCL_PATH = tclsh
 TCLTK_PATH = wish
 XGETTEXT = xgettext
@@ -621,7 +617,7 @@ TEST_PROGRAMS_NEED_X += test-svn-fe
 TEST_PROGRAMS_NEED_X += test-urlmatch-normalization
 TEST_PROGRAMS_NEED_X += test-wildmatch
 
-TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
+TEST_PROGRAMS = $(patsubst %,t/helper/%$X,$(TEST_PROGRAMS_NEED_X))
 
 # List built-in command $C whose implementation cmd_$C() is not in
 # builtin/$C.o but is linked in as part of some other command.
@@ -952,6 +948,10 @@ include config.mak.uname
 -include config.mak.autogen
 -include config.mak
 
+ifdef DEVELOPER
+CFLAGS += $(DEVELOPER_CFLAGS)
+endif
+
 ifndef sysconfdir
 ifeq ($(prefix),/usr)
 sysconfdir = /etc
@@ -1898,7 +1898,7 @@ VCSSVN_OBJS += vcs-svn/fast_export.o
 VCSSVN_OBJS += vcs-svn/svndiff.o
 VCSSVN_OBJS += vcs-svn/svndump.o
 
-TEST_OBJS := $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
+TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS))
 OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
        $(XDIFF_OBJS) \
        $(VCSSVN_OBJS) \
@@ -2205,7 +2205,7 @@ bin-wrappers/%: wrap-for-bin.sh
        @mkdir -p bin-wrappers
        $(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
             -e 's|@@BUILD_DIR@@|$(shell pwd)|' \
-            -e 's|@@PROG@@|$(@F)|' < $< > $@ && \
+            -e 's|@@PROG@@|$(patsubst test-%,t/helper/test-%,$(@F))|' < $< > $@ && \
        chmod +x $@
 
 # GNU make supports exporting all variables by "export" without parameters.
@@ -2225,25 +2225,25 @@ perf: all
 
 .PHONY: test perf
 
-test-ctype$X: ctype.o
+t/helper/test-ctype$X: ctype.o
 
-test-date$X: date.o ctype.o
+t/helper/test-date$X: date.o ctype.o
 
-test-delta$X: diff-delta.o patch-delta.o
+t/helper/test-delta$X: diff-delta.o patch-delta.o
 
-test-line-buffer$X: vcs-svn/lib.a
+t/helper/test-line-buffer$X: vcs-svn/lib.a
 
-test-parse-options$X: parse-options.o parse-options-cb.o
+t/helper/test-parse-options$X: parse-options.o parse-options-cb.o
 
-test-svn-fe$X: vcs-svn/lib.a
+t/helper/test-svn-fe$X: vcs-svn/lib.a
 
 .PRECIOUS: $(TEST_OBJS)
 
-test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS)
+t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
 
-check-sha1:: test-sha1$X
-       ./test-sha1.sh
+check-sha1:: t/helper/test-sha1$X
+       t/helper/test-sha1.sh
 
 SP_OBJ = $(patsubst %.o,%.sp,$(C_OBJ))
 
@@ -2390,31 +2390,25 @@ quick-install-html:
 
 ### Maintainer's dist rules
 
-git.spec: git.spec.in GIT-VERSION-FILE
-       sed -e 's/@@VERSION@@/$(GIT_VERSION)/g' < $< > $@+
-       mv $@+ $@
-
 GIT_TARNAME = git-$(GIT_VERSION)
-dist: git.spec git-archive$(X) configure
+dist: git-archive$(X) configure
        ./git-archive --format=tar \
                --prefix=$(GIT_TARNAME)/ HEAD^{tree} > $(GIT_TARNAME).tar
        @mkdir -p $(GIT_TARNAME)
-       @cp git.spec configure $(GIT_TARNAME)
+       @cp configure $(GIT_TARNAME)
        @echo $(GIT_VERSION) > $(GIT_TARNAME)/version
        @$(MAKE) -C git-gui TARDIR=../$(GIT_TARNAME)/git-gui dist-version
        $(TAR) rf $(GIT_TARNAME).tar \
-               $(GIT_TARNAME)/git.spec \
                $(GIT_TARNAME)/configure \
                $(GIT_TARNAME)/version \
                $(GIT_TARNAME)/git-gui/version
        @$(RM) -r $(GIT_TARNAME)
        gzip -f -9 $(GIT_TARNAME).tar
 
-rpm: dist
-       $(RPMBUILD) \
-               --define "_source_filedigest_algorithm md5" \
-               --define "_binary_filedigest_algorithm md5" \
-               -ta $(GIT_TARNAME).tar.gz
+rpm::
+       @echo >&2 "Use distro packaged sources to run rpmbuild"
+       @false
+.PHONY: rpm
 
 htmldocs = git-htmldocs-$(GIT_VERSION)
 manpages = git-manpages-$(GIT_VERSION)
@@ -2450,8 +2444,8 @@ profile-clean:
        $(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
 
 clean: profile-clean coverage-clean
-       $(RM) *.o *.res refs/*.o block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o
-       $(RM) xdiff/*.o vcs-svn/*.o ewah/*.o builtin/*.o
+       $(RM) *.res
+       $(RM) $(OBJECTS)
        $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
@@ -2490,6 +2484,7 @@ ALL_COMMANDS += git-gui git-citool
 
 .PHONY: check-docs
 check-docs::
+       $(MAKE) -C Documentation lint-docs
        @(for v in $(ALL_COMMANDS); \
        do \
                case "$$v" in \
index d1ffbb61702ee1c51f38ee7f65800147d514d084..bd8a918a9bcbd46590078d98a66f3d7e939df3fb 100644 (file)
--- a/README.md
+++ b/README.md
@@ -17,14 +17,14 @@ 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".
+read with `man gittutorial` or `git help tutorial`, and the
+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
+(`man gitcvs-migration` or `git help cvs-migration` if git is
 installed).
 
 The user discussion and development of Git take place on the Git
@@ -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 eb82eea64fef8ce0d4bcd18e4daab09aa53e026a..66606735cbf4b890b8faf5be7604508bc37c9256 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.8.3.txt
\ No newline at end of file
+Documentation/RelNotes/2.9.0.txt
\ No newline at end of file
index 7996c2907b0e571578f2ce18095c83c134a503b3..6d93edbcb97382269bd310d38cacd95ce3619091 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -860,8 +860,8 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
        /* Create file BISECT_ANCESTORS_OK. */
        fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
        if (fd < 0)
-               warning("could not create file '%s': %s",
-                       filename, strerror(errno));
+               warning_errno("could not create file '%s'",
+                             filename);
        else
                close(fd);
  done:
@@ -910,8 +910,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
                        *read_good = "good";
                        return;
                } else {
-                       die("could not read file '%s': %s", filename,
-                               strerror(errno));
+                       die_errno("could not read file '%s'", filename);
                }
        } else {
                strbuf_getline_lf(&str, fp);
index 416244370783adcd7648ae7c03c7d0d8cf778b12..a5a8dcbd0ed929d09a73674361ee1dd81dd5b88c 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -334,15 +334,16 @@ void remove_branch_state(void)
        unlink(git_path_squash_msg());
 }
 
-void die_if_checked_out(const char *branch)
+void die_if_checked_out(const char *branch, int ignore_current_worktree)
 {
-       char *existing;
+       const struct worktree *wt;
 
-       existing = find_shared_symref("HEAD", branch);
-       if (existing) {
-               skip_prefix(branch, "refs/heads/", &branch);
-               die(_("'%s' is already checked out at '%s'"), branch, existing);
-       }
+       wt = find_shared_symref("HEAD", branch);
+       if (!wt || (ignore_current_worktree && wt->is_current))
+               return;
+       skip_prefix(branch, "refs/heads/", &branch);
+       die(_("'%s' is already checked out at '%s'"),
+           branch, wt->path);
 }
 
 int replace_each_worktree_head_symref(const char *oldref, const char *newref)
@@ -357,7 +358,8 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref)
                if (strcmp(oldref, worktrees[i]->head_ref))
                        continue;
 
-               if (set_worktree_head_symref(worktrees[i]->git_dir, newref)) {
+               if (set_worktree_head_symref(get_worktree_git_dir(worktrees[i]),
+                                            newref)) {
                        ret = -1;
                        error(_("HEAD of working tree %s is not updated"),
                              worktrees[i]->path);
index d69163daf793f92f3dab92e2fb9bc5217b2dfd6b..b2f964933270e1306eff1f0589c58efd22d6c76b 100644 (file)
--- a/branch.h
+++ b/branch.h
@@ -58,7 +58,7 @@ extern int read_branch_desc(struct strbuf *, const char *branch_name);
  * worktree and die (with a message describing its checkout location) if
  * it is.
  */
-extern void die_if_checked_out(const char *branch);
+extern void die_if_checked_out(const char *branch, int ignore_current_worktree);
 
 /*
  * Update all per-worktree HEADs pointing at the old ref to point the new ref.
index d003939bc5c65947451de4a6e1817eb0f184211f..3dfe70b7a039afadf2de4dfe8f809e3321287004 100644 (file)
@@ -769,15 +769,15 @@ static int split_mail_conv(mail_conv_fn fn, struct am_state *state,
                        in = fopen(*paths, "r");
 
                if (!in)
-                       return error(_("could not open '%s' for reading: %s"),
-                                       *paths, strerror(errno));
+                       return error_errno(_("could not open '%s' for reading"),
+                                          *paths);
 
                mail = mkpath("%s/%0*d", state->dir, state->prec, i + 1);
 
                out = fopen(mail, "w");
                if (!out)
-                       return error(_("could not open '%s' for writing: %s"),
-                                       mail, strerror(errno));
+                       return error_errno(_("could not open '%s' for writing"),
+                                          mail);
 
                ret = fn(out, in, keep_cr);
 
@@ -857,8 +857,7 @@ static int split_mail_stgit_series(struct am_state *state, const char **paths,
 
        fp = fopen(*paths, "r");
        if (!fp)
-               return error(_("could not open '%s' for reading: %s"), *paths,
-                               strerror(errno));
+               return error_errno(_("could not open '%s' for reading"), *paths);
 
        while (!strbuf_getline_lf(&sb, fp)) {
                if (*sb.buf == '#')
index ce3b77853c7a409b40d14400172327978266f9ca..c770d7d3d9fd923957478667291a77d33935ebe5 100644 (file)
@@ -442,7 +442,7 @@ static int is_dev_null(const char *str)
 #define TERM_SPACE     1
 #define TERM_TAB       2
 
-static int name_terminate(const char *name, int namelen, int c, int terminate)
+static int name_terminate(int c, int terminate)
 {
        if (c == ' ' && !(terminate & TERM_SPACE))
                return 0;
@@ -671,7 +671,7 @@ static char *find_name_common(const char *line, const char *def,
                if (!end && isspace(c)) {
                        if (c == '\n')
                                break;
-                       if (name_terminate(start, line-start, c, terminate))
+                       if (name_terminate(c, terminate))
                                break;
                }
                line++;
@@ -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 37af77161e4f899f9897db0838f0b736b8450bec..2ecde53bf838777e191926557c0c8190cdc7d09a 100644 (file)
@@ -220,12 +220,12 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                name = mkpathdup(fmt, bname.buf);
 
                if (kinds == FILTER_REFS_BRANCHES) {
-                       char *worktree = find_shared_symref("HEAD", name);
-                       if (worktree) {
+                       const struct worktree *wt =
+                               find_shared_symref("HEAD", name);
+                       if (wt) {
                                error(_("Cannot delete branch '%s' "
                                        "checked out at '%s'"),
-                                     bname.buf, worktree);
-                               free(worktree);
+                                     bname.buf, wt->path);
                                ret = 1;
                                continue;
                        }
@@ -526,6 +526,29 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
        ref_array_clear(&array);
 }
 
+static void reject_rebase_or_bisect_branch(const char *target)
+{
+       struct worktree **worktrees = get_worktrees();
+       int i;
+
+       for (i = 0; worktrees[i]; i++) {
+               struct worktree *wt = worktrees[i];
+
+               if (!wt->is_detached)
+                       continue;
+
+               if (is_worktree_being_rebased(wt, target))
+                       die(_("Branch %s is being rebased at %s"),
+                           target, wt->path);
+
+               if (is_worktree_being_bisected(wt, target))
+                       die(_("Branch %s is being bisected at %s"),
+                           target, wt->path);
+       }
+
+       free_worktrees(worktrees);
+}
+
 static void rename_branch(const char *oldname, const char *newname, int force)
 {
        struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
@@ -555,6 +578,8 @@ static void rename_branch(const char *oldname, const char *newname, int force)
 
        validate_new_branchname(newname, &newref, force, clobber_head_ok);
 
+       reject_rebase_or_bisect_branch(oldref.buf);
+
        strbuf_addf(&logmsg, "Branch: renamed %s to %s",
                 oldref.buf, newref.buf);
 
@@ -595,8 +620,7 @@ static int edit_branch_description(const char *branch_name)
                    branch_name, comment_line_char);
        if (write_file_gently(git_path(edit_description), "%s", buf.buf)) {
                strbuf_release(&buf);
-               return error(_("could not write branch description template: %s"),
-                            strerror(errno));
+               return error_errno(_("could not write branch description template"));
        }
        strbuf_reset(&buf);
        if (launch_editor(git_path(edit_description), &buf, NULL)) {
index 54db1184a003897294b6aaa46bc6bf29faa919bc..618103fdeeb7f7b35911ebd12d0d811cc4a5297a 100644 (file)
@@ -154,6 +154,13 @@ struct expand_data {
         * elements above, so you can retrieve the response from there.
         */
        struct object_info info;
+
+       /*
+        * This flag will be true if the requested batch format and options
+        * don't require us to call sha1_object_info, which can then be
+        * optimized out.
+        */
+       unsigned skip_object_info : 1;
 };
 
 static int is_atom(const char *atom, const char *s, int slen)
@@ -258,7 +265,8 @@ static void batch_object_write(const char *obj_name, struct batch_options *opt,
 {
        struct strbuf buf = STRBUF_INIT;
 
-       if (sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) {
+       if (!data->skip_object_info &&
+           sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) {
                printf("%s missing\n", obj_name ? obj_name : sha1_to_hex(data->sha1));
                fflush(stdout);
                return;
@@ -369,6 +377,13 @@ static int batch_objects(struct batch_options *opt)
        strbuf_expand(&buf, opt->format, expand_format, &data);
        data.mark_query = 0;
 
+       if (opt->all_objects) {
+               struct object_info empty;
+               memset(&empty, 0, sizeof(empty));
+               if (!memcmp(&data.info, &empty, sizeof(empty)))
+                       data.skip_object_info = 1;
+       }
+
        /*
         * If we are printing out the object, then always fill in the type,
         * since we will want to decide whether or not to stream.
@@ -489,6 +504,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
 
        git_config(git_cat_file_config, NULL);
 
+       batch.buffer_output = -1;
        argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0);
 
        if (opt) {
@@ -512,6 +528,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
                usage_with_options(cat_file_usage, options);
        }
 
+       if (batch.buffer_output < 0)
+               batch.buffer_output = batch.all_objects;
+
        if (batch.enabled)
                return batch_objects(&batch);
 
index efcbd8f6b5e0b7d551c88c02b6d7d3069a47944d..3398c61e9a64ab686bf59fb027277f3de578b5fb 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);
@@ -1111,7 +1110,7 @@ static int checkout_branch(struct checkout_opts *opts,
                char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
                if (head_ref &&
                    (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
-                       die_if_checked_out(new->path);
+                       die_if_checked_out(new->path, 1);
                free(head_ref);
        }
 
index 661639255c564acf3f811e20a0ca3141805cde5d..5f867e67d8066fe70fdd67c3d8e2853ea6c3eaa1 100644 (file)
@@ -40,6 +40,7 @@ static const char * const builtin_clone_usage[] = {
 
 static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;
 static int option_local = -1, option_no_hardlinks, option_shared, option_recursive;
+static int option_shallow_submodules = -1;
 static char *option_template, *option_depth;
 static char *option_origin = NULL;
 static char *option_branch = NULL;
@@ -51,6 +52,7 @@ static enum transport_family family;
 static struct string_list option_config;
 static struct string_list option_reference;
 static int option_dissociate;
+static int max_jobs = -1;
 
 static struct option builtin_clone_options[] = {
        OPT__VERBOSITY(&option_verbosity),
@@ -73,6 +75,8 @@ static struct option builtin_clone_options[] = {
                    N_("initialize submodules in the clone")),
        OPT_BOOL(0, "recurse-submodules", &option_recursive,
                    N_("initialize submodules in the clone")),
+       OPT_INTEGER('j', "jobs", &max_jobs,
+                   N_("number of submodules cloned in parallel")),
        OPT_STRING(0, "template", &option_template, N_("template-directory"),
                   N_("directory from which templates will be used")),
        OPT_STRING_LIST(0, "reference", &option_reference, N_("repo"),
@@ -89,6 +93,8 @@ static struct option builtin_clone_options[] = {
                    N_("create a shallow clone of that depth")),
        OPT_BOOL(0, "single-branch", &option_single_branch,
                    N_("clone only one branch, HEAD or --branch")),
+       OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules,
+                   N_("any cloned submodules will be shallow")),
        OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
                   N_("separate git dir from working tree")),
        OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
@@ -100,10 +106,6 @@ static struct option builtin_clone_options[] = {
        OPT_END()
 };
 
-static const char *argv_submodule[] = {
-       "submodule", "update", "--init", "--recursive", NULL
-};
-
 static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
 {
        static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
@@ -732,8 +734,20 @@ static int checkout(void)
        err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
                           sha1_to_hex(sha1), "1", NULL);
 
-       if (!err && option_recursive)
-               err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
+       if (!err && option_recursive) {
+               struct argv_array args = ARGV_ARRAY_INIT;
+               argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);
+
+               if (option_shallow_submodules == 1
+                   || (option_shallow_submodules == -1 && option_depth))
+                       argv_array_push(&args, "--depth=1");
+
+               if (max_jobs != -1)
+                       argv_array_pushf(&args, "--jobs=%d", max_jobs);
+
+               err = run_command_v_opt(args.argv, RUN_GIT_CMD);
+               argv_array_clear(&args);
+       }
 
        return err;
 }
index 3feeffeab1ccd51a5e30098c31b4daa62e7ef684..8a674bc9e759116a06f7464222ec591472b65610 100644 (file)
@@ -33,10 +33,6 @@ static int commit_tree_config(const char *var, const char *value, void *cb)
        int status = git_gpg_config(var, value, NULL);
        if (status)
                return status;
-       if (!strcmp(var, "commit.gpgsign")) {
-               sign_commit = git_config_bool(var, value) ? "" : NULL;
-               return 0;
-       }
        return git_default_config(var, value, cb);
 }
 
index e13303787a13aecf282ecd6b207f7f0c2a855cde..443ff9196d16a9b6fc2d1d8b22393b1533faeb8f 100644 (file)
@@ -114,6 +114,7 @@ static char *fixup_message, *squash_message;
 static int all, also, interactive, patch_interactive, only, amend, signoff;
 static int edit_flag = -1; /* unspecified */
 static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
+static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
 static char *sign_commit;
@@ -186,6 +187,7 @@ static void status_init_config(struct wt_status *s, config_fn_t fn)
        gitmodules_config();
        git_config(fn, s);
        determine_whence(s);
+       init_diff_ui_defaults();
        s->hints = advice_status_hints; /* must come after git_config() */
 }
 
@@ -1514,6 +1516,11 @@ static int git_commit_config(const char *k, const char *v, void *cb)
                sign_commit = git_config_bool(k, v) ? "" : NULL;
                return 0;
        }
+       if (!strcmp(k, "commit.verbose")) {
+               int is_bool;
+               config_commit_verbose = git_config_bool_or_int(k, v, &is_bool);
+               return 0;
+       }
 
        status = git_gpg_config(k, v, NULL);
        if (status)
@@ -1660,9 +1667,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                if (parse_commit(current_head))
                        die(_("could not parse HEAD commit"));
        }
+       verbose = -1; /* unspecified */
        argc = parse_and_validate_options(argc, argv, builtin_commit_options,
                                          builtin_commit_usage,
                                          prefix, current_head, &s);
+       if (verbose == -1)
+               verbose = (config_commit_verbose < 0) ? 0 : config_commit_verbose;
+
        if (dry_run)
                return dry_run_commit(argc, argv, prefix, current_head, &s);
        index_file = prepare_index(argc, argv, prefix, current_head, 0);
index 8ed2eb8813a442b64c8f43ed23acbdbd1c0dd452..15c61fd8d1ef891b013404ea5e7cbe42befac7bd 100644 (file)
@@ -24,6 +24,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
        gitmodules_config();
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        rev.abbrev = 0;
+       precompose_argv(argc, argv);
 
        argc = setup_revisions(argc, argv, &rev, NULL);
        while (1 < argc && argv[1][0] == '-') {
index d979824f9395a0cbc8ffffa9cede524589710d43..1af373d0021f56f539a1d261e908a60d92ff5b4f 100644 (file)
@@ -21,6 +21,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
        gitmodules_config();
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        rev.abbrev = 0;
+       precompose_argv(argc, argv);
 
        argc = setup_revisions(argc, argv, &rev, NULL);
        for (i = 1; i < argc; i++) {
index 2a12b81e065bfb75f05baf6694e1a9d53129bc40..806dd7a885e9e7b87e19ca8acd2b6d0383b41d85 100644 (file)
@@ -114,6 +114,8 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
        opt->disable_stdin = 1;
        memset(&s_r_opt, 0, sizeof(s_r_opt));
        s_r_opt.tweak = diff_tree_tweak_rev;
+
+       precompose_argv(argc, argv);
        argc = setup_revisions(argc, argv, opt, &s_r_opt);
 
        while (--argc > 0) {
index 52c98a9217c60a60f0012dbdafbb193e78ec8a33..b7a9405d9fbec46edf99637557d7499fb58c4d77 100644 (file)
@@ -318,7 +318,9 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 
        if (!no_index)
                gitmodules_config();
+       init_diff_ui_defaults();
        git_config(git_diff_ui_config, NULL);
+       precompose_argv(argc, argv);
 
        init_revisions(&rev, prefix);
 
index e4639d8eb1d5fda586520f10271c05a0897f2ea5..1582ca71847fe4d79366e85cbea86f9f64f55b90 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;
@@ -607,7 +607,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
 
        fp = fopen(filename, "a");
        if (!fp)
-               return error(_("cannot open %s: %s\n"), filename, strerror(errno));
+               return error_errno(_("cannot open %s"), filename);
 
        if (raw_url)
                url = transport_anonymize_url(raw_url);
@@ -848,7 +848,7 @@ static int truncate_fetch_head(void)
        FILE *fp = fopen_for_writing(filename);
 
        if (!fp)
-               return error(_("cannot open %s: %s\n"), filename, strerror(errno));
+               return error_errno(_("cannot open %s"), filename);
        fclose(fp);
        return 0;
 }
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 111b6f6cf1a61b0fb481101fcdc5a9f3f48c4650..462e60790165a333d58d81ec65ae4ae6da8d4f79 100644 (file)
@@ -438,7 +438,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                strbuf_add(base, entry.path, te_len);
 
                if (S_ISREG(entry.mode)) {
-                       hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len,
+                       hit |= grep_sha1(opt, entry.oid->hash, base->buf, tn_len,
                                         check_attr ? base->buf + tn_len : NULL);
                }
                else if (S_ISDIR(entry.mode)) {
@@ -447,10 +447,10 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                        void *data;
                        unsigned long size;
 
-                       data = lock_and_read_sha1_file(entry.sha1, &type, &size);
+                       data = lock_and_read_sha1_file(entry.oid->hash, &type, &size);
                        if (!data)
                                die(_("unable to read tree (%s)"),
-                                   sha1_to_hex(entry.sha1));
+                                   oid_to_hex(entry.oid));
 
                        strbuf_addch(base, '/');
                        init_tree_desc(&sub, data, size);
index 3c55ce456309ee7da95eac0517f11161ab3c1f7c..88480131cf9fa85f303ede2662eabe09d848ba4e 100644 (file)
@@ -127,7 +127,7 @@ static void exec_woman_emacs(const char *path, const char *page)
                        path = "emacsclient";
                strbuf_addf(&man_page, "(woman \"%s\")", page);
                execlp(path, "emacsclient", "-e", man_page.buf, (char *)NULL);
-               warning(_("failed to exec '%s': %s"), path, strerror(errno));
+               warning_errno(_("failed to exec '%s'"), path);
        }
 }
 
@@ -148,7 +148,7 @@ static void exec_man_konqueror(const char *path, const char *page)
                        path = "kfmclient";
                strbuf_addf(&man_page, "man:%s(1)", page);
                execlp(path, filename, "newTab", man_page.buf, (char *)NULL);
-               warning(_("failed to exec '%s': %s"), path, strerror(errno));
+               warning_errno(_("failed to exec '%s'"), path);
        }
 }
 
@@ -157,7 +157,7 @@ static void exec_man_man(const char *path, const char *page)
        if (!path)
                path = "man";
        execlp(path, "man", page, (char *)NULL);
-       warning(_("failed to exec '%s': %s"), path, strerror(errno));
+       warning_errno(_("failed to exec '%s'"), path);
 }
 
 static void exec_man_cmd(const char *cmd, const char *page)
@@ -165,7 +165,7 @@ static void exec_man_cmd(const char *cmd, const char *page)
        struct strbuf shell_cmd = STRBUF_INIT;
        strbuf_addf(&shell_cmd, "%s %s", cmd, page);
        execl(SHELL_PATH, SHELL_PATH, "-c", shell_cmd.buf, (char *)NULL);
-       warning(_("failed to exec '%s': %s"), cmd, strerror(errno));
+       warning(_("failed to exec '%s'"), cmd);
 }
 
 static void add_man_viewer(const char *name)
index 0d738d6ddc0d6335e15392dd90e4118e2cb58b88..099f4f7be92c6d4371dcc8cba1322351c6840439 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);
@@ -696,6 +702,7 @@ static void add_header(const char *value)
 #define THREAD_DEEP 2
 static int thread;
 static int do_signoff;
+static int base_auto;
 static const char *signature = git_version_string;
 static const char *signature_file;
 static int config_cover_letter;
@@ -780,6 +787,10 @@ static int git_format_config(const char *var, const char *value, void *cb)
        }
        if (!strcmp(var, "format.outputdirectory"))
                return git_config_string(&config_output_directory, var, value);
+       if (!strcmp(var, "format.useautobase")) {
+               base_auto = git_config_bool(var, value);
+               return 0;
+       }
 
        return git_log_config(var, value, cb);
 }
@@ -1185,6 +1196,155 @@ static int from_callback(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+struct base_tree_info {
+       struct object_id base_commit;
+       int nr_patch_id, alloc_patch_id;
+       struct object_id *patch_id;
+};
+
+static struct commit *get_base_commit(const char *base_commit,
+                                     struct commit **list,
+                                     int total)
+{
+       struct commit *base = NULL;
+       struct commit **rev;
+       int i = 0, rev_nr = 0;
+
+       if (base_commit && strcmp(base_commit, "auto")) {
+               base = lookup_commit_reference_by_name(base_commit);
+               if (!base)
+                       die(_("Unknown commit %s"), base_commit);
+       } else if ((base_commit && !strcmp(base_commit, "auto")) || base_auto) {
+               struct branch *curr_branch = branch_get(NULL);
+               const char *upstream = branch_get_upstream(curr_branch, NULL);
+               if (upstream) {
+                       struct commit_list *base_list;
+                       struct commit *commit;
+                       unsigned char sha1[20];
+
+                       if (get_sha1(upstream, sha1))
+                               die(_("Failed to resolve '%s' as a valid ref."), upstream);
+                       commit = lookup_commit_or_die(sha1, "upstream base");
+                       base_list = get_merge_bases_many(commit, total, list);
+                       /* There should be one and only one merge base. */
+                       if (!base_list || base_list->next)
+                               die(_("Could not find exact merge base."));
+                       base = base_list->item;
+                       free_commit_list(base_list);
+               } else {
+                       die(_("Failed to get upstream, if you want to record base commit automatically,\n"
+                             "please use git branch --set-upstream-to to track a remote branch.\n"
+                             "Or you could specify base commit by --base=<base-commit-id> manually."));
+               }
+       }
+
+       ALLOC_ARRAY(rev, total);
+       for (i = 0; i < total; i++)
+               rev[i] = list[i];
+
+       rev_nr = total;
+       /*
+        * Get merge base through pair-wise computations
+        * and store it in rev[0].
+        */
+       while (rev_nr > 1) {
+               for (i = 0; i < rev_nr / 2; i++) {
+                       struct commit_list *merge_base;
+                       merge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]);
+                       if (!merge_base || merge_base->next)
+                               die(_("Failed to find exact merge base"));
+
+                       rev[i] = merge_base->item;
+               }
+
+               if (rev_nr % 2)
+                       rev[i] = rev[2 * i];
+               rev_nr = (rev_nr + 1) / 2;
+       }
+
+       if (!in_merge_bases(base, rev[0]))
+               die(_("base commit should be the ancestor of revision list"));
+
+       for (i = 0; i < total; i++) {
+               if (base == list[i])
+                       die(_("base commit shouldn't be in revision list"));
+       }
+
+       free(rev);
+       return base;
+}
+
+static void prepare_bases(struct base_tree_info *bases,
+                         struct commit *base,
+                         struct commit **list,
+                         int total)
+{
+       struct commit *commit;
+       struct rev_info revs;
+       struct diff_options diffopt;
+       int i;
+
+       if (!base)
+               return;
+
+       diff_setup(&diffopt);
+       DIFF_OPT_SET(&diffopt, RECURSIVE);
+       diff_setup_done(&diffopt);
+
+       oidcpy(&bases->base_commit, &base->object.oid);
+
+       init_revisions(&revs, NULL);
+       revs.max_parents = 1;
+       revs.topo_order = 1;
+       for (i = 0; i < total; i++) {
+               list[i]->object.flags &= ~UNINTERESTING;
+               add_pending_object(&revs, &list[i]->object, "rev_list");
+               list[i]->util = (void *)1;
+       }
+       base->object.flags |= UNINTERESTING;
+       add_pending_object(&revs, &base->object, "base");
+
+       if (prepare_revision_walk(&revs))
+               die(_("revision walk setup failed"));
+       /*
+        * Traverse the commits list, get prerequisite patch ids
+        * and stuff them in bases structure.
+        */
+       while ((commit = get_revision(&revs)) != NULL) {
+               unsigned char sha1[20];
+               struct object_id *patch_id;
+               if (commit->util)
+                       continue;
+               if (commit_patch_id(commit, &diffopt, sha1))
+                       die(_("cannot get patch id"));
+               ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id);
+               patch_id = bases->patch_id + bases->nr_patch_id;
+               hashcpy(patch_id->hash, sha1);
+               bases->nr_patch_id++;
+       }
+}
+
+static void print_bases(struct base_tree_info *bases)
+{
+       int i;
+
+       /* Only do this once, either for the cover or for the first one */
+       if (is_null_oid(&bases->base_commit))
+               return;
+
+       /* Show the base commit */
+       printf("base-commit: %s\n", oid_to_hex(&bases->base_commit));
+
+       /* Show the prerequisite patches */
+       for (i = bases->nr_patch_id - 1; i >= 0; i--)
+               printf("prerequisite-patch-id: %s\n", oid_to_hex(&bases->patch_id[i]));
+
+       free(bases->patch_id);
+       bases->nr_patch_id = 0;
+       bases->alloc_patch_id = 0;
+       oidclr(&bases->base_commit);
+}
+
 int cmd_format_patch(int argc, const char **argv, const char *prefix)
 {
        struct commit *commit;
@@ -1209,6 +1369,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int reroll_count = -1;
        char *branch_name = NULL;
        char *from = NULL;
+       char *base_commit = NULL;
+       struct base_tree_info bases;
+
        const struct option builtin_format_patch_options[] = {
                { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
                            N_("use [PATCH n/m] even with a single patch"),
@@ -1271,6 +1434,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                            PARSE_OPT_OPTARG, thread_callback },
                OPT_STRING(0, "signature", &signature, N_("signature"),
                            N_("add a signature")),
+               OPT_STRING(0, "base", &base_commit, N_("base-commit"),
+                          N_("add prerequisite tree info to the patch series")),
                OPT_FILENAME(0, "signature-file", &signature_file,
                                N_("add a signature from a file")),
                OPT__QUIET(&quiet, N_("don't print the patch filenames")),
@@ -1280,10 +1445,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        extra_hdr.strdup_strings = 1;
        extra_to.strdup_strings = 1;
        extra_cc.strdup_strings = 1;
-       init_grep_defaults();
+       init_log_defaults();
        git_config(git_format_config, NULL);
        init_revisions(&rev, prefix);
        rev.commit_format = CMIT_FMT_EMAIL;
+       rev.expand_tabs_in_log_default = 0;
        rev.verbose_header = 1;
        rev.diff = 1;
        rev.max_parents = 1;
@@ -1507,6 +1673,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                signature = strbuf_detach(&buf, NULL);
        }
 
+       memset(&bases, 0, sizeof(bases));
+       if (base_commit || base_auto) {
+               struct commit *base = get_base_commit(base_commit, list, nr);
+               reset_revision_walk();
+               prepare_bases(&bases, base, list, nr);
+       }
+
        if (in_reply_to || thread || cover_letter)
                rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
        if (in_reply_to) {
@@ -1520,6 +1693,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        gen_message_id(&rev, "cover");
                make_cover_letter(&rev, use_stdout,
                                  origin, nr, list, branch_name, quiet);
+               print_bases(&bases);
                total++;
                start_number--;
        }
@@ -1585,6 +1759,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                                       rev.mime_boundary);
                        else
                                print_signature();
+                       print_bases(&bases);
                }
                if (!use_stdout)
                        fclose(stdout);
index 104277acc49a08d9472523edc8337c3eeeb345cd..4859ede38adc76521baf97326b82a007a37b3893 100644 (file)
@@ -109,7 +109,7 @@ static int populate_maildir_list(struct string_list *list, const char *path)
                if ((dir = opendir(name)) == NULL) {
                        if (errno == ENOENT)
                                continue;
-                       error("cannot opendir %s (%s)", name, strerror(errno));
+                       error_errno("cannot opendir %s", name);
                        goto out;
                }
 
@@ -174,12 +174,12 @@ static int split_maildir(const char *maildir, const char *dir,
 
                f = fopen(file, "r");
                if (!f) {
-                       error("cannot open mail %s (%s)", file, strerror(errno));
+                       error_errno("cannot open mail %s", file);
                        goto out;
                }
 
                if (strbuf_getwholeline(&buf, f, '\n')) {
-                       error("cannot read mail %s (%s)", file, strerror(errno));
+                       error_errno("cannot read mail %s", file);
                        goto out;
                }
 
@@ -210,7 +210,7 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,
        int file_done = 0;
 
        if (!f) {
-               error("cannot open mbox %s", file);
+               error_errno("cannot open mbox %s", file);
                goto out;
        }
 
@@ -318,7 +318,7 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
                }
 
                if (stat(arg, &argstat) == -1) {
-                       error("cannot stat %s (%s)", arg, strerror(errno));
+                       error_errno("cannot stat %s", arg);
                        return 1;
                }
 
index 55447053f2dde004ceffff9967b015b4a01c35d1..13e22a2f0be73ec7e521a02d1f36c414e77a8700 100644 (file)
@@ -62,8 +62,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                usage_with_options(merge_file_usage, options);
        if (quiet) {
                if (!freopen("/dev/null", "w", stderr))
-                       return error("failed to redirect stderr to /dev/null: "
-                                    "%s", strerror(errno));
+                       return error_errno("failed to redirect stderr to /dev/null");
        }
 
        if (prefix)
@@ -95,12 +94,13 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
 
                if (!f)
-                       ret = error("Could not open %s for writing", filename);
+                       ret = error_errno("Could not open %s for writing",
+                                         filename);
                else if (result.size &&
                         fwrite(result.ptr, result.size, 1, f) != 1)
-                       ret = error("Could not write to %s", filename);
+                       ret = error_errno("Could not write to %s", filename);
                else if (fclose(f))
-                       ret = error("Could not close %s", filename);
+                       ret = error_errno("Could not close %s", filename);
                free(result.ptr);
        }
 
index ca570041df0f67efa92b056cec01e7d9a4e38ad7..5b7ab9b9672e08d7db843d0ccd951ee4c7bdac15 100644 (file)
@@ -150,15 +150,15 @@ static void show_result(void)
 /* An empty entry never compares same, not even to another empty entry */
 static int same_entry(struct name_entry *a, struct name_entry *b)
 {
-       return  a->sha1 &&
-               b->sha1 &&
-               !hashcmp(a->sha1, b->sha1) &&
+       return  a->oid &&
+               b->oid &&
+               !oidcmp(a->oid, b->oid) &&
                a->mode == b->mode;
 }
 
 static int both_empty(struct name_entry *a, struct name_entry *b)
 {
-       return !(a->sha1 || b->sha1);
+       return !(a->oid || b->oid);
 }
 
 static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path)
@@ -188,8 +188,8 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s
                return;
 
        path = traverse_path(info, result);
-       orig = create_entry(2, ours->mode, ours->sha1, path);
-       final = create_entry(0, result->mode, result->sha1, path);
+       orig = create_entry(2, ours->mode, ours->oid->hash, path);
+       final = create_entry(0, result->mode, result->oid->hash, path);
 
        final->link = orig;
 
@@ -213,7 +213,7 @@ static void unresolved_directory(const struct traverse_info *info,
 
        newbase = traverse_path(info, p);
 
-#define ENTRY_SHA1(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->sha1 : NULL)
+#define ENTRY_SHA1(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->oid->hash : NULL)
        buf0 = fill_tree_descriptor(t+0, ENTRY_SHA1(n + 0));
        buf1 = fill_tree_descriptor(t+1, ENTRY_SHA1(n + 1));
        buf2 = fill_tree_descriptor(t+2, ENTRY_SHA1(n + 2));
@@ -239,7 +239,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info
                path = entry->path;
        else
                path = traverse_path(info, n);
-       link = create_entry(stage, n->mode, n->sha1, path);
+       link = create_entry(stage, n->mode, n->oid->hash, path);
        link->link = entry;
        return link;
 }
@@ -314,7 +314,7 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s
        }
 
        if (same_entry(entry+0, entry+1)) {
-               if (entry[2].sha1 && !S_ISDIR(entry[2].mode)) {
+               if (entry[2].oid && !S_ISDIR(entry[2].mode)) {
                        /* We did not touch, they modified -- take theirs */
                        resolve(info, entry+1, entry+2);
                        return mask;
index bf2f2614fbb5ba82df3dca5cad8f79dbe605feff..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)
@@ -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 6fd058de9272631a3d5135ccbbd79bf1b4ff135c..c65b59ad9a340e9b68e4d09ea8912499227fce66 100644 (file)
@@ -847,15 +847,15 @@ static int merge(int argc, const char **argv, const char *prefix)
                update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
                           0, UPDATE_REFS_DIE_ON_ERR);
        else { /* Merge has unresolved conflicts */
-               char *existing;
+               const struct worktree *wt;
                /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
                update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
                           0, UPDATE_REFS_DIE_ON_ERR);
                /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
-               existing = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
-               if (existing)
+               wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
+               if (wt)
                        die(_("A notes merge into %s is already in-progress at %s"),
-                           default_notes_ref(), existing);
+                           default_notes_ref(), wt->path);
                if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
                        die("Failed to store link to current notes ref (%s)",
                            default_notes_ref());
index b6664ce4d8d72cde05e1f1be5edcd967f003f0ef..8f5e358e22b40ff2e162cc7037339ad357622650 100644 (file)
@@ -835,8 +835,7 @@ static void write_pack_file(void)
                         * to preserve this property.
                         */
                        if (stat(pack_tmp_name, &st) < 0) {
-                               warning("failed to stat %s: %s",
-                                       pack_tmp_name, strerror(errno));
+                               warning_errno("failed to stat %s", pack_tmp_name);
                        } else if (!last_mtime) {
                                last_mtime = st.st_mtime;
                        } else {
@@ -844,8 +843,7 @@ static void write_pack_file(void)
                                utb.actime = st.st_atime;
                                utb.modtime = --last_mtime;
                                if (utime(pack_tmp_name, &utb) < 0)
-                                       warning("failed utime() on %s: %s",
-                                               pack_tmp_name, strerror(errno));
+                                       warning_errno("failed utime() on %s", pack_tmp_name);
                        }
 
                        strbuf_addf(&tmpname, "%s-", base_name);
@@ -1193,7 +1191,7 @@ static void add_pbase_object(struct tree_desc *tree,
                if (cmp < 0)
                        return;
                if (name[cmplen] != '/') {
-                       add_object_entry(entry.sha1,
+                       add_object_entry(entry.oid->hash,
                                         object_type(entry.mode),
                                         fullname, 1);
                        return;
@@ -1204,7 +1202,7 @@ static void add_pbase_object(struct tree_desc *tree,
                        const char *down = name+cmplen+1;
                        int downlen = name_cmp_len(down);
 
-                       tree = pbase_tree_get(entry.sha1);
+                       tree = pbase_tree_get(entry.oid->hash);
                        if (!tree)
                                return;
                        init_tree_desc(&sub, tree->tree_data, tree->tree_size);
index 366ce5a5d419649dba34f607ed3f54b6a28bd3ea..a84d0003a3087c842b6eab6a987a93eebaddec79 100644 (file)
@@ -81,16 +81,13 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
 
        while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) {
                char *line = line_buf->buf;
-               char *p = line;
+               const char *p = line;
                int len;
 
-               if (!memcmp(line, "diff-tree ", 10))
-                       p += 10;
-               else if (!memcmp(line, "commit ", 7))
-                       p += 7;
-               else if (!memcmp(line, "From ", 5))
-                       p += 5;
-               else if (!memcmp(line, "\\ ", 2) && 12 < strlen(line))
+               if (!skip_prefix(line, "diff-tree ", &p) &&
+                   !skip_prefix(line, "commit ", &p) &&
+                   !skip_prefix(line, "From ", &p) &&
+                   starts_with(line, "\\ ") && 12 < strlen(line))
                        continue;
 
                if (!get_oid_hex(p, next_oid)) {
@@ -99,14 +96,14 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
                }
 
                /* Ignore commit comments */
-               if (!patchlen && memcmp(line, "diff ", 5))
+               if (!patchlen && !starts_with(line, "diff "))
                        continue;
 
                /* Parsing diff header?  */
                if (before == -1) {
-                       if (!memcmp(line, "index ", 6))
+                       if (starts_with(line, "index "))
                                continue;
-                       else if (!memcmp(line, "--- ", 4))
+                       else if (starts_with(line, "--- "))
                                before = after = 1;
                        else if (!isalpha(line[0]))
                                break;
@@ -114,14 +111,14 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
 
                /* Looking for a valid hunk header?  */
                if (before == 0 && after == 0) {
-                       if (!memcmp(line, "@@ -", 4)) {
+                       if (starts_with(line, "@@ -")) {
                                /* Parse next hunk, but ignore line numbers.  */
                                scan_hunk_header(line, &before, &after);
                                continue;
                        }
 
                        /* Split at the end of the patch.  */
-                       if (memcmp(line, "diff ", 5))
+                       if (!starts_with(line, "diff "))
                                break;
 
                        /* Else we're parsing another header.  */
index 6214af9b10c213046e13b81fb30cea64abc0e619..1d7333c8a11f7aedf244c99f5ac67bea7fccaab4 100644 (file)
@@ -86,9 +86,12 @@ static char *opt_commit;
 static char *opt_edit;
 static char *opt_ff;
 static char *opt_verify_signatures;
+static int opt_autostash = -1;
+static int config_autostash;
 static struct argv_array opt_strategies = ARGV_ARRAY_INIT;
 static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;
 static char *opt_gpg_sign;
+static int opt_allow_unrelated_histories;
 
 /* Options passed to git-fetch */
 static char *opt_all;
@@ -149,6 +152,8 @@ static struct option pull_options[] = {
        OPT_PASSTHRU(0, "verify-signatures", &opt_verify_signatures, NULL,
                N_("verify that the named commit has a valid GPG signature"),
                PARSE_OPT_NOARG),
+       OPT_BOOL(0, "autostash", &opt_autostash,
+               N_("automatically stash/stash pop before and after rebase")),
        OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
                N_("merge strategy to use"),
                0),
@@ -159,6 +164,9 @@ static struct option pull_options[] = {
        OPT_PASSTHRU('S', "gpg-sign", &opt_gpg_sign, N_("key-id"),
                N_("GPG sign commit"),
                PARSE_OPT_OPTARG),
+       OPT_SET_INT(0, "allow-unrelated-histories",
+                   &opt_allow_unrelated_histories,
+                   N_("allow merging unrelated histories"), 1),
 
        /* Options passed to git-fetch */
        OPT_GROUP(N_("Options related to fetching")),
@@ -305,6 +313,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.
  */
@@ -612,6 +632,8 @@ static int run_merge(void)
        argv_array_pushv(&args, opt_strategy_opts.argv);
        if (opt_gpg_sign)
                argv_array_push(&args, opt_gpg_sign);
+       if (opt_allow_unrelated_histories > 0)
+               argv_array_push(&args, "--allow-unrelated-histories");
 
        argv_array_push(&args, "FETCH_HEAD");
        ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
@@ -789,6 +811,10 @@ static int run_rebase(const unsigned char *curr_head,
        argv_array_pushv(&args, opt_strategy_opts.argv);
        if (opt_gpg_sign)
                argv_array_push(&args, opt_gpg_sign);
+       if (opt_autostash == 0)
+               argv_array_push(&args, "--no-autostash");
+       else if (opt_autostash == 1)
+               argv_array_push(&args, "--autostash");
 
        argv_array_push(&args, "--onto");
        argv_array_push(&args, sha1_to_hex(merge_head));
@@ -823,7 +849,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
        if (opt_rebase < 0)
                opt_rebase = config_get_rebase();
 
-       git_config(git_default_config, NULL);
+       git_config(git_pull_config, NULL);
 
        if (read_cache_unmerged())
                die_resolve_conflict("Pull");
@@ -834,13 +860,17 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
        if (get_sha1("HEAD", orig_head))
                hashclr(orig_head);
 
+       if (!opt_rebase && opt_autostash != -1)
+               die(_("--[no-]autostash option is only valid with --rebase."));
+
        if (opt_rebase) {
-               int autostash = 0;
+               int autostash = config_autostash;
+               if (opt_autostash != -1)
+                       autostash = opt_autostash;
 
                if (is_null_sha1(orig_head) && !is_cache_unborn())
                        die(_("Updating an unborn branch with changes added to the index."));
 
-               git_config_get_bool("rebase.autostash", &autostash);
                if (!autostash)
                        die_on_unclean_work_tree(prefix);
 
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 2d46b6482a0110072828e6d16da2d2fa3e0f8389..7a7136e53e2b8cf1eaa1c79b1183692dd54278a7 100644 (file)
@@ -84,8 +84,8 @@ static int tree_is_complete(const unsigned char *sha1)
        init_tree_desc(&desc, tree->buffer, tree->size);
        complete = 1;
        while (tree_entry(&desc, &entry)) {
-               if (!has_sha1_file(entry.sha1) ||
-                   (S_ISDIR(entry.mode) && !tree_is_complete(entry.sha1))) {
+               if (!has_sha1_file(entry.oid->hash) ||
+                   (S_ISDIR(entry.mode) && !tree_is_complete(entry.oid->hash))) {
                        tree->object.flags |= INCOMPLETE;
                        complete = 0;
                }
index be83c4347a9cd6fdd6a76a9575f8e39a5b2bd6c2..8abb0207fa8e4da05ea447ae4e58c8e54c97c35b 100644 (file)
@@ -152,7 +152,7 @@ static int check_local_mod(unsigned char *head, int index_only)
 
                if (lstat(ce->name, &st) < 0) {
                        if (errno != ENOENT && errno != ENOTDIR)
-                               warning("'%s': %s", ce->name, strerror(errno));
+                               warning_errno(_("failed to stat '%s'"), ce->name);
                        /* It already vanished from the working tree */
                        continue;
                }
index 3bea3aaa5029082198abab2b5a5dab838dbd6dec..926d2051623be390d5de2611d81d5fe9f39a8169 100644 (file)
@@ -9,6 +9,211 @@
 #include "submodule-config.h"
 #include "string-list.h"
 #include "run-command.h"
+#include "remote.h"
+#include "refs.h"
+#include "connect.h"
+
+static char *get_default_remote(void)
+{
+       char *dest = NULL, *ret;
+       unsigned char sha1[20];
+       struct strbuf sb = STRBUF_INIT;
+       const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
+
+       if (!refname)
+               die(_("No such ref: %s"), "HEAD");
+
+       /* detached HEAD */
+       if (!strcmp(refname, "HEAD"))
+               return xstrdup("origin");
+
+       if (!skip_prefix(refname, "refs/heads/", &refname))
+               die(_("Expecting a full ref name, got %s"), refname);
+
+       strbuf_addf(&sb, "branch.%s.remote", refname);
+       if (git_config_get_string(sb.buf, &dest))
+               ret = xstrdup("origin");
+       else
+               ret = dest;
+
+       strbuf_release(&sb);
+       return ret;
+}
+
+static int starts_with_dot_slash(const char *str)
+{
+       return str[0] == '.' && is_dir_sep(str[1]);
+}
+
+static int starts_with_dot_dot_slash(const char *str)
+{
+       return str[0] == '.' && str[1] == '.' && is_dir_sep(str[2]);
+}
+
+/*
+ * Returns 1 if it was the last chop before ':'.
+ */
+static int chop_last_dir(char **remoteurl, int is_relative)
+{
+       char *rfind = find_last_dir_sep(*remoteurl);
+       if (rfind) {
+               *rfind = '\0';
+               return 0;
+       }
+
+       rfind = strrchr(*remoteurl, ':');
+       if (rfind) {
+               *rfind = '\0';
+               return 1;
+       }
+
+       if (is_relative || !strcmp(".", *remoteurl))
+               die(_("cannot strip one component off url '%s'"),
+                       *remoteurl);
+
+       free(*remoteurl);
+       *remoteurl = xstrdup(".");
+       return 0;
+}
+
+/*
+ * The `url` argument is the URL that navigates to the submodule origin
+ * repo. When relative, this URL is relative to the superproject origin
+ * URL repo. The `up_path` argument, if specified, is the relative
+ * path that navigates from the submodule working tree to the superproject
+ * working tree. Returns the origin URL of the submodule.
+ *
+ * Return either an absolute URL or filesystem path (if the superproject
+ * origin URL is an absolute URL or filesystem path, respectively) or a
+ * relative file system path (if the superproject origin URL is a relative
+ * file system path).
+ *
+ * When the output is a relative file system path, the path is either
+ * relative to the submodule working tree, if up_path is specified, or to
+ * the superproject working tree otherwise.
+ *
+ * NEEDSWORK: This works incorrectly on the domain and protocol part.
+ * remote_url      url              outcome          expectation
+ * http://a.com/b  ../c             http://a.com/c   as is
+ * http://a.com/b  ../../c          http://c         error out
+ * http://a.com/b  ../../../c       http:/c          error out
+ * http://a.com/b  ../../../../c    http:c           error out
+ * http://a.com/b  ../../../../../c    .:c           error out
+ * NEEDSWORK: Given how chop_last_dir() works, this function is broken
+ * when a local part has a colon in its path component, too.
+ */
+static char *relative_url(const char *remote_url,
+                               const char *url,
+                               const char *up_path)
+{
+       int is_relative = 0;
+       int colonsep = 0;
+       char *out;
+       char *remoteurl = xstrdup(remote_url);
+       struct strbuf sb = STRBUF_INIT;
+       size_t len = strlen(remoteurl);
+
+       if (is_dir_sep(remoteurl[len]))
+               remoteurl[len] = '\0';
+
+       if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
+               is_relative = 0;
+       else {
+               is_relative = 1;
+               /*
+                * Prepend a './' to ensure all relative
+                * remoteurls start with './' or '../'
+                */
+               if (!starts_with_dot_slash(remoteurl) &&
+                   !starts_with_dot_dot_slash(remoteurl)) {
+                       strbuf_reset(&sb);
+                       strbuf_addf(&sb, "./%s", remoteurl);
+                       free(remoteurl);
+                       remoteurl = strbuf_detach(&sb, NULL);
+               }
+       }
+       /*
+        * When the url starts with '../', remove that and the
+        * last directory in remoteurl.
+        */
+       while (url) {
+               if (starts_with_dot_dot_slash(url)) {
+                       url += 3;
+                       colonsep |= chop_last_dir(&remoteurl, is_relative);
+               } else if (starts_with_dot_slash(url))
+                       url += 2;
+               else
+                       break;
+       }
+       strbuf_reset(&sb);
+       strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url);
+       free(remoteurl);
+
+       if (starts_with_dot_slash(sb.buf))
+               out = xstrdup(sb.buf + 2);
+       else
+               out = xstrdup(sb.buf);
+       strbuf_reset(&sb);
+
+       if (!up_path || !is_relative)
+               return out;
+
+       strbuf_addf(&sb, "%s%s", up_path, out);
+       free(out);
+       return strbuf_detach(&sb, NULL);
+}
+
+static int resolve_relative_url(int argc, const char **argv, const char *prefix)
+{
+       char *remoteurl = NULL;
+       char *remote = get_default_remote();
+       const char *up_path = NULL;
+       char *res;
+       const char *url;
+       struct strbuf sb = STRBUF_INIT;
+
+       if (argc != 2 && argc != 3)
+               die("resolve-relative-url only accepts one or two arguments");
+
+       url = argv[1];
+       strbuf_addf(&sb, "remote.%s.url", remote);
+       free(remote);
+
+       if (git_config_get_string(sb.buf, &remoteurl))
+               /* the repository is its own authoritative upstream */
+               remoteurl = xgetcwd();
+
+       if (argc == 3)
+               up_path = argv[2];
+
+       res = relative_url(remoteurl, url, up_path);
+       puts(res);
+       free(res);
+       free(remoteurl);
+       return 0;
+}
+
+static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)
+{
+       char *remoteurl, *res;
+       const char *up_path, *url;
+
+       if (argc != 4)
+               die("resolve-relative-url-test only accepts three arguments: <up_path> <remoteurl> <url>");
+
+       up_path = argv[1];
+       remoteurl = xstrdup(argv[2]);
+       url = argv[3];
+
+       if (!strcmp(up_path, "(null)"))
+               up_path = NULL;
+
+       res = relative_url(remoteurl, url, up_path);
+       puts(res);
+       free(res);
+       free(remoteurl);
+       return 0;
+}
 
 struct module_list {
        const struct cache_entry **entries;
@@ -82,10 +287,8 @@ static int module_list(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, module_list_options,
                             git_submodule_helper_usage, 0);
 
-       if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
-               printf("#unmatched\n");
+       if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
                return 1;
-       }
 
        for (i = 0; i < list.nr; i++) {
                const struct cache_entry *ce = list.entries[i];
@@ -100,6 +303,125 @@ static int module_list(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
+static void init_submodule(const char *path, const char *prefix, int quiet)
+{
+       const struct submodule *sub;
+       struct strbuf sb = STRBUF_INIT;
+       char *upd = NULL, *url = NULL, *displaypath;
+
+       /* Only loads from .gitmodules, no overlay with .git/config */
+       gitmodules_config();
+
+       if (prefix) {
+               strbuf_addf(&sb, "%s%s", prefix, path);
+               displaypath = strbuf_detach(&sb, NULL);
+       } else
+               displaypath = xstrdup(path);
+
+       sub = submodule_from_path(null_sha1, path);
+
+       if (!sub)
+               die(_("No url found for submodule path '%s' in .gitmodules"),
+                       displaypath);
+
+       /*
+        * Copy url setting when it is not set yet.
+        * To look up the url in .git/config, we must not fall back to
+        * .gitmodules, so look it up directly.
+        */
+       strbuf_reset(&sb);
+       strbuf_addf(&sb, "submodule.%s.url", sub->name);
+       if (git_config_get_string(sb.buf, &url)) {
+               url = xstrdup(sub->url);
+
+               if (!url)
+                       die(_("No url found for submodule path '%s' in .gitmodules"),
+                               displaypath);
+
+               /* Possibly a url relative to parent */
+               if (starts_with_dot_dot_slash(url) ||
+                   starts_with_dot_slash(url)) {
+                       char *remoteurl, *relurl;
+                       char *remote = get_default_remote();
+                       struct strbuf remotesb = STRBUF_INIT;
+                       strbuf_addf(&remotesb, "remote.%s.url", remote);
+                       free(remote);
+
+                       if (git_config_get_string(remotesb.buf, &remoteurl))
+                               /*
+                                * The repository is its own
+                                * authoritative upstream
+                                */
+                               remoteurl = xgetcwd();
+                       relurl = relative_url(remoteurl, url, NULL);
+                       strbuf_release(&remotesb);
+                       free(remoteurl);
+                       free(url);
+                       url = relurl;
+               }
+
+               if (git_config_set_gently(sb.buf, url))
+                       die(_("Failed to register url for submodule path '%s'"),
+                           displaypath);
+               if (!quiet)
+                       fprintf(stderr,
+                               _("Submodule '%s' (%s) registered for path '%s'\n"),
+                               sub->name, url, displaypath);
+       }
+
+       /* Copy "update" setting when it is not set yet */
+       strbuf_reset(&sb);
+       strbuf_addf(&sb, "submodule.%s.update", sub->name);
+       if (git_config_get_string(sb.buf, &upd) &&
+           sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
+               if (sub->update_strategy.type == SM_UPDATE_COMMAND) {
+                       fprintf(stderr, _("warning: command update mode suggested for submodule '%s'\n"),
+                               sub->name);
+                       upd = xstrdup("none");
+               } else
+                       upd = xstrdup(submodule_strategy_to_string(&sub->update_strategy));
+
+               if (git_config_set_gently(sb.buf, upd))
+                       die(_("Failed to register update mode for submodule path '%s'"), displaypath);
+       }
+       strbuf_release(&sb);
+       free(displaypath);
+       free(url);
+       free(upd);
+}
+
+static int module_init(int argc, const char **argv, const char *prefix)
+{
+       struct pathspec pathspec;
+       struct module_list list = MODULE_LIST_INIT;
+       int quiet = 0;
+       int i;
+
+       struct option module_init_options[] = {
+               OPT_STRING(0, "prefix", &prefix,
+                          N_("path"),
+                          N_("alternative anchor for relative paths")),
+               OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")),
+               OPT_END()
+       };
+
+       const char *const git_submodule_helper_usage[] = {
+               N_("git submodule--helper init [<path>]"),
+               NULL
+       };
+
+       argc = parse_options(argc, argv, prefix, module_init_options,
+                            git_submodule_helper_usage, 0);
+
+       if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+               return 1;
+
+       for (i = 0; i < list.nr; i++)
+               init_submodule(list.entries[i]->name, prefix, quiet);
+
+       return 0;
+}
+
 static int module_name(int argc, const char **argv, const char *prefix)
 {
        const struct submodule *sub;
@@ -118,6 +440,7 @@ static int module_name(int argc, const char **argv, const char *prefix)
 
        return 0;
 }
+
 static int clone_submodule(const char *path, const char *gitdir, const char *url,
                           const char *depth, const char *reference, int quiet)
 {
@@ -139,7 +462,7 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url
        argv_array_push(&cp.args, path);
 
        cp.git_cmd = 1;
-       cp.env = local_repo_env;
+       prepare_submodule_repo_env(&cp.env_array);
        cp.no_stdin = 1;
 
        return run_command(&cp);
@@ -180,16 +503,17 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 
        const char *const git_submodule_helper_usage[] = {
                N_("git submodule--helper clone [--prefix=<path>] [--quiet] "
-                  "[--reference <repository>] [--name <name>] [--url <url>]"
-                  "[--depth <depth>] [--] [<path>...]"),
+                  "[--reference <repository>] [--name <name>] [--depth <depth>] "
+                  "--url <url> --path <path>"),
                NULL
        };
 
        argc = parse_options(argc, argv, prefix, module_clone_options,
                             git_submodule_helper_usage, 0);
 
-       if (!path || !*path)
-               die(_("submodule--helper: unspecified or empty --path"));
+       if (argc || !url || !path || !*path)
+               usage_with_options(git_submodule_helper_usage,
+                                  module_clone_options);
 
        strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name);
        sm_gitdir = xstrdup(absolute_path(sb.buf));
@@ -244,6 +568,280 @@ static int module_clone(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
+struct submodule_update_clone {
+       /* index into 'list', the list of submodules to look into for cloning */
+       int current;
+       struct module_list list;
+       unsigned warn_if_uninitialized : 1;
+
+       /* update parameter passed via commandline */
+       struct submodule_update_strategy update;
+
+       /* configuration parameters which are passed on to the children */
+       int quiet;
+       const char *reference;
+       const char *depth;
+       const char *recursive_prefix;
+       const char *prefix;
+
+       /* Machine-readable status lines to be consumed by git-submodule.sh */
+       struct string_list projectlines;
+
+       /* If we want to stop as fast as possible and return an error */
+       unsigned quickstop : 1;
+};
+#define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
+       SUBMODULE_UPDATE_STRATEGY_INIT, 0, NULL, NULL, NULL, NULL, \
+       STRING_LIST_INIT_DUP, 0}
+
+
+static void next_submodule_warn_missing(struct submodule_update_clone *suc,
+               struct strbuf *out, const char *displaypath)
+{
+       /*
+        * Only mention uninitialized submodules when their
+        * paths have been specified.
+        */
+       if (suc->warn_if_uninitialized) {
+               strbuf_addf(out,
+                       _("Submodule path '%s' not initialized"),
+                       displaypath);
+               strbuf_addch(out, '\n');
+               strbuf_addstr(out,
+                       _("Maybe you want to use 'update --init'?"));
+               strbuf_addch(out, '\n');
+       }
+}
+
+/**
+ * Determine whether 'ce' needs to be cloned. If so, prepare the 'child' to
+ * run the clone. Returns 1 if 'ce' needs to be cloned, 0 otherwise.
+ */
+static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
+                                          struct child_process *child,
+                                          struct submodule_update_clone *suc,
+                                          struct strbuf *out)
+{
+       const struct submodule *sub = NULL;
+       struct strbuf displaypath_sb = STRBUF_INIT;
+       struct strbuf sb = STRBUF_INIT;
+       const char *displaypath = NULL;
+       char *url = NULL;
+       int needs_cloning = 0;
+
+       if (ce_stage(ce)) {
+               if (suc->recursive_prefix)
+                       strbuf_addf(&sb, "%s/%s", suc->recursive_prefix, ce->name);
+               else
+                       strbuf_addf(&sb, "%s", ce->name);
+               strbuf_addf(out, _("Skipping unmerged submodule %s"), sb.buf);
+               strbuf_addch(out, '\n');
+               goto cleanup;
+       }
+
+       sub = submodule_from_path(null_sha1, ce->name);
+
+       if (suc->recursive_prefix)
+               displaypath = relative_path(suc->recursive_prefix,
+                                           ce->name, &displaypath_sb);
+       else
+               displaypath = ce->name;
+
+       if (!sub) {
+               next_submodule_warn_missing(suc, out, displaypath);
+               goto cleanup;
+       }
+
+       if (suc->update.type == SM_UPDATE_NONE
+           || (suc->update.type == SM_UPDATE_UNSPECIFIED
+               && sub->update_strategy.type == SM_UPDATE_NONE)) {
+               strbuf_addf(out, _("Skipping submodule '%s'"), displaypath);
+               strbuf_addch(out, '\n');
+               goto cleanup;
+       }
+
+       /*
+        * Looking up the url in .git/config.
+        * We must not fall back to .gitmodules as we only want
+        * to process configured submodules.
+        */
+       strbuf_reset(&sb);
+       strbuf_addf(&sb, "submodule.%s.url", sub->name);
+       git_config_get_string(sb.buf, &url);
+       if (!url) {
+               next_submodule_warn_missing(suc, out, displaypath);
+               goto cleanup;
+       }
+
+       strbuf_reset(&sb);
+       strbuf_addf(&sb, "%s/.git", ce->name);
+       needs_cloning = !file_exists(sb.buf);
+
+       strbuf_reset(&sb);
+       strbuf_addf(&sb, "%06o %s %d %d\t%s\n", ce->ce_mode,
+                       sha1_to_hex(ce->sha1), ce_stage(ce),
+                       needs_cloning, ce->name);
+       string_list_append(&suc->projectlines, sb.buf);
+
+       if (!needs_cloning)
+               goto cleanup;
+
+       child->git_cmd = 1;
+       child->no_stdin = 1;
+       child->stdout_to_stderr = 1;
+       child->err = -1;
+       argv_array_push(&child->args, "submodule--helper");
+       argv_array_push(&child->args, "clone");
+       if (suc->quiet)
+               argv_array_push(&child->args, "--quiet");
+       if (suc->prefix)
+               argv_array_pushl(&child->args, "--prefix", suc->prefix, NULL);
+       argv_array_pushl(&child->args, "--path", sub->path, NULL);
+       argv_array_pushl(&child->args, "--name", sub->name, NULL);
+       argv_array_pushl(&child->args, "--url", url, NULL);
+       if (suc->reference)
+               argv_array_push(&child->args, suc->reference);
+       if (suc->depth)
+               argv_array_push(&child->args, suc->depth);
+
+cleanup:
+       free(url);
+       strbuf_reset(&displaypath_sb);
+       strbuf_reset(&sb);
+
+       return needs_cloning;
+}
+
+static int update_clone_get_next_task(struct child_process *child,
+                                     struct strbuf *err,
+                                     void *suc_cb,
+                                     void **void_task_cb)
+{
+       struct submodule_update_clone *suc = suc_cb;
+
+       for (; suc->current < suc->list.nr; suc->current++) {
+               const struct cache_entry *ce = suc->list.entries[suc->current];
+               if (prepare_to_clone_next_submodule(ce, child, suc, err)) {
+                       suc->current++;
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static int update_clone_start_failure(struct strbuf *err,
+                                     void *suc_cb,
+                                     void *void_task_cb)
+{
+       struct submodule_update_clone *suc = suc_cb;
+       suc->quickstop = 1;
+       return 1;
+}
+
+static int update_clone_task_finished(int result,
+                                     struct strbuf *err,
+                                     void *suc_cb,
+                                     void *void_task_cb)
+{
+       struct submodule_update_clone *suc = suc_cb;
+
+       if (!result)
+               return 0;
+
+       suc->quickstop = 1;
+       return 1;
+}
+
+static int update_clone(int argc, const char **argv, const char *prefix)
+{
+       const char *update = NULL;
+       int max_jobs = -1;
+       struct string_list_item *item;
+       struct pathspec pathspec;
+       struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
+
+       struct option module_update_clone_options[] = {
+               OPT_STRING(0, "prefix", &prefix,
+                          N_("path"),
+                          N_("path into the working tree")),
+               OPT_STRING(0, "recursive-prefix", &suc.recursive_prefix,
+                          N_("path"),
+                          N_("path into the working tree, across nested "
+                             "submodule boundaries")),
+               OPT_STRING(0, "update", &update,
+                          N_("string"),
+                          N_("rebase, merge, checkout or none")),
+               OPT_STRING(0, "reference", &suc.reference, N_("repo"),
+                          N_("reference repository")),
+               OPT_STRING(0, "depth", &suc.depth, "<depth>",
+                          N_("Create a shallow clone truncated to the "
+                             "specified number of revisions")),
+               OPT_INTEGER('j', "jobs", &max_jobs,
+                           N_("parallel jobs")),
+               OPT__QUIET(&suc.quiet, N_("don't print cloning progress")),
+               OPT_END()
+       };
+
+       const char *const git_submodule_helper_usage[] = {
+               N_("git submodule--helper update_clone [--prefix=<path>] [<path>...]"),
+               NULL
+       };
+       suc.prefix = prefix;
+
+       argc = parse_options(argc, argv, prefix, module_update_clone_options,
+                            git_submodule_helper_usage, 0);
+
+       if (update)
+               if (parse_submodule_update_strategy(update, &suc.update) < 0)
+                       die(_("bad value for update parameter"));
+
+       if (module_list_compute(argc, argv, prefix, &pathspec, &suc.list) < 0)
+               return 1;
+
+       if (pathspec.nr)
+               suc.warn_if_uninitialized = 1;
+
+       /* Overlay the parsed .gitmodules file with .git/config */
+       gitmodules_config();
+       git_config(submodule_config, NULL);
+
+       if (max_jobs < 0)
+               max_jobs = parallel_submodules();
+
+       run_processes_parallel(max_jobs,
+                              update_clone_get_next_task,
+                              update_clone_start_failure,
+                              update_clone_task_finished,
+                              &suc);
+
+       /*
+        * We saved the output and put it out all at once now.
+        * That means:
+        * - the listener does not have to interleave their (checkout)
+        *   work with our fetching.  The writes involved in a
+        *   checkout involve more straightforward sequential I/O.
+        * - the listener can avoid doing any work if fetching failed.
+        */
+       if (suc.quickstop)
+               return 1;
+
+       for_each_string_list_item(item, &suc.projectlines)
+               utf8_fprintf(stdout, "%s", item->string);
+
+       return 0;
+}
+
+static int resolve_relative_path(int argc, const char **argv, const char *prefix)
+{
+       struct strbuf sb = STRBUF_INIT;
+       if (argc != 3)
+               die("submodule--helper relative_path takes exactly 2 arguments, got %d", argc);
+
+       printf("%s", relative_path(argv[1], argv[2], &sb));
+       strbuf_release(&sb);
+       return 0;
+}
+
 struct cmd_struct {
        const char *cmd;
        int (*fn)(int, const char **, const char *);
@@ -253,19 +851,24 @@ static struct cmd_struct commands[] = {
        {"list", module_list},
        {"name", module_name},
        {"clone", module_clone},
+       {"update-clone", update_clone},
+       {"relative-path", resolve_relative_path},
+       {"resolve-relative-url", resolve_relative_url},
+       {"resolve-relative-url-test", resolve_relative_url_test},
+       {"init", module_init}
 };
 
 int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
 {
        int i;
        if (argc < 2)
-               die(_("fatal: submodule--helper subcommand must be "
+               die(_("submodule--helper subcommand must be "
                      "called with a subcommand"));
 
        for (i = 0; i < ARRAY_SIZE(commands); i++)
                if (!strcmp(argv[1], commands[i].cmd))
                        return commands[i].fn(argc - 1, argv + 1, prefix);
 
-       die(_("fatal: '%s' is not a valid submodule--helper "
+       die(_("'%s' is not a valid submodule--helper "
              "subcommand"), argv[1]);
 }
index 1705c9466546c7de52b12d3fbafbbe7b6d43cf4e..50e4ae5678c21f348c3ce0e0d0662c9d5f995847 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)
 {
@@ -104,13 +105,7 @@ static int delete_tag(const char *name, const char *ref,
 static int verify_tag(const char *name, const char *ref,
                                const unsigned char *sha1)
 {
-       const char *argv_verify_tag[] = {"verify-tag",
-                                       "-v", "SHA1_HEX", NULL};
-       argv_verify_tag[2] = sha1_to_hex(sha1);
-
-       if (run_command_v_opt(argv_verify_tag, RUN_GIT_CMD))
-               return error(_("could not verify the tag '%s'"), name);
-       return 0;
+       return gpg_verify_tag(sha1, name, GPG_VERIFY_VERBOSE);
 }
 
 static int do_sign(struct strbuf *buffer)
@@ -166,6 +161,11 @@ static int git_tag_config(const char *var, const char *value, void *cb)
        status = git_gpg_config(var, value, cb);
        if (status)
                return status;
+       if (!strcmp(var, "tag.forcesignannotated")) {
+               force_sign_annotate = git_config_bool(var, value);
+               return 0;
+       }
+
        if (starts_with(var, "column."))
                return git_column_config(var, value, "tag", &colopts);
        return git_default_config(var, value, cb);
@@ -327,7 +327,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        char *cleanup_arg = NULL;
        int create_reflog = 0;
        int annotate = 0, force = 0;
-       int cmdmode = 0;
+       int cmdmode = 0, create_tag_object = 0;
        const char *msgfile = NULL, *keyid = NULL;
        struct msg_arg msg = { 0, STRBUF_INIT };
        struct ref_transaction *transaction;
@@ -385,12 +385,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                opt.sign = 1;
                set_signing_key(keyid);
        }
-       if (opt.sign)
-               annotate = 1;
+       create_tag_object = (opt.sign || annotate || msg.given || msgfile);
+
        if (argc == 0 && !cmdmode)
                cmdmode = 'l';
 
-       if ((annotate || msg.given || msgfile || force) && (cmdmode != 0))
+       if ((create_tag_object || force) && (cmdmode != 0))
                usage_with_options(git_tag_usage, options);
 
        finalize_colopts(&colopts, -1);
@@ -431,7 +431,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        if (msg.given || msgfile) {
                if (msg.given && msgfile)
                        die(_("only one -F or -m option is allowed."));
-               annotate = 1;
                if (msg.given)
                        strbuf_addbuf(&buf, &(msg.buf));
                else {
@@ -474,8 +473,11 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        else
                die(_("Invalid cleanup mode %s"), cleanup_arg);
 
-       if (annotate)
+       if (create_tag_object) {
+               if (force_sign_annotate && !annotate)
+                       opt.sign = 1;
                create_tag(object, tag, &buf, &opt, prev, object);
+       }
 
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
index 1c94ca59bfcd756c94d86ef46592528e8fbfdcb1..b8b8522249337703eb66b245ad263e7dc1b41582 100644 (file)
@@ -255,7 +255,7 @@ static int process_lstat_error(const char *path, int err)
 {
        if (err == ENOENT || err == ENOTDIR)
                return remove_one_path(path);
-       return error("lstat(\"%s\"): %s", path, strerror(errno));
+       return error("lstat(\"%s\"): %s", path, strerror(err));
 }
 
 static int add_one_path(const struct cache_entry *old, const char *path, int len, struct stat *st)
index dbfe14f3fec3f8122e4855727bd7d1c3e4b3073e..2caedf184959e2120d8e30a02f885925ceb412b4 100644 (file)
@@ -104,8 +104,7 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
                pfd[1].events = POLLIN;
                if (poll(pfd, 2, -1) < 0) {
                        if (errno != EINTR) {
-                               error("poll failed resuming: %s",
-                                     strerror(errno));
+                               error_errno("poll failed resuming");
                                sleep(1);
                        }
                        continue;
index 00663f6a3003976aaa33f08ae7309fc190ea4748..99f8148cf79bac1d636bfe35a93282f3da2ebfd7 100644 (file)
@@ -18,55 +18,6 @@ static const char * const verify_tag_usage[] = {
                NULL
 };
 
-static int run_gpg_verify(const char *buf, unsigned long size, unsigned flags)
-{
-       struct signature_check sigc;
-       int len;
-       int ret;
-
-       memset(&sigc, 0, sizeof(sigc));
-
-       len = parse_signature(buf, size);
-
-       if (size == len) {
-               if (flags & GPG_VERIFY_VERBOSE)
-                       write_in_full(1, buf, len);
-               return error("no signature found");
-       }
-
-       ret = check_signature(buf, len, buf + len, size - len, &sigc);
-       print_signature_buffer(&sigc, flags);
-
-       signature_check_clear(&sigc);
-       return ret;
-}
-
-static int verify_tag(const char *name, unsigned flags)
-{
-       enum object_type type;
-       unsigned char sha1[20];
-       char *buf;
-       unsigned long size;
-       int ret;
-
-       if (get_sha1(name, sha1))
-               return error("tag '%s' not found.", name);
-
-       type = sha1_object_info(sha1, NULL);
-       if (type != OBJ_TAG)
-               return error("%s: cannot verify a non-tag object of type %s.",
-                               name, typename(type));
-
-       buf = read_sha1_file(sha1, &type, &size);
-       if (!buf)
-               return error("%s: unable to read file.", name);
-
-       ret = run_gpg_verify(buf, size, flags);
-
-       free(buf);
-       return ret;
-}
-
 static int git_verify_tag_config(const char *var, const char *value, void *cb)
 {
        int status = git_gpg_config(var, value, cb);
@@ -95,11 +46,13 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
        if (verbose)
                flags |= GPG_VERIFY_VERBOSE;
 
-       /* sometimes the program was terminated because this signal
-        * was received in the process of writing the gpg input: */
-       signal(SIGPIPE, SIG_IGN);
-       while (i < argc)
-               if (verify_tag(argv[i++], flags))
+       while (i < argc) {
+               unsigned char sha1[20];
+               const char *name = argv[i++];
+               if (get_sha1(name, sha1))
+                       had_error = !!error("tag '%s' not found.", name);
+               else if (gpg_verify_tag(sha1, name, flags))
                        had_error = 1;
+       }
        return had_error;
 }
index 38b56096bd3b914d1128e0bebb5c964a3cdd1b74..96a2834a18be8ee03427cfdd408914ea71d59243 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;
 };
@@ -109,7 +110,7 @@ static void prune_worktrees(void)
                if (ret < 0 && errno == ENOTDIR)
                        ret = unlink(path.buf);
                if (ret)
-                       error(_("failed to remove: %s"), strerror(errno));
+                       error_errno(_("failed to remove '%s'"), path.buf);
        }
        closedir(dir);
        if (!show_only)
@@ -204,7 +205,7 @@ static int add_worktree(const char *path, const char *refname,
        if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
                 ref_exists(symref.buf)) { /* it's a branch */
                if (!opts->force)
-                       die_if_checked_out(symref.buf);
+                       die_if_checked_out(symref.buf, 0);
        } else { /* must be a commit */
                commit = lookup_commit_reference_by_name(refname);
                if (!commit)
@@ -284,18 +285,22 @@ static int add_worktree(const char *path, const char *refname,
        if (ret)
                goto done;
 
-       cp.argv = NULL;
-       argv_array_clear(&cp.args);
-       argv_array_pushl(&cp.args, "reset", "--hard", NULL);
-       cp.env = child_env.argv;
-       ret = run_command(&cp);
-       if (!ret) {
-               is_junk = 0;
-               free(junk_work_tree);
-               free(junk_git_dir);
-               junk_work_tree = NULL;
-               junk_git_dir = NULL;
+       if (opts->checkout) {
+               cp.argv = NULL;
+               argv_array_clear(&cp.args);
+               argv_array_pushl(&cp.args, "reset", "--hard", NULL);
+               cp.env = child_env.argv;
+               ret = run_command(&cp);
+               if (ret)
+                       goto done;
        }
+
+       is_junk = 0;
+       free(junk_work_tree);
+       free(junk_git_dir);
+       junk_work_tree = NULL;
+       junk_git_dir = NULL;
+
 done:
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/locked", sb_repo.buf);
@@ -320,10 +325,12 @@ static int add(int ac, const char **av, const char *prefix)
                OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
                           N_("create or reset a branch")),
                OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
+               OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
                OPT_END()
        };
 
        memset(&opts, 0, sizeof(opts));
+       opts.checkout = 1;
        ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
        if (!!opts.detach + !!opts.new_branch + !!new_branch_force > 1)
                die(_("-b, -B, and --detach are mutually exclusive"));
@@ -342,7 +349,7 @@ static int add(int ac, const char **av, const char *prefix)
                if (!opts.force &&
                    !strbuf_check_branch_ref(&symref, opts.new_branch) &&
                    ref_exists(symref.buf))
-                       die_if_checked_out(symref.buf);
+                       die_if_checked_out(symref.buf, 0);
                strbuf_release(&symref);
        }
 
index 3ebf9c3aa44eb2ab25f573192b6449f63311ffd7..ddf0cc9f9aa51f577b0d1a764ebc3e0a318fe1ba 100644 (file)
@@ -663,7 +663,7 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
                        cnt++;
                else {
                        struct cache_tree_sub *sub;
-                       struct tree *subtree = lookup_tree(entry.sha1);
+                       struct tree *subtree = lookup_tree(entry.oid->hash);
                        if (!subtree->object.parsed)
                                parse_tree(subtree);
                        sub = cache_tree_sub(it, entry.path);
@@ -710,7 +710,7 @@ int cache_tree_matches_traversal(struct cache_tree *root,
 
        it = find_cache_tree_from_traversal(root, info);
        it = cache_tree_find(it, ent->path);
-       if (it && it->entry_count > 0 && !hashcmp(ent->sha1, it->sha1))
+       if (it && it->entry_count > 0 && !hashcmp(ent->oid->hash, it->sha1))
                return it->entry_count;
        return 0;
 }
diff --git a/cache.h b/cache.h
index 4ff196c25926cb1a6662f7582969ed665bae5ee3..6049f867113896def34306f22ac6927b8f0e8e1e 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -654,6 +654,7 @@ extern int warn_on_object_refname_ambiguity;
 extern const char *apply_default_whitespace;
 extern const char *apply_default_ignorewhitespace;
 extern const char *git_attributes_file;
+extern const char *git_hooks_path;
 extern int zlib_compression_level;
 extern int core_compression_level;
 extern int core_compression_seen;
@@ -807,11 +808,14 @@ extern void check_repository_format(void);
  */
 extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 
 extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
 extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
+extern void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+       __attribute__((format (printf, 2, 3)));
 extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
@@ -1162,6 +1166,8 @@ extern int get_sha1_blob(const char *str, unsigned char *sha1);
 extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
 extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc);
 
+extern int get_oid(const char *str, struct object_id *oid);
+
 typedef int each_abbrev_fn(const unsigned char *sha1, void *);
 extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
 
@@ -1772,8 +1778,8 @@ int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int
 extern int diff_auto_refresh_index;
 
 /* match-trees.c */
-void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
-void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *, const char *);
+void shift_tree(const struct object_id *, const struct object_id *, struct object_id *, int);
+void shift_tree_by(const struct object_id *, const struct object_id *, struct object_id *, const char *);
 
 /*
  * whitespace rules.
index 00d92a16631a80ff8ec4e995dafcd3e55434fad5..24b6542352a60006ac23c09a7cc17fb3aef009fa 100644 (file)
@@ -12,7 +12,7 @@ int main(int ac, char **av)
                struct stat st;
 
                if (lstat(ce->name, &st)) {
-                       error("lstat(%s): %s", ce->name, strerror(errno));
+                       error_errno("lstat(%s)", ce->name);
                        continue;
                }
 
index 0e1d4b0893ce0266a6d15f880bb6ad66eb2619fb..8f2313d5022326dc244fa8eba4a8640299ebeac8 100644 (file)
@@ -1005,8 +1005,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                        struct strbuf buf = STRBUF_INIT;
 
                        if (strbuf_readlink(&buf, elem->path, st.st_size) < 0) {
-                               error("readlink(%s): %s", elem->path,
-                                     strerror(errno));
+                               error_errno("readlink(%s)", elem->path);
                                return;
                        }
                        result_size = buf.len;
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 b905aea31bf53dc0f614d5e95c0dbd382818cbc4..6c7c9b60538d932d6dbc2d32774a6f64f8687c69 100644 (file)
@@ -28,13 +28,13 @@ void syslog(int priority, const char *fmt, ...)
        va_end(ap);
 
        if (str_len < 0) {
-               warning("vsnprintf failed: '%s'", strerror(errno));
+               warning_errno("vsnprintf failed");
                return;
        }
 
        str = malloc(st_add(str_len, 1));
        if (!str) {
-               warning("malloc failed: '%s'", strerror(errno));
+               warning_errno("malloc failed");
                return;
        }
 
@@ -45,7 +45,7 @@ void syslog(int priority, const char *fmt, ...)
        while ((pos = strstr(str, "%1")) != NULL) {
                str = realloc(str, st_add(++str_len, 1));
                if (!str) {
-                       warning("realloc failed: '%s'", strerror(errno));
+                       warning_errno("realloc failed");
                        return;
                }
                memmove(pos + 2, pos + 1, strlen(pos));
index 5dfa5ed61f49c4032d82c5825c0c43c86e04799f..3be60ce1c6fc0b5a6d96fdbe1101ed99e28b3585 100644 (file)
@@ -483,6 +483,7 @@ static size_t sizeof_ioinfo = 0;
 #define IOINFO_L2E 5
 #define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
 
+#define FPIPE 0x08
 #define FDEV  0x40
 
 static inline ioinfo* _pioinfo(int fd)
@@ -530,6 +531,45 @@ static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
        return old_handle;
 }
 
+#ifdef DETECT_MSYS_TTY
+
+#include <winternl.h>
+#include <ntstatus.h>
+
+static void detect_msys_tty(int fd)
+{
+       ULONG result;
+       BYTE buffer[1024];
+       POBJECT_NAME_INFORMATION nameinfo = (POBJECT_NAME_INFORMATION) buffer;
+       PWSTR name;
+
+       /* check if fd is a pipe */
+       HANDLE h = (HANDLE) _get_osfhandle(fd);
+       if (GetFileType(h) != FILE_TYPE_PIPE)
+               return;
+
+       /* get pipe name */
+       if (!NT_SUCCESS(NtQueryObject(h, ObjectNameInformation,
+                       buffer, sizeof(buffer) - 2, &result)))
+               return;
+       name = nameinfo->Name.Buffer;
+       name[nameinfo->Name.Length] = 0;
+
+       /* check if this could be a MSYS2 pty pipe ('msys-XXXX-ptyN-XX') */
+       if (!wcsstr(name, L"msys-") || !wcsstr(name, L"-pty"))
+               return;
+
+       /* init ioinfo size if we haven't done so */
+       if (init_sizeof_ioinfo())
+               return;
+
+       /* set FDEV flag, reset FPIPE flag */
+       _pioinfo(fd)->osflags &= ~FPIPE;
+       _pioinfo(fd)->osflags |= FDEV;
+}
+
+#endif
+
 void winansi_init(void)
 {
        int con1, con2;
@@ -538,8 +578,15 @@ void winansi_init(void)
        /* check if either stdout or stderr is a console output screen buffer */
        con1 = is_console(1);
        con2 = is_console(2);
-       if (!con1 && !con2)
+       if (!con1 && !con2) {
+#ifdef DETECT_MSYS_TTY
+               /* check if stdin / stdout / stderr are MSYS2 pty pipes */
+               detect_msys_tty(0);
+               detect_msys_tty(1);
+               detect_msys_tty(2);
+#endif
                return;
+       }
 
        /* create a named pipe to communicate with the console thread */
        xsnprintf(name, sizeof(name), "\\\\.\\pipe\\winansi%lu", GetCurrentProcessId());
@@ -575,8 +622,11 @@ void winansi_init(void)
 HANDLE winansi_get_osfhandle(int fd)
 {
        HANDLE hnd = (HANDLE) _get_osfhandle(fd);
-       if ((fd == 1 || fd == 2) && isatty(fd)
-           && GetFileType(hnd) == FILE_TYPE_PIPE)
-               return (fd == 1) ? hconsole1 : hconsole2;
+       if (isatty(fd) && GetFileType(hnd) == FILE_TYPE_PIPE) {
+               if (fd == 1 && hconsole1)
+                       return hconsole1;
+               else if (fd == 2 && hconsole2)
+                       return hconsole2;
+       }
        return hnd;
 }
index adf12a17ef41f70244df320df404d9ccaefd7b4b..f51c56bf92d384ace98e580c510842bd7eab9871 100644 (file)
--- a/config.c
+++ b/config.c
@@ -162,7 +162,7 @@ void git_config_push_parameter(const char *text)
 {
        struct strbuf env = STRBUF_INIT;
        const char *old = getenv(CONFIG_DATA_ENVIRONMENT);
-       if (old) {
+       if (old && *old) {
                strbuf_addstr(&env, old);
                strbuf_addch(&env, ' ');
        }
@@ -717,6 +717,9 @@ static int git_default_core_config(const char *var, const char *value)
        if (!strcmp(var, "core.attributesfile"))
                return git_config_pathname(&git_attributes_file, var, value);
 
+       if (!strcmp(var, "core.hookspath"))
+               return git_config_pathname(&git_hooks_path, var, value);
+
        if (!strcmp(var, "core.bare")) {
                is_bare_repository_cfg = git_config_bool(var, value);
                return 0;
@@ -803,8 +806,6 @@ static int git_default_core_config(const char *var, const char *value)
 
        if (!strcmp(var, "core.autocrlf")) {
                if (value && !strcasecmp(value, "input")) {
-                       if (core_eol == EOL_CRLF)
-                               return error("core.autocrlf=input conflicts with core.eol=crlf");
                        auto_crlf = AUTO_CRLF_INPUT;
                        return 0;
                }
@@ -830,8 +831,6 @@ static int git_default_core_config(const char *var, const char *value)
                        core_eol = EOL_NATIVE;
                else
                        core_eol = EOL_UNSET;
-               if (core_eol == EOL_CRLF && auto_crlf == AUTO_CRLF_INPUT)
-                       return error("core.autocrlf=input conflicts with core.eol=crlf");
                return 0;
        }
 
@@ -2017,7 +2016,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
        lock = xcalloc(1, sizeof(struct lock_file));
        fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (fd < 0) {
-               error("could not lock config file %s: %s", config_filename, strerror(errno));
+               error_errno("could not lock config file %s", config_filename);
                free(store.key);
                ret = CONFIG_NO_LOCK;
                goto out_free;
@@ -2031,8 +2030,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                free(store.key);
 
                if ( ENOENT != errno ) {
-                       error("opening %s: %s", config_filename,
-                             strerror(errno));
+                       error_errno("opening %s", config_filename);
                        ret = CONFIG_INVALID_FILE; /* same as "invalid config file" */
                        goto out_free;
                }
@@ -2116,8 +2114,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                if (contents == MAP_FAILED) {
                        if (errno == ENODEV && S_ISDIR(st.st_mode))
                                errno = EISDIR;
-                       error("unable to mmap '%s': %s",
-                             config_filename, strerror(errno));
+                       error_errno("unable to mmap '%s'", config_filename);
                        ret = CONFIG_INVALID_FILE;
                        contents = NULL;
                        goto out_free;
@@ -2126,8 +2123,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                in_fd = -1;
 
                if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
-                       error("chmod on %s failed: %s",
-                             get_lock_file_path(lock), strerror(errno));
+                       error_errno("chmod on %s failed", get_lock_file_path(lock));
                        ret = CONFIG_NO_WRITE;
                        goto out_free;
                }
@@ -2183,8 +2179,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
        }
 
        if (commit_lock_file(lock) < 0) {
-               error("could not write config file %s: %s", config_filename,
-                     strerror(errno));
+               error_errno("could not write config file %s", config_filename);
                ret = CONFIG_NO_WRITE;
                lock = NULL;
                goto out_free;
@@ -2335,8 +2330,8 @@ int git_config_rename_section_in_file(const char *config_filename,
        fstat(fileno(config_file), &st);
 
        if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
-               ret = error("chmod on %s failed: %s",
-                           get_lock_file_path(lock), strerror(errno));
+               ret = error_errno("chmod on %s failed",
+                                 get_lock_file_path(lock));
                goto out;
        }
 
@@ -2390,8 +2385,8 @@ int git_config_rename_section_in_file(const char *config_filename,
        fclose(config_file);
 unlock_and_out:
        if (commit_lock_file(lock) < 0)
-               ret = error("could not write config file %s: %s",
-                           config_filename, strerror(errno));
+               ret = error_errno("could not write config file %s",
+                                 config_filename);
 out:
        free(filename_buf);
        return ret;
index 40d6b29eeef9a9b89b02100bb087fe9f37a3a7d6..a88f13989ac9c2cbb303e2ae5f9ed43c9a5f778f 100644 (file)
@@ -557,7 +557,8 @@ else
                        BASIC_LDFLAGS += -Wl,--large-address-aware
                endif
                CC = gcc
-               COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0
+               COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY
+               EXTLIBS += -lntdll
                INSTALL = /bin/install
                NO_R_TO_GCC_LINKER = YesPlease
                INTERNAL_QSORT = YesPlease
index 299c56090bc38b1572b0ac03ec48532f40de605f..bf1b12e7ecaf476304811cabc870d58fc7f3f1dd 100644 (file)
@@ -86,17 +86,14 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
                memcpy(commit, sha1_to_hex(sha1), 40);
                if (write_in_full(rev_list.in, commit, 41) < 0) {
                        if (errno != EPIPE && errno != EINVAL)
-                               error(_("failed write to rev-list: %s"),
-                                     strerror(errno));
+                               error_errno(_("failed write to rev-list"));
                        err = -1;
                        break;
                }
        } while (!fn(cb_data, sha1));
 
-       if (close(rev_list.in)) {
-               error(_("failed to close rev-list's stdin: %s"), strerror(errno));
-               err = -1;
-       }
+       if (close(rev_list.in))
+               err = error_errno(_("failed to close rev-list's stdin"));
 
        sigchain_pop(SIGPIPE);
        return finish_command(&rev_list) || err;
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 53c71b422a7ac26511b76bc3854e1f04918096f6..100cc7a6d35c66e388d09dbd8d566b860a9b7c0d 100644 (file)
@@ -1,3 +1,11 @@
+Release 1.3.1 (bugfix-only release)
+===================================
+
+* Generate links to commits in combined emails (it was done only for
+  commit emails in 1.3.0).
+
+* Fix broken links on PyPi.
+
 Release 1.3.0
 =============
 
index 1e0480197843ee7e3a5a9152f98dcae8f3f56f14..0c91d19a57093a8676b126a3cc531840e6898d89 100644 (file)
@@ -1,4 +1,4 @@
-git-multimail 1.3.0
+git-multimail 1.3.1
 ===================
 
 .. image:: https://travis-ci.org/git-multimail/git-multimail.svg?branch=master
index ee1fa75f99f193bbb8f05b343b99f2c776aa1084..1210bde045c8ce850e49bf619a0c5fe35409030f 100644 (file)
@@ -6,10 +6,10 @@ website:
     https://github.com/git-multimail/git-multimail
 
 The version in this directory was obtained from the upstream project
-on May 03 2016 and consists of the "git-multimail" subdirectory from
+on May 13 2016 and consists of the "git-multimail" subdirectory from
 revision
 
-    26f3ae9f86aa7f8a054ba89235c4d3879f98b03d refs/tags/1.3.0
+    3ce5470d4abf7251604cbf64e73a962e1b617f5e refs/tags/1.3.1
 
 Please see the README file in this directory for information about how
 to report bugs or contribute to git-multimail.
index f2c92aeed8e45545887197e7e4dfbda1b366153c..54ab4a49429fd9a3a31cdc471befe4852db369a6 100755 (executable)
@@ -1,6 +1,6 @@
 #! /usr/bin/env python
 
-__version__ = '1.3.0'
+__version__ = '1.3.1'
 
 # Copyright (c) 2015 Matthieu Moy and others
 # Copyright (c) 2012-2014 Michael Haggerty and others
@@ -1704,6 +1704,14 @@ def generate_combined_email(self, push, revision, body_filter=None, extra_header
         self.header_template = COMBINED_HEADER_TEMPLATE
         self.intro_template = COMBINED_INTRO_TEMPLATE
         self.footer_template = COMBINED_FOOTER_TEMPLATE
+
+        def revision_gen_link(base_url):
+            # revision is used only to generate the body, and
+            # _content_type is set while generating headers. Get it
+            # from the BranchChange object.
+            revision._content_type = self._content_type
+            return revision.generate_browse_link(base_url)
+        self.generate_browse_link = revision_gen_link
         for line in self.generate_email(push, body_filter, values):
             yield line
 
index f524b8d7f4daf70a610a1e00af2ae6f1c23f8f62..b1614bf7ff0d38325baa427da09859fb4e36e1c9 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1380,27 +1380,22 @@ static struct stream_filter *ident_filter(const unsigned char *sha1)
 struct stream_filter *get_stream_filter(const char *path, const unsigned char *sha1)
 {
        struct conv_attrs ca;
-       enum crlf_action crlf_action;
        struct stream_filter *filter = NULL;
 
        convert_attrs(&ca, path);
-
        if (ca.drv && (ca.drv->smudge || ca.drv->clean))
-               return filter;
+               return NULL;
+
+       if (ca.crlf_action == CRLF_AUTO || ca.crlf_action == CRLF_AUTO_CRLF)
+               return NULL;
 
        if (ca.ident)
                filter = ident_filter(sha1);
 
-       crlf_action = ca.crlf_action;
-
-       if ((crlf_action == CRLF_BINARY) ||
-                       crlf_action == CRLF_AUTO_INPUT ||
-                       (crlf_action == CRLF_TEXT_INPUT))
-               filter = cascade_filter(filter, &null_filter_singleton);
-
-       else if (output_eol(crlf_action) == EOL_CRLF &&
-                !(crlf_action == CRLF_AUTO || crlf_action == CRLF_AUTO_CRLF))
+       if (output_eol(ca.crlf_action) == EOL_CRLF)
                filter = cascade_filter(filter, lf_to_crlf_filter());
+       else
+               filter = cascade_filter(filter, &null_filter_singleton);
 
        return filter;
 }
diff --git a/copy.c b/copy.c
index 574fa1f09dadc2e9cc9a39702bafb352f71a56c4..4de6a110f0912d81def3f2dd4ffd6ddc9bc30d2f 100644 (file)
--- a/copy.c
+++ b/copy.c
@@ -42,15 +42,15 @@ int copy_file(const char *dst, const char *src, int mode)
        status = copy_fd(fdi, fdo);
        switch (status) {
        case COPY_READ_ERROR:
-               error("copy-fd: read returned %s", strerror(errno));
+               error_errno("copy-fd: read returned");
                break;
        case COPY_WRITE_ERROR:
-               error("copy-fd: write returned %s", strerror(errno));
+               error_errno("copy-fd: write returned");
                break;
        }
        close(fdi);
        if (close(fdo) != 0)
-               return error("%s: close error: %s", dst, strerror(errno));
+               return error_errno("%s: close error", dst);
 
        if (!status && adjust_shared_perm(dst))
                return -1;
index 291c0fd5e935b5abedc629697d44e5a9f57727bd..1f14d56e98834e73dce91f3a20c3e3795ba6fb37 100644 (file)
@@ -179,12 +179,12 @@ static int serve_cache_loop(int fd)
 
                client = accept(fd, NULL, NULL);
                if (client < 0) {
-                       warning("accept failed: %s", strerror(errno));
+                       warning_errno("accept failed");
                        return 1;
                }
                client2 = dup(client);
                if (client2 < 0) {
-                       warning("dup failed: %s", strerror(errno));
+                       warning_errno("dup failed");
                        close(client);
                        return 1;
                }
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);
        }
index 03daadb25af835a695c7d0baf466b0e1f82b58c4..1f8999b9cabf52c85b63f27178023ff53b7567c3 100644 (file)
@@ -65,8 +65,7 @@ static int populate_from_stdin(struct diff_filespec *s)
        size_t size = 0;
 
        if (strbuf_read(&buf, 0, 0) < 0)
-               return error("error while reading from stdin %s",
-                                    strerror(errno));
+               return error_errno("error while reading from stdin");
 
        s->should_munmap = 0;
        s->data = strbuf_detach(&buf, &size);
diff --git a/diff.c b/diff.c
index 059123c5dcef4129763895b0f2ad5a54728b0c07..d3734d3181b7eebedf80bf6f3d534c61961dca9f 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -26,6 +26,7 @@
 #endif
 
 static int diff_detect_rename_default;
+static int diff_compaction_heuristic = 1;
 static int diff_rename_limit_default = 400;
 static int diff_suppress_blank_empty;
 static int diff_use_color_default = -1;
@@ -168,6 +169,11 @@ long parse_algorithm_value(const char *value)
  * never be affected by the setting of diff.renames
  * the user happens to have in the configuration file.
  */
+void init_diff_ui_defaults(void)
+{
+       diff_detect_rename_default = 1;
+}
+
 int git_diff_ui_config(const char *var, const char *value, void *cb)
 {
        if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
@@ -184,6 +190,10 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                diff_detect_rename_default = git_config_rename(var, value);
                return 0;
        }
+       if (!strcmp(var, "diff.compactionheuristic")) {
+               diff_compaction_heuristic = git_config_bool(var, value);
+               return 0;
+       }
        if (!strcmp(var, "diff.autorefreshindex")) {
                diff_auto_refresh_index = git_config_bool(var, value);
                return 0;
@@ -3273,6 +3283,8 @@ void diff_setup(struct diff_options *options)
        options->use_color = diff_use_color_default;
        options->detect_rename = diff_detect_rename_default;
        options->xdl_opts |= diff_algorithm;
+       if (diff_compaction_heuristic)
+               DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
 
        options->orderfile = diff_order_file_cfg;
 
@@ -3793,6 +3805,10 @@ int diff_opt_parse(struct diff_options *options,
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
        else if (!strcmp(arg, "--ignore-blank-lines"))
                DIFF_XDL_SET(options, IGNORE_BLANK_LINES);
+       else if (!strcmp(arg, "--compaction-heuristic"))
+               DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
+       else if (!strcmp(arg, "--no-compaction-heuristic"))
+               DIFF_XDL_CLR(options, COMPACTION_HEURISTIC);
        else if (!strcmp(arg, "--patience"))
                options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
        else if (!strcmp(arg, "--histogram"))
diff --git a/diff.h b/diff.h
index 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 *);
diff --git a/dir.c b/dir.c
index 656f272adc69b633ddd06277bf9cc77205a7e8ee..6172b3438d356d0359c2158708c718c6694e183a 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -53,13 +53,12 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
        int check_only, const struct path_simplify *simplify);
 static int get_dtype(struct dirent *de, const char *path, int len);
 
-/* helper string functions with support for the ignore_case flag */
-int strcmp_icase(const char *a, const char *b)
+int fspathcmp(const char *a, const char *b)
 {
        return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
 }
 
-int strncmp_icase(const char *a, const char *b, size_t count)
+int fspathncmp(const char *a, const char *b, size_t count)
 {
        return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
 }
@@ -795,12 +794,12 @@ int match_basename(const char *basename, int basenamelen,
 {
        if (prefix == patternlen) {
                if (patternlen == basenamelen &&
-                   !strncmp_icase(pattern, basename, basenamelen))
+                   !fspathncmp(pattern, basename, basenamelen))
                        return 1;
        } else if (flags & EXC_FLAG_ENDSWITH) {
                /* "*literal" matching against "fooliteral" */
                if (patternlen - 1 <= basenamelen &&
-                   !strncmp_icase(pattern + 1,
+                   !fspathncmp(pattern + 1,
                                   basename + basenamelen - (patternlen - 1),
                                   patternlen - 1))
                        return 1;
@@ -837,7 +836,7 @@ int match_pathname(const char *pathname, int pathlen,
         */
        if (pathlen < baselen + 1 ||
            (baselen && pathname[baselen] != '/') ||
-           strncmp_icase(pathname, base, baselen))
+           fspathncmp(pathname, base, baselen))
                return 0;
 
        namelen = baselen ? pathlen - baselen - 1 : pathlen;
@@ -851,7 +850,7 @@ int match_pathname(const char *pathname, int pathlen,
                if (prefix > namelen)
                        return 0;
 
-               if (strncmp_icase(pattern, name, prefix))
+               if (fspathncmp(pattern, name, prefix))
                        return 0;
                pattern += prefix;
                patternlen -= prefix;
diff --git a/dir.h b/dir.h
index d56d2fb48f68191d54fdfb8c5c104cb87cba1c41..bfde698c488adcc75b7a294476c622afb6f92b36 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -270,8 +270,8 @@ extern int remove_dir_recursively(struct strbuf *path, int flag);
 /* tries to remove the path with empty directories along it, ignores ENOENT */
 extern int remove_path(const char *path);
 
-extern int strcmp_icase(const char *a, const char *b);
-extern int strncmp_icase(const char *a, const char *b, size_t count);
+extern int fspathcmp(const char *a, const char *b);
+extern int fspathncmp(const char *a, const char *b, size_t count);
 
 /*
  * The prefix part of pattern must not contains wildcards.
index 01c644cddbe8e57e31cc711aa0ca3838cf23c64d..7519edecdc297a0c0fdbbb2f3c68cb55abf2e8b2 100644 (file)
--- a/editor.c
+++ b/editor.c
@@ -63,7 +63,6 @@ int launch_editor(const char *path, struct strbuf *buffer, const char *const *en
        if (!buffer)
                return 0;
        if (strbuf_read_file(buffer, path, 0) < 0)
-               return error("could not read file '%s': %s",
-                               path, strerror(errno));
+               return error_errno("could not read file '%s'", path);
        return 0;
 }
diff --git a/entry.c b/entry.c
index a4109574fa72e0f3f32ad7b9e1ed0e402c8aecff..519e04227b72f92a338836ce457d2a5be2b80218 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -168,8 +168,8 @@ static int write_entry(struct cache_entry *ce,
                        ret = symlink(new, path);
                        free(new);
                        if (ret)
-                               return error("unable to create symlink %s (%s)",
-                                            path, strerror(errno));
+                               return error_errno("unable to create symlink %s",
+                                                  path);
                        break;
                }
 
@@ -186,8 +186,7 @@ static int write_entry(struct cache_entry *ce,
                fd = open_output_fd(path, ce, to_tempfile);
                if (fd < 0) {
                        free(new);
-                       return error("unable to create file %s (%s)",
-                               path, strerror(errno));
+                       return error_errno("unable to create file %s", path);
                }
 
                wrote = write_in_full(fd, new, size);
@@ -284,8 +283,7 @@ int checkout_entry(struct cache_entry *ce,
                                return error("%s is a directory", path.buf);
                        remove_subtree(&path);
                } else if (unlink(path.buf))
-                       return error("unable to unlink old '%s' (%s)",
-                                    path.buf, strerror(errno));
+                       return error_errno("unable to unlink old '%s'", path.buf);
        } else if (state->not_new)
                return 0;
 
index 96160a75a53340fcc9b2e1a81d2198d073bd4562..ca72464a985021e58b898bb0b1f9af6c4a5282ac 100644 (file)
@@ -31,6 +31,7 @@ const char *git_log_output_encoding;
 const char *apply_default_whitespace;
 const char *apply_default_ignorewhitespace;
 const char *git_attributes_file;
+const char *git_hooks_path;
 int zlib_compression_level = Z_BEST_SPEED;
 int core_compression_level;
 int core_compression_seen;
index 9fc7093406b1e2085a3b2180a8506bcd6d486e06..c504ef752db124e21156be5b92360dbe432e568f 100644 (file)
@@ -329,6 +329,7 @@ static const char *export_marks_file;
 static const char *import_marks_file;
 static int import_marks_file_from_stream;
 static int import_marks_file_ignore_missing;
+static int import_marks_file_done;
 static int relative_marks_paths;
 
 /* Our last blob */
@@ -414,7 +415,7 @@ static void write_crash_report(const char *err)
        struct recent_command *rc;
 
        if (!rpt) {
-               error("can't write crash report %s: %s", loc, strerror(errno));
+               error_errno("can't write crash report %s", loc);
                free(loc);
                return;
        }
@@ -1512,7 +1513,7 @@ static int tree_content_set(
        t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
-               if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+               if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
                        if (!*slash1) {
                                if (!S_ISDIR(mode)
                                                && e->versions[1].mode == mode
@@ -1602,7 +1603,7 @@ static int tree_content_remove(
        t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
-               if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+               if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
                        if (*slash1 && !S_ISDIR(e->versions[1].mode))
                                /*
                                 * If p names a file in some subdirectory, and a
@@ -1669,7 +1670,7 @@ static int tree_content_get(
        t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
-               if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+               if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
                        if (!*slash1)
                                goto found_entry;
                        if (!S_ISDIR(e->versions[1].mode))
@@ -1802,12 +1803,12 @@ static void dump_marks(void)
        static struct lock_file mark_lock;
        FILE *f;
 
-       if (!export_marks_file)
+       if (!export_marks_file || (import_marks_file && !import_marks_file_done))
                return;
 
        if (hold_lock_file_for_update(&mark_lock, export_marks_file, 0) < 0) {
-               failure |= error("Unable to write marks file %s: %s",
-                       export_marks_file, strerror(errno));
+               failure |= error_errno("Unable to write marks file %s",
+                                      export_marks_file);
                return;
        }
 
@@ -1822,8 +1823,8 @@ static void dump_marks(void)
 
        dump_marks_helper(f, 0, marks);
        if (commit_lock_file(&mark_lock)) {
-               failure |= error("Unable to write file %s: %s",
-                       export_marks_file, strerror(errno));
+               failure |= error_errno("Unable to write file %s",
+                                      export_marks_file);
                return;
        }
 }
@@ -1835,7 +1836,7 @@ static void read_marks(void)
        if (f)
                ;
        else if (import_marks_file_ignore_missing && errno == ENOENT)
-               return; /* Marks file does not exist */
+               goto done; /* Marks file does not exist */
        else
                die_errno("cannot read '%s'", import_marks_file);
        while (fgets(line, sizeof(line), f)) {
@@ -1865,6 +1866,8 @@ static void read_marks(void)
                insert_mark(mark, e);
        }
        fclose(f);
+done:
+       import_marks_file_done = 1;
 }
 
 
diff --git a/fsck.c b/fsck.c
index 3366b3fb620fddaf542d7c23e1f6c1c564f8fbe8..05315451c56fe19b2a2e8663d1f4847070bc946f 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -313,9 +313,9 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
                if (S_ISGITLINK(entry.mode))
                        continue;
                if (S_ISDIR(entry.mode))
-                       result = options->walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data, options);
+                       result = options->walk(&lookup_tree(entry.oid->hash)->object, OBJ_TREE, data, options);
                else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))
-                       result = options->walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data, options);
+                       result = options->walk(&lookup_blob(entry.oid->hash)->object, OBJ_BLOB, data, options);
                else {
                        result = error("in tree %s: entry %s has bad mode %.6o",
                                        oid_to_hex(&tree->object.oid), entry.path, entry.mode);
@@ -451,11 +451,11 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
        while (desc.size) {
                unsigned mode;
                const char *name;
-               const unsigned char *sha1;
+               const struct object_id *oid;
 
-               sha1 = tree_entry_extract(&desc, &name, &mode);
+               oid = tree_entry_extract(&desc, &name, &mode);
 
-               has_null_sha1 |= is_null_sha1(sha1);
+               has_null_sha1 |= is_null_oid(oid);
                has_full_path |= !!strchr(name, '/');
                has_empty_name |= !*name;
                has_dot |= !strcmp(name, ".");
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 1f8b5f3b1f1ac17716681fee2d72b9c124a9b99c..49d4029b8dddcb06dc6bea3d5f47c020785e3ddf 100644 (file)
@@ -409,7 +409,9 @@ extern NORETURN void usagef(const char *err, ...) __attribute__((format (printf,
 extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern NORETURN void die_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
+extern int error_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
+extern void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
 
 #ifndef NO_OPENSSL
 #ifdef APPLE_COMMON_CRYPTO
index 488d14b153e50f69fcfb0c732cc8c23cf4b643de..ebd13baa6e06cf7007fdd5c48713f06cf511721d 100755 (executable)
@@ -138,6 +138,7 @@ sub setup_dir_diff
        my %submodule;
        my %symlink;
        my @working_tree = ();
+       my %working_tree_dups = ();
        my @rawdiff = split('\0', $diffrtn);
 
        my $i = 0;
@@ -188,6 +189,10 @@ sub setup_dir_diff
                }
 
                if ($rmode ne $null_mode) {
+                       # Avoid duplicate working_tree entries
+                       if ($working_tree_dups{$dst_path}++) {
+                               next;
+                       }
                        my ($use, $wt_sha1) = use_wt_file($repo, $workdir,
                                                          $dst_path, $rsha1);
                        if ($use) {
@@ -273,7 +278,7 @@ sub setup_dir_diff
        # temporary file to both the left and right directories to show the
        # change in the recorded SHA1 for the submodule.
        for my $path (keys %submodule) {
-               my $ok;
+               my $ok = 0;
                if (defined($submodule{$path}{left})) {
                        $ok = write_to_file("$ldir/$path",
                                "Subproject commit $submodule{$path}{left}");
@@ -289,7 +294,7 @@ sub setup_dir_diff
        # shows only the link itself, not the contents of the link target.
        # This loop replicates that behavior.
        for my $path (keys %symlink) {
-               my $ok;
+               my $ok = 0;
                if (defined($symlink{$path}{left})) {
                        $ok = write_to_file("$ldir/$path",
                                        $symlink{$path}{left});
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 92adcc0d0785f8b78fce2c49bbcc40e49020ee24..9abd00be212b3e55883c0f9fa0487c7b31a6039f 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 e752153f6f16247c23ff390e20ae18cf51158a30..b6593cf9a19144650cc476fae0243140f4c0a8bb 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -1167,6 +1167,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():
@@ -2318,6 +2327,15 @@ def extractFilesFromCommit(self, commit):
             fnum = fnum + 1
         return files
 
+    def extractJobsFromCommit(self, commit):
+        jobs = []
+        jnum = 0
+        while commit.has_key("job%s" % jnum):
+            job = commit["job%s" % jnum]
+            jobs.append(job)
+            jnum = jnum + 1
+        return jobs
+
     def stripRepoPath(self, path, prefixes):
         """When streaming files, this is called to map a p4 depot path
            to where it should go in git.  The prefixes are either
@@ -2663,6 +2681,7 @@ def hasBranchPrefix(self, path):
     def commit(self, details, files, branch, parent = ""):
         epoch = details["time"]
         author = details["user"]
+        jobs = self.extractJobsFromCommit(details)
 
         if self.verbose:
             print('commit into {0}'.format(branch))
@@ -2690,6 +2709,8 @@ def commit(self, details, files, branch, parent = ""):
 
         self.gitStream.write("data <<EOT\n")
         self.gitStream.write(details["desc"])
+        if len(jobs) > 0:
+            self.gitStream.write("\nJobs: %s" % (' '.join(jobs)))
         self.gitStream.write("\n[git-p4: depot-paths = \"%s\": change = %s" %
                              (','.join(self.branchPrefixes), details["change"]))
         if len(details['options']) > 0:
index 655ebaa4717fe6e43b4b16a69e5eb282140edd6d..6e96abcc3c6b942eed3e8c522f089d823b02a1fa 100644 (file)
@@ -192,7 +192,6 @@ make_patch () {
 die_with_patch () {
        echo "$1" > "$state_dir"/stopped-sha
        make_patch "$1"
-       git rerere
        die "$2"
 }
 
@@ -549,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)
@@ -866,12 +866,12 @@ add_exec_commands () {
 # $3: the input filename
 check_commit_sha () {
        badsha=0
-       if test -z $1
+       if test -z "$1"
        then
                badsha=1
        else
                sha1_verif="$(git rev-parse --verify --quiet $1^{commit})"
-               if test -z $sha1_verif
+               if test -z "$sha1_verif"
                then
                        badsha=1
                fi
index 2cc2a6d2734c0ef12222004f22d717f4f724d82f..8d43db9069f5c478fa6cb2deb6cf7d1d37f1e6b2 100644 (file)
@@ -67,7 +67,9 @@ call_merge () {
                GIT_MERGE_VERBOSITY=1 && export GIT_MERGE_VERBOSITY
        fi
        test -z "$strategy" && strategy=recursive
-       eval 'git-merge-$strategy' $strategy_opts '"$cmt^" -- "$hd" "$cmt"'
+       # If cmt doesn't have a parent, don't include it as a base
+       base=$(git rev-parse --verify --quiet $cmt^)
+       eval 'git-merge-$strategy' $strategy_opts $base ' -- "$hd" "$cmt"'
        rv=$?
        case "$rv" in
        0)
index cf60c4390870ef28cff08b6a173a435d41e2d470..44ede367ae0e24ad80ba91608d26ad3bd4bbd8da 100755 (executable)
@@ -87,7 +87,10 @@ preserve_merges=
 autosquash=
 keep_empty=
 test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
-gpg_sign_opt=
+case "$(git config --bool commit.gpgsign)" in
+true)  gpg_sign_opt=-S ;;
+*)     gpg_sign_opt= ;;
+esac
 
 read_basic_state () {
        test -f "$state_dir/head-name" &&
@@ -248,6 +251,7 @@ do
                ;;
        --exec=*)
                cmd="${cmd}exec ${1#--exec=}${LF}"
+               test -z "$interactive_rebase" && interactive_rebase=implied
                ;;
        --interactive)
                interactive_rebase=explicit
@@ -348,12 +352,6 @@ do
 done
 test $# -gt 2 && usage
 
-if test -n "$cmd" &&
-   test "$interactive_rebase" != explicit
-then
-       die "$(gettext "The --exec option must be used with the --interactive option")"
-fi
-
 if test -n "$action"
 then
        test -z "$in_progress" && die "$(gettext "No rebase in progress?")"
index c7c65e25f50e7b558952a5180081ad499f2a90fc..c7509e8da47cf296143e3fa53a7f431f7672d78e 100755 (executable)
@@ -185,7 +185,7 @@ store_stash () {
 
        git update-ref --create-reflog -m "$stash_msg" $ref_stash $w_commit
        ret=$?
-       test $ret != 0 && test -z $quiet &&
+       test $ret != 0 && test -z "$quiet" &&
        die "$(eval_gettext "Cannot update \$ref_stash with \$w_commit")"
        return $ret
 }
@@ -277,7 +277,7 @@ save_stash () {
                        git clean --force --quiet -d $CLEAN_X_OPTION
                fi
 
-               if test "$keep_index" = "t" && test -n $i_tree
+               if test "$keep_index" = "t" && test -n "$i_tree"
                then
                        git read-tree --reset -u $i_tree
                fi
index d56207ea05db971f8ebdf566848b31df86b81c64..7fe8a511b37a9705249132afedb57032fb65e86c 100755 (executable)
@@ -46,114 +46,6 @@ prefix=
 custom_name=
 depth=
 
-# The function takes at most 2 arguments. The first argument is the
-# URL that navigates to the submodule origin repo. When relative, this URL
-# is relative to the superproject origin URL repo. The second up_path
-# argument, if specified, is the relative path that navigates
-# from the submodule working tree to the superproject working tree.
-#
-# The output of the function is the origin URL of the submodule.
-#
-# The output will either be an absolute URL or filesystem path (if the
-# superproject origin URL is an absolute URL or filesystem path,
-# respectively) or a relative file system path (if the superproject
-# origin URL is a relative file system path).
-#
-# When the output is a relative file system path, the path is either
-# relative to the submodule working tree, if up_path is specified, or to
-# the superproject working tree otherwise.
-resolve_relative_url ()
-{
-       remote=$(get_default_remote)
-       remoteurl=$(git config "remote.$remote.url") ||
-               remoteurl=$(pwd) # the repository is its own authoritative upstream
-       url="$1"
-       remoteurl=${remoteurl%/}
-       sep=/
-       up_path="$2"
-
-       case "$remoteurl" in
-       *:*|/*)
-               is_relative=
-               ;;
-       ./*|../*)
-               is_relative=t
-               ;;
-       *)
-               is_relative=t
-               remoteurl="./$remoteurl"
-               ;;
-       esac
-
-       while test -n "$url"
-       do
-               case "$url" in
-               ../*)
-                       url="${url#../}"
-                       case "$remoteurl" in
-                       */*)
-                               remoteurl="${remoteurl%/*}"
-                               ;;
-                       *:*)
-                               remoteurl="${remoteurl%:*}"
-                               sep=:
-                               ;;
-                       *)
-                               if test -z "$is_relative" || test "." = "$remoteurl"
-                               then
-                                       die "$(eval_gettext "cannot strip one component off url '\$remoteurl'")"
-                               else
-                                       remoteurl=.
-                               fi
-                               ;;
-                       esac
-                       ;;
-               ./*)
-                       url="${url#./}"
-                       ;;
-               *)
-                       break;;
-               esac
-       done
-       remoteurl="$remoteurl$sep${url%/}"
-       echo "${is_relative:+${up_path}}${remoteurl#./}"
-}
-
-# Resolve a path to be relative to another path.  This is intended for
-# converting submodule paths when git-submodule is run in a subdirectory
-# and only handles paths where the directory separator is '/'.
-#
-# The output is the first argument as a path relative to the second argument,
-# which defaults to $wt_prefix if it is omitted.
-relative_path ()
-{
-       local target curdir result
-       target=$1
-       curdir=${2-$wt_prefix}
-       curdir=${curdir%/}
-       result=
-
-       while test -n "$curdir"
-       do
-               case "$target" in
-               "$curdir/"*)
-                       target=${target#"$curdir"/}
-                       break
-                       ;;
-               esac
-
-               result="${result}../"
-               if test "$curdir" = "${curdir%/*}"
-               then
-                       curdir=
-               else
-                       curdir="${curdir%/*}"
-               fi
-       done
-
-       echo "$result$target"
-}
-
 die_if_unmatched ()
 {
        if test "$1" = "#unmatched"
@@ -192,6 +84,17 @@ isnumber()
        n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
 }
 
+# Sanitize the local git environment for use within a submodule. We
+# can't simply use clear_local_git_env since we want to preserve some
+# of the settings from GIT_CONFIG_PARAMETERS.
+sanitize_submodule_env()
+{
+       save_config=$GIT_CONFIG_PARAMETERS
+       clear_local_git_env
+       GIT_CONFIG_PARAMETERS=$save_config
+       export GIT_CONFIG_PARAMETERS
+}
+
 #
 # Add a new submodule to the working tree, .gitmodules and the index
 #
@@ -281,7 +184,7 @@ cmd_add()
                die "$(gettext "Relative path can only be used from the toplevel of the working tree")"
 
                # dereference source url relative to parent's url
-               realrepo=$(resolve_relative_url "$repo") || exit
+               realrepo=$(git submodule--helper resolve-relative-url "$repo") || exit
                ;;
        *:*|/*)
                # absolute url
@@ -347,9 +250,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
@@ -407,20 +310,23 @@ cmd_foreach()
        # command in the subshell (and a recursive call to this function)
        exec 3<&0
 
-       git submodule--helper list --prefix "$wt_prefix"|
+       {
+               git submodule--helper list --prefix "$wt_prefix" ||
+               echo "#unmatched"
+       } |
        while read mode sha1 stage sm_path
        do
                die_if_unmatched "$mode"
                if test -e "$sm_path"/.git
                then
-                       displaypath=$(relative_path "$prefix$sm_path")
+                       displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
                        say "$(eval_gettext "Entering '\$displaypath'")"
                        name=$(git submodule--helper name "$sm_path")
                        (
                                prefix="$prefix$sm_path/"
-                               clear_local_git_env
+                               sanitize_submodule_env
                                cd "$sm_path" &&
-                               sm_path=$(relative_path "$sm_path") &&
+                               sm_path=$(git submodule--helper relative-path "$sm_path" "$wt_prefix") &&
                                # we make $path available to scripts ...
                                path=$sm_path &&
                                if test $# -eq 1
@@ -467,50 +373,7 @@ cmd_init()
                shift
        done
 
-       git submodule--helper list --prefix "$wt_prefix" "$@" |
-       while read mode sha1 stage sm_path
-       do
-               die_if_unmatched "$mode"
-               name=$(git submodule--helper name "$sm_path") || exit
-
-               displaypath=$(relative_path "$prefix$sm_path")
-
-               # Copy url setting when it is not set yet
-               if test -z "$(git config "submodule.$name.url")"
-               then
-                       url=$(git config -f .gitmodules submodule."$name".url)
-                       test -z "$url" &&
-                       die "$(eval_gettext "No url found for submodule path '\$displaypath' in .gitmodules")"
-
-                       # Possibly a url relative to parent
-                       case "$url" in
-                       ./*|../*)
-                               url=$(resolve_relative_url "$url") || exit
-                               ;;
-                       esac
-                       git config submodule."$name".url "$url" ||
-                       die "$(eval_gettext "Failed to register url for submodule path '\$displaypath'")"
-
-                       say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$displaypath'")"
-               fi
-
-               # Copy "update" setting when it is not set yet
-               if upd="$(git config -f .gitmodules submodule."$name".update)" &&
-                  test -n "$upd" &&
-                  test -z "$(git config submodule."$name".update)"
-               then
-                       case "$upd" in
-                       checkout | rebase | merge | none)
-                               ;; # known modes of updating
-                       *)
-                               echo >&2 "warning: unknown update mode '$upd' suggested for submodule '$name'"
-                               upd=none
-                               ;;
-                       esac
-                       git config submodule."$name".update "$upd" ||
-                       die "$(eval_gettext "Failed to register update mode for submodule path '\$displaypath'")"
-               fi
-       done
+       git ${wt_prefix:+-C "$wt_prefix"} submodule--helper init ${GIT_QUIET:+--quiet} ${prefix:+--prefix "$prefix"} "$@"
 }
 
 #
@@ -558,13 +421,16 @@ cmd_deinit()
                die "$(eval_gettext "Use '--all' if you really want to deinitialize all submodules")"
        fi
 
-       git submodule--helper list --prefix "$wt_prefix" "$@" |
+       {
+               git submodule--helper list --prefix "$wt_prefix" "$@" ||
+               echo "#unmatched"
+       } |
        while read mode sha1 stage sm_path
        do
                die_if_unmatched "$mode"
                name=$(git submodule--helper name "$sm_path") || exit
 
-               displaypath=$(relative_path "$sm_path")
+               displaypath=$(git submodule--helper relative-path "$sm_path" "$wt_prefix")
 
                # Remove the submodule work tree (unless the user already did it)
                if test -d "$sm_path"
@@ -601,14 +467,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
        '')
@@ -672,6 +538,14 @@ cmd_update()
                --depth=*)
                        depth=$1
                        ;;
+               -j|--jobs)
+                       case "$2" in '') usage ;; esac
+                       jobs="--jobs=$2"
+                       shift
+                       ;;
+               --jobs=*)
+                       jobs=$1
+                       ;;
                --)
                        shift
                        break
@@ -691,17 +565,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)
@@ -716,31 +594,14 @@ cmd_update()
                        fi
                fi
 
-               displaypath=$(relative_path "$prefix$sm_path")
-
-               if test "$update_module" = "none"
-               then
-                       echo "Skipping submodule '$displaypath'"
-                       continue
-               fi
-
-               if test -z "$url"
-               then
-                       # Only mention uninitialized submodules when its
-                       # path have been specified
-                       test "$#" != "0" &&
-                       say "$(eval_gettext "Submodule path '\$displaypath' not initialized
-Maybe you want to use 'update --init'?")"
-                       continue
-               fi
+               displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
 
-               if ! test -d "$sm_path"/.git && ! test -f "$sm_path"/.git
+               if test $just_cloned -eq 1
                then
-                       git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$prefix" --path "$sm_path" --name "$name" --url "$url" "$reference" "$depth" || exit
-                       cloned_modules="$cloned_modules;$name"
                        subsha1=
+                       update_module=checkout
                else
-                       subsha1=$(clear_local_git_env; cd "$sm_path" &&
+                       subsha1=$(sanitize_submodule_env; cd "$sm_path" &&
                                git rev-parse --verify HEAD) ||
                        die "$(eval_gettext "Unable to find current revision in submodule path '\$displaypath'")"
                fi
@@ -750,11 +611,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
@@ -783,13 +644,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)
@@ -819,7 +673,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"
@@ -834,8 +688,9 @@ Maybe you want to use 'update --init'?")"
                if test -n "$recursive"
                then
                        (
-                               prefix="$prefix$sm_path/"
-                               clear_local_git_env
+                               prefix=$(git submodule--helper relative-path "$prefix$sm_path/" "$wt_prefix")
+                               wt_prefix=
+                               sanitize_submodule_env
                                cd "$sm_path" &&
                                eval cmd_update
                        )
@@ -873,7 +728,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 ||
@@ -1017,7 +872,7 @@ cmd_summary() {
                ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_dst^0 >/dev/null &&
                missing_dst=t
 
-               display_name=$(relative_path "$name")
+               display_name=$(git submodule--helper relative-path "$name" "$wt_prefix")
 
                total_commits=
                case "$missing_src,$missing_dst" in
@@ -1129,13 +984,16 @@ cmd_status()
                shift
        done
 
-       git submodule--helper list --prefix "$wt_prefix" "$@" |
+       {
+               git submodule--helper list --prefix "$wt_prefix" "$@" ||
+               echo "#unmatched"
+       } |
        while read mode sha1 stage sm_path
        do
                die_if_unmatched "$mode"
                name=$(git submodule--helper name "$sm_path") || exit
                url=$(git config submodule."$name".url)
-               displaypath=$(relative_path "$prefix$sm_path")
+               displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
                if test "$stage" = U
                then
                        say "U$sha1 $displaypath"
@@ -1157,7 +1015,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"
@@ -1167,7 +1025,7 @@ cmd_status()
                then
                        (
                                prefix="$displaypath/"
-                               clear_local_git_env
+                               sanitize_submodule_env
                                wt_prefix=
                                cd "$sm_path" &&
                                eval cmd_status
@@ -1207,7 +1065,10 @@ cmd_sync()
                esac
        done
        cd_to_toplevel
-       git submodule--helper list --prefix "$wt_prefix" "$@" |
+       {
+               git submodule--helper list --prefix "$wt_prefix" "$@" ||
+               echo "#unmatched"
+       } |
        while read mode sha1 stage sm_path
        do
                die_if_unmatched "$mode"
@@ -1223,9 +1084,9 @@ cmd_sync()
                        # guarantee a trailing /
                        up_path=${up_path%/}/ &&
                        # path from submodule work tree to submodule origin repo
-                       sub_origin_url=$(resolve_relative_url "$url" "$up_path") &&
+                       sub_origin_url=$(git submodule--helper resolve-relative-url "$url" "$up_path") &&
                        # path from superproject work tree to submodule origin repo
-                       super_config_url=$(resolve_relative_url "$url") || exit
+                       super_config_url=$(git submodule--helper resolve-relative-url "$url") || exit
                        ;;
                *)
                        sub_origin_url="$url"
@@ -1235,14 +1096,14 @@ cmd_sync()
 
                if git config "submodule.$name.url" >/dev/null 2>/dev/null
                then
-                       displaypath=$(relative_path "$prefix$sm_path")
+                       displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
                        say "$(eval_gettext "Synchronizing submodule url for '\$displaypath'")"
                        git config submodule."$name".url "$super_config_url"
 
                        if test -e "$sm_path"/.git
                        then
                        (
-                               clear_local_git_env
+                               sanitize_submodule_env
                                cd "$sm_path"
                                remote=$(get_default_remote)
                                git config remote."$remote".url "$sub_origin_url"
diff --git a/git.spec.in b/git.spec.in
deleted file mode 100644 (file)
index bfd1cfb..0000000
+++ /dev/null
@@ -1,330 +0,0 @@
-# Pass --without docs to rpmbuild if you don't want the documentation
-
-Name:          git
-Version:       @@VERSION@@
-Release:       1%{?dist}
-Summary:       Core git tools
-License:       GPL
-Group:                 Development/Tools
-URL:           http://kernel.org/pub/software/scm/git/
-Source:        http://kernel.org/pub/software/scm/git/%{name}-%{version}.tar.gz
-BuildRequires: zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel, gettext  %{!?_without_docs:, xmlto, asciidoc > 6.0.3}
-BuildRoot:     %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-
-Requires:      perl-Git = %{version}-%{release}
-Requires:      zlib >= 1.2, rsync, less, openssh-clients, expat
-Provides:      git-core = %{version}-%{release}
-Obsoletes:     git-core <= 1.5.4.2
-Obsoletes:     git-p4
-
-%description
-Git is a fast, scalable, distributed revision control system with an
-unusually rich command set that provides both high-level operations
-and full access to internals.
-
-The git rpm installs the core tools with minimal dependencies.  To
-install all git packages, including tools for integrating with other
-SCMs, install the git-all meta-package.
-
-%package all
-Summary:       Meta-package to pull in all git tools
-Group:         Development/Tools
-Requires:      git = %{version}-%{release}
-Requires:      git-svn = %{version}-%{release}
-Requires:      git-cvs = %{version}-%{release}
-Requires:      git-arch = %{version}-%{release}
-Requires:      git-email = %{version}-%{release}
-Requires:      gitk = %{version}-%{release}
-Requires:      gitweb = %{version}-%{release}
-Requires:      git-gui = %{version}-%{release}
-Obsoletes:     git <= 1.5.4.2
-
-%description all
-Git is a fast, scalable, distributed revision control system with an
-unusually rich command set that provides both high-level operations
-and full access to internals.
-
-This is a dummy package which brings in all subpackages.
-
-%package svn
-Summary:        Git tools for importing Subversion repositories
-Group:          Development/Tools
-Requires:       git = %{version}-%{release}, subversion
-%description svn
-Git tools for importing Subversion repositories.
-
-%package cvs
-Summary:        Git tools for importing CVS repositories
-Group:          Development/Tools
-Requires:       git = %{version}-%{release}, cvs, cvsps
-%description cvs
-Git tools for importing CVS repositories.
-
-%package arch
-Summary:        Git tools for importing Arch repositories
-Group:          Development/Tools
-Requires:       git = %{version}-%{release}, tla
-%description arch
-Git tools for importing Arch repositories.
-
-%package email
-Summary:        Git tools for sending email
-Group:          Development/Tools
-Requires:      git = %{version}-%{release}
-%description email
-Git tools for sending email.
-
-%package gui
-Summary:        Git GUI tool
-Group:          Development/Tools
-Requires:       git = %{version}-%{release}, tk >= 8.4
-%description gui
-Git GUI tool
-
-%package -n gitk
-Summary:        Git revision tree visualiser ('gitk')
-Group:          Development/Tools
-Requires:       git = %{version}-%{release}, tk >= 8.4
-%description -n gitk
-Git revision tree visualiser ('gitk')
-
-%package -n gitweb
-Summary:       Git web interface
-Group:          Development/Tools
-Requires:       git = %{version}-%{release}
-%description -n gitweb
-Browsing git repository on the web
-
-%package -n perl-Git
-Summary:        Perl interface to Git
-Group:          Development/Libraries
-Requires:       git = %{version}-%{release}
-Requires:       perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
-BuildRequires:  perl(Error)
-BuildRequires:  perl(ExtUtils::MakeMaker)
-
-%description -n perl-Git
-Perl interface to Git
-
-%define path_settings ETC_GITCONFIG=/etc/gitconfig prefix=%{_prefix} mandir=%{_mandir} htmldir=%{_docdir}/%{name}-%{version}
-%{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
-
-%prep
-%setup -q
-
-%build
-make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" \
-     %{path_settings} \
-     all %{!?_without_docs: doc}
-
-%install
-rm -rf $RPM_BUILD_ROOT
-make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" DESTDIR=$RPM_BUILD_ROOT \
-     %{path_settings} \
-     INSTALLDIRS=vendor install %{!?_without_docs: install-doc}
-test ! -d $RPM_BUILD_ROOT%{python_sitelib} || rm -fr $RPM_BUILD_ROOT%{python_sitelib}
-find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} ';'
-find $RPM_BUILD_ROOT -type f -name '*.bs' -empty -exec rm -f {} ';'
-find $RPM_BUILD_ROOT -type f -name perllocal.pod -exec rm -f {} ';'
-
-(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "archimport|svn|cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@)               > bin-man-doc-files
-(find $RPM_BUILD_ROOT%{_libexecdir}/git-core -type f | grep -vE "archimport|svn|cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@)               >> bin-man-doc-files
-(find $RPM_BUILD_ROOT%{perl_vendorlib} -type f | sed -e s@^$RPM_BUILD_ROOT@@) >> perl-files
-%if %{!?_without_docs:1}0
-(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "archimport|svn|git-cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
-%else
-rm -rf $RPM_BUILD_ROOT%{_mandir}
-%endif
-rm -rf $RPM_BUILD_ROOT%{_datadir}/locale
-
-mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d
-install -m 644 -T contrib/completion/git-completion.bash $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/git
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-
-%files -f bin-man-doc-files
-%defattr(-,root,root)
-%{_datadir}/git-core/
-%doc README.md COPYING Documentation/*.txt
-%{!?_without_docs: %doc Documentation/*.html Documentation/howto}
-%{!?_without_docs: %doc Documentation/technical}
-%{_sysconfdir}/bash_completion.d
-
-%files svn
-%defattr(-,root,root)
-%{_libexecdir}/git-core/*svn*
-%doc Documentation/*svn*.txt
-%{!?_without_docs: %{_mandir}/man1/*svn*.1*}
-%{!?_without_docs: %doc Documentation/*svn*.html }
-
-%files cvs
-%defattr(-,root,root)
-%doc Documentation/*git-cvs*.txt
-%{_bindir}/git-cvsserver
-%{_libexecdir}/git-core/*cvs*
-%{!?_without_docs: %{_mandir}/man1/*cvs*.1*}
-%{!?_without_docs: %doc Documentation/*git-cvs*.html }
-
-%files arch
-%defattr(-,root,root)
-%doc Documentation/git-archimport.txt
-%{_libexecdir}/git-core/git-archimport
-%{!?_without_docs: %{_mandir}/man1/git-archimport.1*}
-%{!?_without_docs: %doc Documentation/git-archimport.html }
-
-%files email
-%defattr(-,root,root)
-%doc Documentation/*email*.txt
-%{_libexecdir}/git-core/*email*
-%{!?_without_docs: %{_mandir}/man1/*email*.1*}
-%{!?_without_docs: %doc Documentation/*email*.html }
-
-%files gui
-%defattr(-,root,root)
-%{_libexecdir}/git-core/git-gui
-%{_libexecdir}/git-core/git-citool
-%{_libexecdir}/git-core/git-gui--askpass
-%{_datadir}/git-gui/
-%{!?_without_docs: %{_mandir}/man1/git-gui.1*}
-%{!?_without_docs: %doc Documentation/git-gui.html}
-%{!?_without_docs: %{_mandir}/man1/git-citool.1*}
-%{!?_without_docs: %doc Documentation/git-citool.html}
-
-%files -n gitk
-%defattr(-,root,root)
-%doc Documentation/*gitk*.txt
-%{_bindir}/*gitk*
-%{_datadir}/gitk/
-%{!?_without_docs: %{_mandir}/man1/*gitk*.1*}
-%{!?_without_docs: %doc Documentation/*gitk*.html }
-
-%files -n gitweb
-%defattr(-,root,root)
-%doc gitweb/README gitweb/INSTALL Documentation/*gitweb*.txt
-%{_datadir}/gitweb
-%{!?_without_docs: %{_mandir}/man1/*gitweb*.1*}
-%{!?_without_docs: %{_mandir}/man5/*gitweb*.5*}
-%{!?_without_docs: %doc Documentation/*gitweb*.html }
-
-%files -n perl-Git -f perl-files
-%defattr(-,root,root)
-
-%files all
-# No files for you!
-
-%changelog
-* Sun Sep 18 2011 Jakub Narebski <jnareb@gmail.com>
-- Add gitweb manpages to 'gitweb' subpackage
-
-* Wed Jun 30 2010 Junio C Hamano <gitster@pobox.com>
-- Add 'gitweb' subpackage.
-
-* Fri Mar 26 2010 Ian Ward Comfort <icomfort@stanford.edu>
-- Ship bash completion support from contrib/ in the core package.
-
-* Sun Jan 31 2010 Junio C Hamano <gitster@pobox.com>
-- Do not use %define inside %{!?...} construct.
-
-* Sat Jan 30 2010 Junio C Hamano <gitster@pobox.com>
-- We don't ship Python bits until a real foreign scm interface comes.
-
-* Mon Feb 04 2009 David J. Mellor <dmellor@whistlingcat.com>
-- fixed broken git help -w after renaming the git-core package to git.
-
-* Fri Sep 12 2008 Quy Tonthat <qtonthat@gmail.com>
-- move git-cvsserver to bindir.
-
-* Sun Jun 15 2008 Junio C Hamano <gitster@pobox.com>
-- Remove curl from Requires list.
-
-* Fri Feb 15 2008 Kristian Høgsberg <krh@redhat.com>
-- Rename git-core to just git and rename meta package from git to git-all.
-
-* Sun Feb 03 2008 James Bowes <jbowes@dangerouslyinc.com>
-- Add a BuildRequires for gettext
-
-* Fri Jan 11 2008 Junio C Hamano <gitster@pobox.com>
-- Include gitk message files
-
-* Sun Jan 06 2008 James Bowes <jbowes@dangerouslyinc.com>
-- Make the metapackage require the same version of the subpackages.
-
-* Wed Dec 12 2007 Junio C Hamano <gitster@pobox.com>
-- Adjust htmldir to point at /usr/share/doc/git-core-$version/
-
-* Sun Jul 15 2007 Sean Estabrooks <seanlkml@sympatico.ca>
-- Removed p4import.
-
-* Tue Jun 26 2007 Quy Tonthat <qtonthat@gmail.com>
-- Fixed problems looking for wrong manpages.
-
-* Thu Jun 21 2007 Shawn O. Pearce <spearce@spearce.org>
-- Added documentation files for git-gui
-
-* Tue May 13 2007 Quy Tonthat <qtonthat@gmail.com>
-- Added lib files for git-gui
-- Added Documentation/technical (As needed by Git Users Manual)
-
-* Tue May 8 2007 Quy Tonthat <qtonthat@gmail.com>
-- Added howto files
-
-* Tue Mar 27 2007 Eygene Ryabinkin <rea-git@codelabs.ru>
-- Added the git-p4 package: Perforce import stuff.
-
-* Mon Feb 13 2007 Nicolas Pitre <nico@fluxnic.net>
-- Update core package description (Git isn't as stupid as it used to be)
-
-* Mon Feb 12 2007 Junio C Hamano <junkio@cox.net>
-- Add git-gui and git-citool.
-
-* Mon Nov 14 2005 H. Peter Anvin <hpa@zytor.com> 0.99.9j-1
-- Change subpackage names to git-<name> instead of git-core-<name>
-- Create empty root package which brings in all subpackages
-- Rename git-tk -> gitk
-
-* Thu Nov 10 2005 Chris Wright <chrisw@osdl.org> 0.99.9g-1
-- zlib dependency fix
-- Minor cleanups from split
-- Move arch import to separate package as well
-
-* Tue Sep 27 2005 Jim Radford <radford@blackbean.org>
-- Move programs with non-standard dependencies (svn, cvs, email)
-  into separate packages
-
-* Tue Sep 27 2005 H. Peter Anvin <hpa@zytor.com>
-- parallelize build
-- COPTS -> CFLAGS
-
-* Fri Sep 16 2005 Chris Wright <chrisw@osdl.org> 0.99.6-1
-- update to 0.99.6
-
-* Fri Sep 16 2005 Horst H. von Brand <vonbrand@inf.utfsm.cl>
-- Linus noticed that less is required, added to the dependencies
-
-* Sun Sep 11 2005 Horst H. von Brand <vonbrand@inf.utfsm.cl>
-- Updated dependencies
-- Don't assume manpages are gzipped
-
-* Thu Aug 18 2005 Chris Wright <chrisw@osdl.org> 0.99.4-4
-- drop sh_utils, sh-utils, diffutils, mktemp, and openssl Requires
-- use RPM_OPT_FLAGS in spec file, drop patch0
-
-* Wed Aug 17 2005 Tom "spot" Callaway <tcallawa@redhat.com> 0.99.4-3
-- use dist tag to differentiate between branches
-- use rpm optflags by default (patch0)
-- own %{_datadir}/git-core/
-
-* Mon Aug 15 2005 Chris Wright <chrisw@osdl.org>
-- update spec file to fix Buildroot, Requires, and drop Vendor
-
-* Sun Aug 07 2005 Horst H. von Brand <vonbrand@inf.utfsm.cl>
-- Redid the description
-- Cut overlong make line, loosened changelog a bit
-- I think Junio (or perhaps OSDL?) should be vendor...
-
-* Thu Jul 14 2005 Eric Biederman <ebiederm@xmission.com>
-- Add the man pages, and the --without docs build option
-
-* Wed Jul 7 2005 Chris Wright <chris@osdl.org>
-- initial git spec file
index 3dc2fe397e32d79713780596f0ef4666c14b5955..c4b1e8c78d396194753d8a7287678bc476f858d6 100644 (file)
@@ -219,11 +219,9 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
        args_gpg[0] = gpg_program;
        fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
        if (fd < 0)
-               return error(_("could not create temporary file '%s': %s"),
-                            path, strerror(errno));
+               return error_errno(_("could not create temporary file '%s'"), path);
        if (write_in_full(fd, signature, signature_size) < 0)
-               return error(_("failed writing detached signature to '%s': %s"),
-                            path, strerror(errno));
+               return error_errno(_("failed writing detached signature to '%s'"), path);
        close(fd);
 
        gpg.argv = args_gpg;
@@ -237,6 +235,7 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
                return error(_("could not run gpg."));
        }
 
+       sigchain_push(SIGPIPE, SIG_IGN);
        write_in_full(gpg.in, payload, payload_size);
        close(gpg.in);
 
@@ -250,6 +249,7 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
        close(gpg.out);
 
        ret = finish_command(&gpg);
+       sigchain_pop(SIGPIPE);
 
        unlink_or_warn(path);
 
diff --git a/grep.c b/grep.c
index 528b652f713d2b6db5f48e3829448212cc3837bf..ec6f7ffa19622f1a63a4cdd51936d1061e90fc49 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -1732,7 +1732,7 @@ static int grep_source_load_file(struct grep_source *gs)
        if (lstat(filename, &st) < 0) {
        err_ret:
                if (errno != ENOENT)
-                       error(_("'%s': %s"), filename, strerror(errno));
+                       error_errno(_("failed to stat '%s'"), filename);
                return -1;
        }
        if (!S_ISREG(st.st_mode))
@@ -1743,7 +1743,7 @@ static int grep_source_load_file(struct grep_source *gs)
                goto err_ret;
        data = xmallocz(size);
        if (st.st_size != read_in_full(i, data, size)) {
-               error(_("'%s': short read %s"), filename, strerror(errno));
+               error_errno(_("'%s': short read"), filename);
                close(i);
                free(data);
                return -1;
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));
        }
index bd60668707b956160edd9fb22767758023571ebb..a092f0288bd6944be036a1a051a0954216c382e6 100644 (file)
@@ -211,7 +211,7 @@ static void curl_setup_http(CURL *curl, const char *url,
 static struct curl_slist *get_dav_token_headers(struct remote_lock *lock, enum dav_header_flag options)
 {
        struct strbuf buf = STRBUF_INIT;
-       struct curl_slist *dav_headers = NULL;
+       struct curl_slist *dav_headers = http_copy_default_headers();
 
        if (options & DAV_HEADER_IF) {
                strbuf_addf(&buf, "If: (<%s>)", lock->token);
@@ -417,7 +417,7 @@ static void start_put(struct transfer_request *request)
 static void start_move(struct transfer_request *request)
 {
        struct active_request_slot *slot;
-       struct curl_slist *dav_headers = NULL;
+       struct curl_slist *dav_headers = http_copy_default_headers();
 
        slot = get_active_slot();
        slot->callback_func = process_response;
@@ -845,7 +845,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
        char *ep;
        char timeout_header[25];
        struct remote_lock *lock = NULL;
-       struct curl_slist *dav_headers = NULL;
+       struct curl_slist *dav_headers = http_copy_default_headers();
        struct xml_ctx ctx;
        char *escaped;
 
@@ -1126,7 +1126,7 @@ static void remote_ls(const char *path, int flags,
        struct slot_results results;
        struct strbuf in_buffer = STRBUF_INIT;
        struct buffer out_buffer = { STRBUF_INIT, 0 };
-       struct curl_slist *dav_headers = NULL;
+       struct curl_slist *dav_headers = http_copy_default_headers();
        struct xml_ctx ctx;
        struct remote_ls_ctx ls;
 
@@ -1204,7 +1204,7 @@ static int locking_available(void)
        struct slot_results results;
        struct strbuf in_buffer = STRBUF_INIT;
        struct buffer out_buffer = { STRBUF_INIT, 0 };
-       struct curl_slist *dav_headers = NULL;
+       struct curl_slist *dav_headers = http_copy_default_headers();
        struct xml_ctx ctx;
        int lock_flags = 0;
        char *escaped;
@@ -1312,10 +1312,10 @@ static struct object_list **process_tree(struct tree *tree,
        while (tree_entry(&desc, &entry))
                switch (object_type(entry.mode)) {
                case OBJ_TREE:
-                       p = process_tree(lookup_tree(entry.sha1), p);
+                       p = process_tree(lookup_tree(entry.oid->hash), p);
                        break;
                case OBJ_BLOB:
-                       p = process_blob(lookup_blob(entry.sha1), p);
+                       p = process_blob(lookup_blob(entry.oid->hash), p);
                        break;
                default:
                        /* Subproject commit - not in this repository */
diff --git a/http.c b/http.c
index 1044f9ba0e28ff825fa58c56d535861cd9f56a59..df6dd01594a0c7bacbb9beacec683072de38a5b1 100644 (file)
--- a/http.c
+++ b/http.c
@@ -114,6 +114,7 @@ static unsigned long http_auth_methods = CURLAUTH_ANY;
 
 static struct curl_slist *pragma_header;
 static struct curl_slist *no_pragma_header;
+static struct curl_slist *extra_http_headers;
 
 static struct active_request_slot *active_queue_head;
 
@@ -323,6 +324,19 @@ static int http_options(const char *var, const char *value, void *cb)
 #endif
        }
 
+       if (!strcmp("http.extraheader", var)) {
+               if (!value) {
+                       return config_error_nonbool(var);
+               } else if (!*value) {
+                       curl_slist_free_all(extra_http_headers);
+                       extra_http_headers = NULL;
+               } else {
+                       extra_http_headers =
+                               curl_slist_append(extra_http_headers, value);
+               }
+               return 0;
+       }
+
        /* Fall back on the default ones */
        return git_default_config(var, value, cb);
 }
@@ -446,8 +460,7 @@ static int sockopt_callback(void *client, curl_socket_t fd, curlsocktype type)
 
        rc = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&ka, len);
        if (rc < 0)
-               warning("unable to set SO_KEEPALIVE on socket %s",
-                       strerror(errno));
+               warning_errno("unable to set SO_KEEPALIVE on socket");
 
        return 0; /* CURL_SOCKOPT_OK only exists since curl 7.21.5 */
 }
@@ -678,8 +691,10 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
        if (remote)
                var_override(&http_proxy_authmethod, remote->http_proxy_authmethod);
 
-       pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
-       no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
+       pragma_header = curl_slist_append(http_copy_default_headers(),
+               "Pragma: no-cache");
+       no_pragma_header = curl_slist_append(http_copy_default_headers(),
+               "Pragma:");
 
 #ifdef USE_CURL_MULTI
        {
@@ -765,6 +780,9 @@ void http_cleanup(void)
 #endif
        curl_global_cleanup();
 
+       curl_slist_free_all(extra_http_headers);
+       extra_http_headers = NULL;
+
        curl_slist_free_all(pragma_header);
        pragma_header = NULL;
 
@@ -1163,6 +1181,16 @@ int run_one_slot(struct active_request_slot *slot,
        return handle_curl_result(results);
 }
 
+struct curl_slist *http_copy_default_headers(void)
+{
+       struct curl_slist *headers = NULL, *h;
+
+       for (h = extra_http_headers; h; h = h->next)
+               headers = curl_slist_append(headers, h->data);
+
+       return headers;
+}
+
 static CURLcode curlinfo_strbuf(CURL *curl, CURLINFO info, struct strbuf *buf)
 {
        char *ptr;
@@ -1380,7 +1408,7 @@ static int http_request(const char *url,
 {
        struct active_request_slot *slot;
        struct slot_results results;
-       struct curl_slist *headers = NULL;
+       struct curl_slist *headers = http_copy_default_headers();
        struct strbuf buf = STRBUF_INIT;
        const char *accept_language;
        int ret;
@@ -1894,8 +1922,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
        }
 
        if (freq->localfile < 0) {
-               error("Couldn't create temporary file %s: %s",
-                     freq->tmpfile, strerror(errno));
+               error_errno("Couldn't create temporary file %s", freq->tmpfile);
                goto abort;
        }
 
@@ -1940,8 +1967,8 @@ struct http_object_request *new_http_object_request(const char *base_url,
                        prev_posn = 0;
                        lseek(freq->localfile, 0, SEEK_SET);
                        if (ftruncate(freq->localfile, 0) < 0) {
-                               error("Couldn't truncate temporary file %s: %s",
-                                         freq->tmpfile, strerror(errno));
+                               error_errno("Couldn't truncate temporary file %s",
+                                           freq->tmpfile);
                                goto abort;
                        }
                }
diff --git a/http.h b/http.h
index 4ef4bbda7d86d8ca5238d45897b277709b30414a..36f558bfb379a990ec4d4aa0d871edd2a6d250a1 100644 (file)
--- a/http.h
+++ b/http.h
@@ -106,6 +106,7 @@ extern void step_active_slots(void);
 extern void http_init(struct remote *remote, const char *url,
                      int proactive_auth);
 extern void http_cleanup(void);
+extern struct curl_slist *http_copy_default_headers(void);
 
 extern long int git_curl_ipresolve;
 extern int active_requests;
diff --git a/ident.c b/ident.c
index 4fd82d104365c2e2c1ab7e1597e7e246c8eded24..139c5289d03b7594af2a07e6e0364bb285ae0348 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -75,14 +75,12 @@ static int add_mailname_host(struct strbuf *buf)
        mailname = fopen("/etc/mailname", "r");
        if (!mailname) {
                if (errno != ENOENT)
-                       warning("cannot open /etc/mailname: %s",
-                               strerror(errno));
+                       warning_errno("cannot open /etc/mailname");
                return -1;
        }
        if (strbuf_getline(&mailnamebuf, mailname) == EOF) {
                if (ferror(mailname))
-                       warning("cannot read /etc/mailname: %s",
-                               strerror(errno));
+                       warning_errno("cannot read /etc/mailname");
                strbuf_release(&mailnamebuf);
                fclose(mailname);
                return -1;
@@ -125,7 +123,7 @@ static void add_domainname(struct strbuf *out, int *is_bogus)
        char buf[1024];
 
        if (gethostname(buf, sizeof(buf))) {
-               warning("cannot get host name: %s", strerror(errno));
+               warning_errno("cannot get host name");
                strbuf_addstr(out, "(none)");
                *is_bogus = 1;
                return;
index 917cc5d7c9ee04d07199f18b78342b84c0375fcd..f3ca6aafb799eadc6255d61711d0aba8cb516463 100644 (file)
@@ -110,16 +110,16 @@ static void process_tree(struct rev_info *revs,
 
                if (S_ISDIR(entry.mode))
                        process_tree(revs,
-                                    lookup_tree(entry.sha1),
+                                    lookup_tree(entry.oid->hash),
                                     show, base, entry.path,
                                     cb_data);
                else if (S_ISGITLINK(entry.mode))
-                       process_gitlink(revs, entry.sha1,
+                       process_gitlink(revs, entry.oid->hash,
                                        show, base, entry.path,
                                        cb_data);
                else
                        process_blob(revs,
-                                    lookup_blob(entry.sha1),
+                                    lookup_blob(entry.oid->hash),
                                     show, base, entry.path,
                                     cb_data);
        }
index ff4a43a982a6a67ec826fdb01153a9e35e720751..ad8be42f912b456492ca1704ce067fea4713401a 100644 (file)
@@ -47,7 +47,9 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
        assert(opts);
 
        /*
-        * The tentative merge result is the or common ancestor for an internal merge.
+        * The tentative merge result is the common ancestor for an
+        * internal merge.  For the final merge, it is "ours" by
+        * default but -Xours/-Xtheirs can tweak the choice.
         */
        if (opts->virtual_ancestor) {
                stolen = orig;
@@ -383,8 +385,12 @@ int ll_merge(mmbuffer_t *result_buf,
                }
        }
        driver = find_ll_merge_driver(ll_driver_name);
-       if (opts->virtual_ancestor && driver->recursive)
-               driver = find_ll_merge_driver(driver->recursive);
+
+       if (opts->virtual_ancestor) {
+               if (driver->recursive)
+                       driver = find_ll_merge_driver(driver->recursive);
+               marker_size += 2;
+       }
        return driver->fn(driver, result_buf, path, ancestor, ancestor_label,
                          ours, our_label, theirs, their_label,
                          opts, marker_size);
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 972623709fdac5503328f7e252d5334dfcb461c6..b5c521fdea8b0bfc577795b172010dfc2b8bf8ff 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -189,8 +189,7 @@ static int read_mailmap_file(struct string_list *map, const char *filename,
        if (!f) {
                if (errno == ENOENT)
                        return 0;
-               return error("unable to open mailmap at %s: %s",
-                            filename, strerror(errno));
+               return error_errno("unable to open mailmap at %s", filename);
        }
 
        while (fgets(buffer, sizeof(buffer), f) != NULL)
index 1ce0954a3e5915a6bcc30448c9d99fdaba32bcab..396b7338df2c53009900fbd3df36dd06a7219493 100644 (file)
@@ -48,17 +48,17 @@ static int score_matches(unsigned mode1, unsigned mode2, const char *path)
 }
 
 static void *fill_tree_desc_strict(struct tree_desc *desc,
-                                  const unsigned char *hash)
+                                  const struct object_id *hash)
 {
        void *buffer;
        enum object_type type;
        unsigned long size;
 
-       buffer = read_sha1_file(hash, &type, &size);
+       buffer = read_sha1_file(hash->hash, &type, &size);
        if (!buffer)
-               die("unable to read tree (%s)", sha1_to_hex(hash));
+               die("unable to read tree (%s)", oid_to_hex(hash));
        if (type != OBJ_TREE)
-               die("%s is not a tree", sha1_to_hex(hash));
+               die("%s is not a tree", oid_to_hex(hash));
        init_tree_desc(desc, buffer, size);
        return buffer;
 }
@@ -73,7 +73,7 @@ static int base_name_entries_compare(const struct name_entry *a,
 /*
  * Inspect two trees, and give a score that tells how similar they are.
  */
-static int score_trees(const unsigned char *hash1, const unsigned char *hash2)
+static int score_trees(const struct object_id *hash1, const struct object_id *hash2)
 {
        struct tree_desc one;
        struct tree_desc two;
@@ -104,7 +104,7 @@ static int score_trees(const unsigned char *hash1, const unsigned char *hash2)
                else if (cmp > 0)
                        /* path2 does not appear in one */
                        score += score_missing(e2.mode, e2.path);
-               else if (hashcmp(e1.sha1, e2.sha1))
+               else if (oidcmp(e1.oid, e2.oid))
                        /* they are different */
                        score += score_differs(e1.mode, e2.mode, e1.path);
                else
@@ -119,8 +119,8 @@ static int score_trees(const unsigned char *hash1, const unsigned char *hash2)
 /*
  * Match one itself and its subtrees with two and pick the best match.
  */
-static void match_trees(const unsigned char *hash1,
-                       const unsigned char *hash2,
+static void match_trees(const struct object_id *hash1,
+                       const struct object_id *hash2,
                        int *best_score,
                        char **best_match,
                        const char *base,
@@ -131,7 +131,7 @@ static void match_trees(const unsigned char *hash1,
 
        while (one.size) {
                const char *path;
-               const unsigned char *elem;
+               const struct object_id *elem;
                unsigned mode;
                int score;
 
@@ -191,15 +191,15 @@ static int splice_tree(const unsigned char *hash1,
        while (desc.size) {
                const char *name;
                unsigned mode;
-               const unsigned char *sha1;
+               const struct object_id *oid;
 
-               sha1 = tree_entry_extract(&desc, &name, &mode);
+               oid = tree_entry_extract(&desc, &name, &mode);
                if (strlen(name) == toplen &&
                    !memcmp(name, prefix, toplen)) {
                        if (!S_ISDIR(mode))
                                die("entry %s in tree %s is not a tree",
                                    name, sha1_to_hex(hash1));
-                       rewrite_here = (unsigned char *) sha1;
+                       rewrite_here = (unsigned char *) oid->hash;
                        break;
                }
                update_tree_entry(&desc);
@@ -229,9 +229,9 @@ static int splice_tree(const unsigned char *hash1,
  * other hand, it could cover tree one and we might need to pick a
  * subtree of it.
  */
-void shift_tree(const unsigned char *hash1,
-               const unsigned char *hash2,
-               unsigned char *shifted,
+void shift_tree(const struct object_id *hash1,
+               const struct object_id *hash2,
+               struct object_id *shifted,
                int depth_limit)
 {
        char *add_prefix;
@@ -262,7 +262,7 @@ void shift_tree(const unsigned char *hash1,
        match_trees(hash2, hash1, &del_score, &del_prefix, "", depth_limit);
 
        /* Assume we do not have to do any shifting */
-       hashcpy(shifted, hash2);
+       oidcpy(shifted, hash2);
 
        if (add_score < del_score) {
                /* We need to pick a subtree of two */
@@ -271,16 +271,16 @@ void shift_tree(const unsigned char *hash1,
                if (!*del_prefix)
                        return;
 
-               if (get_tree_entry(hash2, del_prefix, shifted, &mode))
+               if (get_tree_entry(hash2->hash, del_prefix, shifted->hash, &mode))
                        die("cannot find path %s in tree %s",
-                           del_prefix, sha1_to_hex(hash2));
+                           del_prefix, oid_to_hex(hash2));
                return;
        }
 
        if (!*add_prefix)
                return;
 
-       splice_tree(hash1, add_prefix, hash2, shifted);
+       splice_tree(hash1->hash, add_prefix, hash2->hash, shifted->hash);
 }
 
 /*
@@ -288,22 +288,22 @@ void shift_tree(const unsigned char *hash1,
  * Unfortunately we cannot fundamentally tell which one to
  * be prefixed, as recursive merge can work in either direction.
  */
-void shift_tree_by(const unsigned char *hash1,
-                  const unsigned char *hash2,
-                  unsigned char *shifted,
+void shift_tree_by(const struct object_id *hash1,
+                  const struct object_id *hash2,
+                  struct object_id *shifted,
                   const char *shift_prefix)
 {
-       unsigned char sub1[20], sub2[20];
+       struct object_id sub1, sub2;
        unsigned mode1, mode2;
        unsigned candidate = 0;
 
        /* Can hash2 be a tree at shift_prefix in tree hash1? */
-       if (!get_tree_entry(hash1, shift_prefix, sub1, &mode1) &&
+       if (!get_tree_entry(hash1->hash, shift_prefix, sub1.hash, &mode1) &&
            S_ISDIR(mode1))
                candidate |= 1;
 
        /* Can hash1 be a tree at shift_prefix in tree hash2? */
-       if (!get_tree_entry(hash2, shift_prefix, sub2, &mode2) &&
+       if (!get_tree_entry(hash2->hash, shift_prefix, sub2.hash, &mode2) &&
            S_ISDIR(mode2))
                candidate |= 2;
 
@@ -313,19 +313,19 @@ void shift_tree_by(const unsigned char *hash1,
                int score;
 
                candidate = 0;
-               score = score_trees(sub1, hash2);
+               score = score_trees(&sub1, hash2);
                if (score > best_score) {
                        candidate = 1;
                        best_score = score;
                }
-               score = score_trees(sub2, hash1);
+               score = score_trees(&sub2, hash1);
                if (score > best_score)
                        candidate = 2;
        }
 
        if (!candidate) {
                /* Neither is plausible -- do not shift */
-               hashcpy(shifted, hash2);
+               oidcpy(shifted, hash2);
                return;
        }
 
@@ -334,11 +334,11 @@ void shift_tree_by(const unsigned char *hash1,
                 * shift tree2 down by adding shift_prefix above it
                 * to match tree1.
                 */
-               splice_tree(hash1, shift_prefix, hash2, shifted);
+               splice_tree(hash1->hash, shift_prefix, hash2->hash, shifted->hash);
        else
                /*
                 * shift tree2 up by removing shift_prefix from it
                 * to match tree1.
                 */
-               hashcpy(shifted, sub2);
+               oidcpy(shifted, &sub2);
 }
index b880ae50e7ee4f8e88bda83618529ea30462e4ef..65cb5d6c1f59d635e44a0fea648e39a976c0eaf0 100644 (file)
@@ -29,9 +29,9 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two,
        struct object_id shifted;
 
        if (!*subtree_shift) {
-               shift_tree(one->object.oid.hash, two->object.oid.hash, shifted.hash, 0);
+               shift_tree(&one->object.oid, &two->object.oid, &shifted, 0);
        } else {
-               shift_tree_by(one->object.oid.hash, two->object.oid.hash, shifted.hash,
+               shift_tree_by(&one->object.oid, &two->object.oid, &shifted,
                              subtree_shift);
        }
        if (!oidcmp(&two->object.oid, &shifted))
@@ -622,7 +622,7 @@ static char *unique_path(struct merge_options *o, const char *path, const char *
        base_len = newpath.len;
        while (string_list_has_string(&o->current_file_set, newpath.buf) ||
               string_list_has_string(&o->current_directory_set, newpath.buf) ||
-              file_exists(newpath.buf)) {
+              (!o->call_depth && file_exists(newpath.buf))) {
                strbuf_setlen(&newpath, base_len);
                strbuf_addf(&newpath, "_%d", suffix++);
        }
@@ -1234,8 +1234,8 @@ static void conflict_rename_rename_2to1(struct merge_options *o,
               a->path, c1->path, ci->branch1,
               b->path, c2->path, ci->branch2);
 
-       remove_file(o, 1, a->path, would_lose_untracked(a->path));
-       remove_file(o, 1, b->path, would_lose_untracked(b->path));
+       remove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));
+       remove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));
 
        mfi_c1 = merge_file_special_markers(o, a, c1, &ci->ren1_other,
                                            o->branch1, c1->path,
@@ -1773,8 +1773,6 @@ static int process_entry(struct merge_options *o,
                        output(o, 1, _("CONFLICT (%s): There is a directory with name %s in %s. "
                               "Adding %s as %s"),
                               conf, path, other_branch, path, new_path);
-                       if (o->call_depth)
-                               remove_file_from_cache(path);
                        update_file(o, 0, sha, mode, new_path);
                        if (o->call_depth)
                                remove_file_from_cache(path);
diff --git a/mergetools/examdiff b/mergetools/examdiff
new file mode 100644 (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/notes.c b/notes.c
index 88cf4747c16724931682e606a2c1fba4c07b2f49..e4e4854d69fb965e68a712dd342121d3276be945 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -446,7 +446,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
                        l = (struct leaf_node *)
                                xcalloc(1, sizeof(struct leaf_node));
                        hashcpy(l->key_sha1, object_sha1);
-                       hashcpy(l->val_sha1, entry.sha1);
+                       hashcpy(l->val_sha1, entry.oid->hash);
                        if (len < 20) {
                                if (!S_ISDIR(entry.mode) || path_len != 2)
                                        goto handle_non_note; /* not subtree */
@@ -493,7 +493,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
                        }
                        strbuf_addstr(&non_note_path, entry.path);
                        add_non_note(t, strbuf_detach(&non_note_path, NULL),
-                                    entry.mode, entry.sha1);
+                                    entry.mode, entry.oid->hash);
                }
        }
        free(buf);
index 47a91920601d74842a0947ce1431c00b3d723972..312a85dbdef5723ef805917f5a556b75a55a7041 100644 (file)
@@ -110,6 +110,8 @@ static int get_value(struct parse_opt_ctx_t *p,
                return 0;
 
        case OPTION_COUNTUP:
+               if (*(int *)opt->value < 0)
+                       *(int *)opt->value = 0;
                *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
                return 0;
 
index b7b3e5a1a700d4d2e1656c605697c2cf6345b8a6..a4d00166646d3dc354f3c06e9e75ce7dc3724f6a 100644 (file)
@@ -4,7 +4,7 @@
 #include "sha1-lookup.h"
 #include "patch-ids.h"
 
-static int commit_patch_id(struct commit *commit, struct diff_options *options,
+int commit_patch_id(struct commit *commit, struct diff_options *options,
                    unsigned char *sha1)
 {
        if (commit->parents)
index c8c7ca110ad34def12a3594a1560b3c3052eb701..eeb56b307f602773a41ea3b8cb22d89959c4f9d9 100644 (file)
@@ -13,6 +13,8 @@ struct patch_ids {
        struct patch_id_bucket *patches;
 };
 
+int commit_patch_id(struct commit *commit, struct diff_options *options,
+                   unsigned char *sha1);
 int init_patch_ids(struct patch_ids *);
 int free_patch_ids(struct patch_ids *);
 struct patch_id *add_commit_patch_id(struct commit *, struct patch_ids *);
diff --git a/path.c b/path.c
index 503766784c4c0c1be71edacbaae6ec4bd698ee8d..259aeed846bd3dac8e10cde30a22ec1ad8697a63 100644 (file)
--- a/path.c
+++ b/path.c
@@ -5,6 +5,7 @@
 #include "strbuf.h"
 #include "string-list.h"
 #include "dir.h"
+#include "worktree.h"
 
 static int get_st_mode_bits(const char *path, int *mode)
 {
@@ -383,10 +384,11 @@ static void adjust_git_path(struct strbuf *buf, int git_dir_len)
                update_common_dir(buf, git_dir_len, NULL);
 }
 
-static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
+static void do_git_path(const struct worktree *wt, struct strbuf *buf,
+                       const char *fmt, va_list args)
 {
        int gitdir_len;
-       strbuf_addstr(buf, get_git_dir());
+       strbuf_addstr(buf, get_worktree_git_dir(wt));
        if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
                strbuf_addch(buf, '/');
        gitdir_len = buf->len;
@@ -400,7 +402,7 @@ char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
        va_list args;
        strbuf_reset(buf);
        va_start(args, fmt);
-       do_git_path(buf, fmt, args);
+       do_git_path(NULL, buf, fmt, args);
        va_end(args);
        return buf->buf;
 }
@@ -409,7 +411,7 @@ void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
 {
        va_list args;
        va_start(args, fmt);
-       do_git_path(sb, fmt, args);
+       do_git_path(NULL, sb, fmt, args);
        va_end(args);
 }
 
@@ -418,7 +420,7 @@ const char *git_path(const char *fmt, ...)
        struct strbuf *pathname = get_pathname();
        va_list args;
        va_start(args, fmt);
-       do_git_path(pathname, fmt, args);
+       do_git_path(NULL, pathname, fmt, args);
        va_end(args);
        return pathname->buf;
 }
@@ -428,7 +430,7 @@ char *git_pathdup(const char *fmt, ...)
        struct strbuf path = STRBUF_INIT;
        va_list args;
        va_start(args, fmt);
-       do_git_path(&path, fmt, args);
+       do_git_path(NULL, &path, fmt, args);
        va_end(args);
        return strbuf_detach(&path, NULL);
 }
@@ -454,6 +456,16 @@ const char *mkpath(const char *fmt, ...)
        return cleanup_path(pathname->buf);
 }
 
+const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)
+{
+       struct strbuf *pathname = get_pathname();
+       va_list args;
+       va_start(args, fmt);
+       do_git_path(wt, pathname, fmt, args);
+       va_end(args);
+       return pathname->buf;
+}
+
 static void do_submodule_path(struct strbuf *buf, const char *path,
                              const char *fmt, va_list args)
 {
@@ -503,6 +515,35 @@ void strbuf_git_path_submodule(struct strbuf *buf, const char *path,
        va_end(args);
 }
 
+static void do_git_common_path(struct strbuf *buf,
+                              const char *fmt,
+                              va_list args)
+{
+       strbuf_addstr(buf, get_git_common_dir());
+       if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
+               strbuf_addch(buf, '/');
+       strbuf_vaddf(buf, fmt, args);
+       strbuf_cleanup_path(buf);
+}
+
+const char *git_common_path(const char *fmt, ...)
+{
+       struct strbuf *pathname = get_pathname();
+       va_list args;
+       va_start(args, fmt);
+       do_git_common_path(pathname, fmt, args);
+       va_end(args);
+       return pathname->buf;
+}
+
+void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+{
+       va_list args;
+       va_start(args, fmt);
+       do_git_common_path(sb, fmt, args);
+       va_end(args);
+}
+
 int validate_headref(const char *path)
 {
        struct stat st;
index b2c14e2ff5485f49af83530ce1b1921c99641f5c..d94d01cfdc8e4bcae2673b2421ef6e48fd69fb24 100644 (file)
@@ -97,7 +97,8 @@ sub resolve_local_globs {
                                    "existing: $existing\n",
                                    " globbed: $refname\n";
                        }
-                       my $u = (::cmt_metadata("$refname"))[0];
+                       my $u = (::cmt_metadata("$refname"))[0] or die
+                           "$refname: no associated commit metadata\n";
                        $u =~ s!^\Q$url\E(/|$)!! or die
                          "$refname: '$url' not found in '$u'\n";
                        if ($pathname ne $u) {
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
index ed352018964ef2260f5da51239800bbcc816ad1b..d0199cace4aa0ab22a46fdb5ac2777c4e7e6153b 100644 (file)
@@ -119,8 +119,7 @@ static int add_recent_loose(const unsigned char *sha1,
                 */
                if (errno == ENOENT)
                        return 0;
-               return error("unable to stat %s: %s",
-                            sha1_to_hex(sha1), strerror(errno));
+               return error_errno("unable to stat %s", sha1_to_hex(sha1));
        }
 
        add_recent_object(sha1, st.st_mtime, data);
diff --git a/refs.c b/refs.c
index 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;
+}
index ea78ce9d90f0bb02fcd471c1c113e22053a3ba94..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 */
@@ -3481,7 +3350,8 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
         * reference itself, plus we might need to update the
         * reference if --updateref was specified:
         */
-       lock = lock_ref_sha1_basic(refname, sha1, NULL, NULL, 0, &type, &err);
+       lock = lock_ref_sha1_basic(refname, sha1, NULL, NULL, REF_NODEREF,
+                                  &type, &err);
        if (!lock) {
                error("cannot lock ref '%s': %s", refname, err.buf);
                strbuf_release(&err);
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 15e48e25fb9fb8cd2e9e3e7a63cf08d2f9483ea2..672b382e5aaf25654f9095e68f45062baed3f397 100644 (file)
@@ -474,7 +474,7 @@ static int run_slot(struct active_request_slot *slot,
 static int probe_rpc(struct rpc_state *rpc, struct slot_results *results)
 {
        struct active_request_slot *slot;
-       struct curl_slist *headers = NULL;
+       struct curl_slist *headers = http_copy_default_headers();
        struct strbuf buf = STRBUF_INIT;
        int err;
 
@@ -503,7 +503,7 @@ static int probe_rpc(struct rpc_state *rpc, struct slot_results *results)
 static int post_rpc(struct rpc_state *rpc)
 {
        struct active_request_slot *slot;
-       struct curl_slist *headers = NULL;
+       struct curl_slist *headers = http_copy_default_headers();
        int use_gzip = rpc->gzip_request;
        char *gzip_body = NULL;
        size_t gzip_size = 0;
index 587b7e2717b14748b92a5960f79947b5fba088f0..aaadec17d867d521fc0eaab7fcdd2e871bdf1267 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");
 
@@ -351,8 +501,7 @@ static int handle_file(const char *path, unsigned char *sha1, const char *output
                error("There were errors while writing %s (%s)",
                      path, strerror(io.io.wrerror));
        if (io.io.output && fclose(io.io.output))
-               io.io.wrerror = error("Failed to flush %s: %s",
-                                     path, strerror(errno));
+               io.io.wrerror = error_errno("Failed to flush %s", path);
 
        if (hunk_no < 0) {
                if (output)
@@ -364,103 +513,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 +620,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 +661,20 @@ static int merge(const struct rerere_id *id, const char *path)
 {
        FILE *f;
        int ret;
-       mmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0};
+       mmfile_t cur = {NULL, 0};
        mmbuffer_t result = {NULL, 0};
 
        /*
         * Normalize the conflicts in path and write it out to
         * "thisimage" temporary file.
         */
-       if (handle_file(path, NULL, rerere_path(id, "thisimage")) < 0) {
-               ret = 1;
-               goto out;
-       }
-
-       if (read_mmfile(&cur, rerere_path(id, "thisimage")) ||
-           read_mmfile(&base, rerere_path(id, "preimage")) ||
-           read_mmfile(&other, rerere_path(id, "postimage"))) {
+       if ((handle_file(path, NULL, rerere_path(id, "thisimage")) < 0) ||
+           read_mmfile(&cur, rerere_path(id, "thisimage"))) {
                ret = 1;
                goto out;
        }
 
-       /*
-        * A three-way merge. Note that this honors user-customizable
-        * low-level merge driver settings.
-        */
-       ret = ll_merge(&result, path, &base, NULL, &cur, "", &other, "", NULL);
+       ret = try_merge(id, path, &cur, &result);
        if (ret)
                goto out;
 
@@ -614,25 +683,20 @@ static int merge(const struct rerere_id *id, const char *path)
         * Mark that "postimage" was used to help gc.
         */
        if (utime(rerere_path(id, "postimage"), NULL) < 0)
-               warning("failed utime() on %s: %s",
-                       rerere_path(id, "postimage"),
-                       strerror(errno));
+               warning_errno("failed utime() on %s",
+                             rerere_path(id, "postimage"));
 
        /* Update "path" with the resolution */
        f = fopen(path, "w");
        if (!f)
-               return error("Could not open %s: %s", path,
-                            strerror(errno));
+               return error_errno("Could not open %s", path);
        if (fwrite(result.ptr, result.size, 1, f) != 1)
-               error("Could not write %s: %s", path, strerror(errno));
+               error_errno("Could not write %s", path);
        if (fclose(f))
-               return error("Writing %s failed: %s", path,
-                            strerror(errno));
+               return error_errno("Writing %s failed", path);
 
 out:
        free(cur.ptr);
-       free(base.ptr);
-       free(other.ptr);
        free(result.ptr);
 
        return ret;
@@ -661,6 +725,13 @@ static void update_paths(struct string_list *update)
                rollback_lock_file(&index_lock);
 }
 
+static void remove_variant(struct rerere_id *id)
+{
+       unlink_or_warn(rerere_path(id, "postimage"));
+       unlink_or_warn(rerere_path(id, "preimage"));
+       id->collection->status[id->variant] = 0;
+}
+
 /*
  * The path indicated by rr_item may still have conflict for which we
  * have a recorded resolution, in which case replay it and optionally
@@ -672,12 +743,47 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
                               struct string_list *update)
 {
        const char *path = rr_item->string;
-       const struct rerere_id *id = rr_item->util;
+       struct rerere_id *id = rr_item->util;
+       struct rerere_dir *rr_dir = id->collection;
+       int variant;
+
+       variant = id->variant;
+
+       /* Has the user resolved it already? */
+       if (variant >= 0) {
+               if (!handle_file(path, NULL, NULL)) {
+                       copy_file(rerere_path(id, "postimage"), path, 0666);
+                       id->collection->status[variant] |= RR_HAS_POSTIMAGE;
+                       fprintf(stderr, "Recorded resolution for '%s'.\n", path);
+                       free_rerere_id(rr_item);
+                       rr_item->util = NULL;
+                       return;
+               }
+               /*
+                * There may be other variants that can cleanly
+                * replay.  Try them and update the variant number for
+                * this one.
+                */
+       }
+
+       /* Does any existing resolution apply cleanly? */
+       for (variant = 0; variant < rr_dir->status_nr; variant++) {
+               const int both = RR_HAS_PREIMAGE | RR_HAS_POSTIMAGE;
+               struct rerere_id vid = *id;
+
+               if ((rr_dir->status[variant] & both) != both)
+                       continue;
+
+               vid.variant = variant;
+               if (merge(&vid, path))
+                       continue; /* failed to replay */
 
-       /* Is there a recorded resolution we could attempt to apply? */
-       if (has_rerere_resolution(id)) {
-               if (merge(id, path))
-                       return; /* failed to replay */
+               /*
+                * If there already is a different variant that applies
+                * cleanly, there is no point maintaining our own variant.
+                */
+               if (0 <= id->variant && id->variant != variant)
+                       remove_variant(id);
 
                if (rerere_autoupdate)
                        string_list_insert(update, path);
@@ -685,15 +791,24 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
                        fprintf(stderr,
                                "Resolved '%s' using previous resolution.\n",
                                path);
-       } else if (!handle_file(path, NULL, NULL)) {
-               /* The user has resolved it. */
-               copy_file(rerere_path(id, "postimage"), path, 0666);
-               fprintf(stderr, "Recorded resolution for '%s'.\n", path);
-       } else {
+               free_rerere_id(rr_item);
+               rr_item->util = NULL;
                return;
        }
-       free_rerere_id(rr_item);
-       rr_item->util = NULL;
+
+       /* None of the existing one applies; we need a new variant */
+       assign_variant(id);
+
+       variant = id->variant;
+       handle_file(path, NULL, rerere_path(id, "preimage"));
+       if (id->collection->status[variant] & RR_HAS_POSTIMAGE) {
+               const char *path = rerere_path(id, "postimage");
+               if (unlink(path))
+                       die_errno("cannot unlink stray '%s'", path);
+               id->collection->status[variant] &= ~RR_HAS_POSTIMAGE;
+       }
+       id->collection->status[variant] |= RR_HAS_PREIMAGE;
+       fprintf(stderr, "Recorded preimage for '%s'\n", path);
 }
 
 static int do_plain_rerere(struct string_list *rr, int fd)
@@ -731,24 +846,8 @@ static int do_plain_rerere(struct string_list *rr, int fd)
                id = new_rerere_id(sha1);
                string_list_insert(rr, path)->util = id;
 
-               /*
-                * If the directory does not exist, create
-                * it.  mkdir_in_gitdir() will fail with
-                * EEXIST if there already is one.
-                *
-                * NEEDSWORK: make sure "gc" does not remove
-                * preimage without removing the directory.
-                */
-               if (mkdir_in_gitdir(rerere_path(id, NULL)))
-                       continue;
-
-               /*
-                * We are the first to encounter this
-                * conflict.  Ask handle_file() to write the
-                * normalized contents to the "preimage" file.
-                */
-               handle_file(path, NULL, rerere_path(id, "preimage"));
-               fprintf(stderr, "Recorded preimage for '%s'\n", path);
+               /* Ensure that the directory exists. */
+               mkdir_in_gitdir(rerere_path(id, NULL));
        }
 
        for (i = 0; i < rr->nr; i++)
@@ -812,12 +911,111 @@ int setup_rerere(struct string_list *merge_rr, int flags)
 int rerere(int flags)
 {
        struct string_list merge_rr = STRING_LIST_INIT_DUP;
-       int fd;
+       int fd, status;
 
        fd = setup_rerere(&merge_rr, flags);
        if (fd < 0)
                return 0;
-       return do_plain_rerere(&merge_rr, fd);
+       status = do_plain_rerere(&merge_rr, fd);
+       free_rerere_dirs();
+       return status;
+}
+
+/*
+ * Subclass of rerere_io that reads from an in-core buffer that is a
+ * strbuf
+ */
+struct rerere_io_mem {
+       struct rerere_io io;
+       struct strbuf input;
+};
+
+/*
+ * ... and its getline() method implementation
+ */
+static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)
+{
+       struct rerere_io_mem *io = (struct rerere_io_mem *)io_;
+       char *ep;
+       size_t len;
+
+       strbuf_release(sb);
+       if (!io->input.len)
+               return -1;
+       ep = memchr(io->input.buf, '\n', io->input.len);
+       if (!ep)
+               ep = io->input.buf + io->input.len;
+       else if (*ep == '\n')
+               ep++;
+       len = ep - io->input.buf;
+       strbuf_add(sb, io->input.buf, len);
+       strbuf_remove(&io->input, 0, len);
+       return 0;
+}
+
+static int handle_cache(const char *path, unsigned char *sha1, const char *output)
+{
+       mmfile_t mmfile[3] = {{NULL}};
+       mmbuffer_t result = {NULL, 0};
+       const struct cache_entry *ce;
+       int pos, len, i, hunk_no;
+       struct rerere_io_mem io;
+       int marker_size = ll_merge_marker_size(path);
+
+       /*
+        * Reproduce the conflicted merge in-core
+        */
+       len = strlen(path);
+       pos = cache_name_pos(path, len);
+       if (0 <= pos)
+               return -1;
+       pos = -pos - 1;
+
+       while (pos < active_nr) {
+               enum object_type type;
+               unsigned long size;
+
+               ce = active_cache[pos++];
+               if (ce_namelen(ce) != len || memcmp(ce->name, path, len))
+                       break;
+               i = ce_stage(ce) - 1;
+               if (!mmfile[i].ptr) {
+                       mmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size);
+                       mmfile[i].size = size;
+               }
+       }
+       for (i = 0; i < 3; i++)
+               if (!mmfile[i].ptr && !mmfile[i].size)
+                       mmfile[i].ptr = xstrdup("");
+
+       /*
+        * NEEDSWORK: handle conflicts from merges with
+        * merge.renormalize set, too?
+        */
+       ll_merge(&result, path, &mmfile[0], NULL,
+                &mmfile[1], "ours",
+                &mmfile[2], "theirs", NULL);
+       for (i = 0; i < 3; i++)
+               free(mmfile[i].ptr);
+
+       memset(&io, 0, sizeof(io));
+       io.io.getline = rerere_mem_getline;
+       if (output)
+               io.io.output = fopen(output, "w");
+       else
+               io.io.output = NULL;
+       strbuf_init(&io.input, 0);
+       strbuf_attach(&io.input, result.ptr, result.size, result.size);
+
+       /*
+        * Grab the conflict ID and optionally write the original
+        * contents with conflict markers out.
+        */
+       hunk_no = handle_path(sha1, (struct rerere_io *)&io, marker_size);
+       strbuf_release(&io.input);
+       if (io.io.output)
+               fclose(io.io.output);
+       return hunk_no;
 }
 
 static int rerere_forget_one_path(const char *path, struct string_list *rr)
@@ -838,11 +1036,43 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
 
        /* Nuke the recorded resolution for the conflict */
        id = new_rerere_id(sha1);
+
+       for (id->variant = 0;
+            id->variant < id->collection->status_nr;
+            id->variant++) {
+               mmfile_t cur = { NULL, 0 };
+               mmbuffer_t result = {NULL, 0};
+               int cleanly_resolved;
+
+               if (!has_rerere_resolution(id))
+                       continue;
+
+               handle_cache(path, sha1, rerere_path(id, "thisimage"));
+               if (read_mmfile(&cur, rerere_path(id, "thisimage"))) {
+                       free(cur.ptr);
+                       error("Failed to update conflicted state in '%s'", path);
+                       goto fail_exit;
+               }
+               cleanly_resolved = !try_merge(id, path, &cur, &result);
+               free(result.ptr);
+               free(cur.ptr);
+               if (cleanly_resolved)
+                       break;
+       }
+
+       if (id->collection->status_nr <= id->variant) {
+               error("no remembered resolution for '%s'", path);
+               goto fail_exit;
+       }
+
        filename = rerere_path(id, "postimage");
-       if (unlink(filename))
-               return (errno == ENOENT
-                       ? error("no remembered resolution for %s", path)
-                       : error("cannot unlink %s: %s", filename, strerror(errno)));
+       if (unlink(filename)) {
+               if (errno == ENOENT)
+                       error("no remembered resolution for %s", path);
+               else
+                       error_errno("cannot unlink %s", filename);
+               goto fail_exit;
+       }
 
        /*
         * Update the preimage so that the user can resolve the
@@ -861,6 +1091,10 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
        item->util = id;
        fprintf(stderr, "Forgot resolution for %s\n", path);
        return 0;
+
+fail_exit:
+       free(id);
+       return -1;
 }
 
 int rerere_forget(struct pathspec *pathspec)
@@ -897,29 +1131,16 @@ int rerere_forget(struct pathspec *pathspec)
  * Garbage collection support
  */
 
-/*
- * Note that this is not reentrant but is used only one-at-a-time
- * so it does not matter right now.
- */
-static struct rerere_id *dirname_to_id(const char *name)
-{
-       static struct rerere_id id;
-       xsnprintf(id.hex, sizeof(id.hex), "%s", name);
-       return &id;
-}
-
-static time_t rerere_created_at(const char *dir_name)
+static time_t rerere_created_at(struct rerere_id *id)
 {
        struct stat st;
-       struct rerere_id *id = dirname_to_id(dir_name);
 
        return stat(rerere_path(id, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
 }
 
-static time_t rerere_last_used_at(const char *dir_name)
+static time_t rerere_last_used_at(struct rerere_id *id)
 {
        struct stat st;
-       struct rerere_id *id = dirname_to_id(dir_name);
 
        return stat(rerere_path(id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
 }
@@ -929,15 +1150,28 @@ static time_t rerere_last_used_at(const char *dir_name)
  */
 static void unlink_rr_item(struct rerere_id *id)
 {
-       unlink(rerere_path(id, "thisimage"));
-       unlink(rerere_path(id, "preimage"));
-       unlink(rerere_path(id, "postimage"));
-       /*
-        * NEEDSWORK: what if this rmdir() fails?  Wouldn't we then
-        * assume that we already have preimage recorded in
-        * do_plain_rerere()?
-        */
-       rmdir(rerere_path(id, NULL));
+       unlink_or_warn(rerere_path(id, "thisimage"));
+       remove_variant(id);
+       id->collection->status[id->variant] = 0;
+}
+
+static void prune_one(struct rerere_id *id, time_t now,
+                     int cutoff_resolve, int cutoff_noresolve)
+{
+       time_t then;
+       int cutoff;
+
+       then = rerere_last_used_at(id);
+       if (then)
+               cutoff = cutoff_resolve;
+       else {
+               then = rerere_created_at(id);
+               if (!then)
+                       return;
+               cutoff = cutoff_noresolve;
+       }
+       if (then < now - cutoff * 86400)
+               unlink_rr_item(id);
 }
 
 void rerere_gc(struct string_list *rr)
@@ -945,8 +1179,8 @@ void rerere_gc(struct string_list *rr)
        struct string_list to_remove = STRING_LIST_INIT_DUP;
        DIR *dir;
        struct dirent *e;
-       int i, cutoff;
-       time_t now = time(NULL), then;
+       int i;
+       time_t now = time(NULL);
        int cutoff_noresolve = 15;
        int cutoff_resolve = 60;
 
@@ -961,25 +1195,32 @@ void rerere_gc(struct string_list *rr)
                die_errno("unable to open rr-cache directory");
        /* Collect stale conflict IDs ... */
        while ((e = readdir(dir))) {
+               struct rerere_dir *rr_dir;
+               struct rerere_id id;
+               int now_empty;
+
                if (is_dot_or_dotdot(e->d_name))
                        continue;
-
-               then = rerere_last_used_at(e->d_name);
-               if (then) {
-                       cutoff = cutoff_resolve;
-               } else {
-                       then = rerere_created_at(e->d_name);
-                       if (!then)
-                               continue;
-                       cutoff = cutoff_noresolve;
+               rr_dir = find_rerere_dir(e->d_name);
+               if (!rr_dir)
+                       continue; /* or should we remove e->d_name? */
+
+               now_empty = 1;
+               for (id.variant = 0, id.collection = rr_dir;
+                    id.variant < id.collection->status_nr;
+                    id.variant++) {
+                       prune_one(&id, now, cutoff_resolve, cutoff_noresolve);
+                       if (id.collection->status[id.variant])
+                               now_empty = 0;
                }
-               if (then < now - cutoff * 86400)
+               if (now_empty)
                        string_list_append(&to_remove, e->d_name);
        }
        closedir(dir);
-       /* ... and then remove them one-by-one */
+
+       /* ... and then remove the empty directories */
        for (i = 0; i < to_remove.nr; i++)
-               unlink_rr_item(dirname_to_id(to_remove.items[i].string));
+               rmdir(git_path("rr-cache/%s", to_remove.items[i].string));
        string_list_clear(&to_remove, 0);
        rollback_lock_file(&write_lock);
 }
@@ -1000,8 +1241,10 @@ void rerere_clear(struct string_list *merge_rr)
 
        for (i = 0; i < merge_rr->nr; i++) {
                struct rerere_id *id = merge_rr->items[i].util;
-               if (!has_rerere_resolution(id))
+               if (!has_rerere_resolution(id)) {
                        unlink_rr_item(id);
+                       rmdir(rerere_path(id, NULL));
+               }
        }
        unlink_or_warn(git_path_merge_rr());
        rollback_lock_file(&write_lock);
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..d30d1c4f802c77a47df0078f49247457bf22c1a3 100644 (file)
@@ -59,10 +59,10 @@ static void mark_tree_contents_uninteresting(struct tree *tree)
        while (tree_entry(&desc, &entry)) {
                switch (object_type(entry.mode)) {
                case OBJ_TREE:
-                       mark_tree_uninteresting(lookup_tree(entry.sha1));
+                       mark_tree_uninteresting(lookup_tree(entry.oid->hash));
                        break;
                case OBJ_BLOB:
-                       mark_blob_uninteresting(lookup_blob(entry.sha1));
+                       mark_blob_uninteresting(lookup_blob(entry.oid->hash));
                        break;
                default:
                        /* Subproject commit - not in this repository */
@@ -1356,8 +1356,10 @@ void init_revisions(struct rev_info *revs, const char *prefix)
        revs->skip_count = -1;
        revs->max_count = -1;
        revs->max_parents = -1;
+       revs->expand_tabs_in_log = -1;
 
        revs->commit_format = CMIT_FMT_DEFAULT;
+       revs->expand_tabs_in_log_default = 8;
 
        init_grep_defaults();
        grep_init(&revs->grep_filter, prefix);
@@ -1854,6 +1856,15 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->verbose_header = 1;
                revs->pretty_given = 1;
                get_commit_format(arg+9, revs);
+       } else if (!strcmp(arg, "--expand-tabs")) {
+               revs->expand_tabs_in_log = 8;
+       } else if (!strcmp(arg, "--no-expand-tabs")) {
+               revs->expand_tabs_in_log = 0;
+       } else if (skip_prefix(arg, "--expand-tabs=", &arg)) {
+               int val;
+               if (strtol_i(arg, 10, &val) < 0 || val < 0)
+                       die("'%s': not a non-negative integer", arg);
+               revs->expand_tabs_in_log = val;
        } else if (!strcmp(arg, "--show-notes") || !strcmp(arg, "--notes")) {
                revs->show_notes = 1;
                revs->show_notes_given = 1;
@@ -2327,6 +2338,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        if (revs->first_parent_only && revs->bisect)
                die(_("--first-parent is incompatible with --bisect"));
 
+       if (revs->expand_tabs_in_log < 0)
+               revs->expand_tabs_in_log = revs->expand_tabs_in_log_default;
+
        return left;
 }
 
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 2d6628012d7f3dc555d9bf385e990ac2c4ec6885..af0c8a10df0ca06e900d26486fa6c1a86bb2ff7f 100644 (file)
@@ -233,7 +233,7 @@ static int wait_or_whine(pid_t pid, const char *argv0, int in_signal)
 
        if (waiting < 0) {
                failed_errno = errno;
-               error("waitpid for %s failed: %s", argv0, strerror(errno));
+               error_errno("waitpid for %s failed", argv0);
        } else if (waiting != pid) {
                error("waitpid is confused (%s)", argv0);
        } else if (WIFSIGNALED(status)) {
@@ -420,8 +420,7 @@ int start_command(struct child_process *cmd)
                }
        }
        if (cmd->pid < 0)
-               error("cannot fork() for %s: %s", cmd->argv[0],
-                       strerror(errno));
+               error_errno("cannot fork() for %s", cmd->argv[0]);
        else if (cmd->clean_on_exit)
                mark_child_for_cleanup(cmd->pid);
 
@@ -482,7 +481,7 @@ int start_command(struct child_process *cmd)
                        cmd->dir, fhin, fhout, fherr);
        failed_errno = errno;
        if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
-               error("cannot spawn %s: %s", cmd->argv[0], strerror(errno));
+               error_errno("cannot spawn %s", cmd->argv[0]);
        if (cmd->clean_on_exit && cmd->pid >= 0)
                mark_child_for_cleanup(cmd->pid);
 
@@ -703,7 +702,7 @@ int start_async(struct async *async)
                if (pipe(fdin) < 0) {
                        if (async->out > 0)
                                close(async->out);
-                       return error("cannot create pipe: %s", strerror(errno));
+                       return error_errno("cannot create pipe");
                }
                async->in = fdin[1];
        }
@@ -715,7 +714,7 @@ int start_async(struct async *async)
                                close_pair(fdin);
                        else if (async->in)
                                close(async->in);
-                       return error("cannot create pipe: %s", strerror(errno));
+                       return error_errno("cannot create pipe");
                }
                async->out = fdout[0];
        }
@@ -740,7 +739,7 @@ int start_async(struct async *async)
 
        async->pid = fork();
        if (async->pid < 0) {
-               error("fork (async) failed: %s", strerror(errno));
+               error_errno("fork (async) failed");
                goto error;
        }
        if (!async->pid) {
@@ -787,7 +786,7 @@ int start_async(struct async *async)
        {
                int err = pthread_create(&async->tid, NULL, run_thread, async);
                if (err) {
-                       error("cannot create thread: %s", strerror(err));
+                       error_errno("cannot create thread");
                        goto error;
                }
        }
@@ -825,7 +824,10 @@ const char *find_hook(const char *name)
        static struct strbuf path = STRBUF_INIT;
 
        strbuf_reset(&path);
-       strbuf_git_path(&path, "hooks/%s", name);
+       if (git_hooks_path)
+               strbuf_addf(&path, "%s/%s", git_hooks_path, name);
+       else
+               strbuf_git_path(&path, "hooks/%s", name);
        if (access(path.buf, X_OK) < 0)
                return NULL;
        return path.buf;
@@ -912,7 +914,7 @@ struct parallel_processes {
        struct strbuf buffered_output; /* of finished children */
 };
 
-static int default_start_failure(struct strbuf *err,
+static int default_start_failure(struct strbuf *out,
                                 void *pp_cb,
                                 void *pp_task_cb)
 {
@@ -920,7 +922,7 @@ static int default_start_failure(struct strbuf *err,
 }
 
 static int default_task_finished(int result,
-                                struct strbuf *err,
+                                struct strbuf *out,
                                 void *pp_cb,
                                 void *pp_task_cb)
 {
@@ -1004,7 +1006,7 @@ static void pp_cleanup(struct parallel_processes *pp)
         * When get_next_task added messages to the buffer in its last
         * iteration, the buffered output is non empty.
         */
-       fputs(pp->buffered_output.buf, stderr);
+       strbuf_write(&pp->buffered_output, stderr);
        strbuf_release(&pp->buffered_output);
 
        sigchain_pop_common();
@@ -1089,7 +1091,7 @@ static void pp_output(struct parallel_processes *pp)
        int i = pp->output_owner;
        if (pp->children[i].state == GIT_CP_WORKING &&
            pp->children[i].err.len) {
-               fputs(pp->children[i].err.buf, stderr);
+               strbuf_write(&pp->children[i].err, stderr);
                strbuf_reset(&pp->children[i].err);
        }
 }
@@ -1127,11 +1129,11 @@ static int pp_collect_finished(struct parallel_processes *pp)
                        strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
                        strbuf_reset(&pp->children[i].err);
                } else {
-                       fputs(pp->children[i].err.buf, stderr);
+                       strbuf_write(&pp->children[i].err, stderr);
                        strbuf_reset(&pp->children[i].err);
 
                        /* Output all other finished child processes */
-                       fputs(pp->buffered_output.buf, stderr);
+                       strbuf_write(&pp->buffered_output, stderr);
                        strbuf_reset(&pp->buffered_output);
 
                        /*
index 49ba764c6c1d6b6d14e2e1cd56f3a5b6e8a37c12..11f76b04edf65fe6117604782c220e55b2d80661 100644 (file)
@@ -141,7 +141,7 @@ void NORETURN async_exit(int code);
  * return the negative signal number.
  */
 typedef int (*get_next_task_fn)(struct child_process *cp,
-                               struct strbuf *err,
+                               struct strbuf *out,
                                void *pp_cb,
                                void **pp_task_cb);
 
@@ -150,7 +150,7 @@ typedef int (*get_next_task_fn)(struct child_process *cp,
  * a new process.
  *
  * You must not write to stdout or stderr in this function. Add your
- * message to the strbuf err instead, which will be printed without
+ * message to the strbuf out instead, which will be printed without
  * messing up the output of the other parallel processes.
  *
  * pp_cb is the callback cookie as passed into run_processes_parallel,
@@ -160,7 +160,7 @@ typedef int (*get_next_task_fn)(struct child_process *cp,
  * To send a signal to other child processes for abortion, return
  * the negative signal number.
  */
-typedef int (*start_failure_fn)(struct strbuf *err,
+typedef int (*start_failure_fn)(struct strbuf *out,
                                void *pp_cb,
                                void *pp_task_cb);
 
@@ -168,7 +168,7 @@ typedef int (*start_failure_fn)(struct strbuf *err,
  * This callback is called on every child process that finished processing.
  *
  * You must not write to stdout or stderr in this function. Add your
- * message to the strbuf err instead, which will be printed without
+ * message to the strbuf out instead, which will be printed without
  * messing up the output of the other parallel processes.
  *
  * pp_cb is the callback cookie as passed into run_processes_parallel,
@@ -179,7 +179,7 @@ typedef int (*start_failure_fn)(struct strbuf *err,
  * the negative signal number.
  */
 typedef int (*task_finished_fn)(int result,
-                               struct strbuf *err,
+                               struct strbuf *out,
                                void *pp_cb,
                                void *pp_task_cb);
 
index e66f2fe0f0409cccf1647f7851044f91d68252a6..4687ad4b80bf651fd9dab66916681b71f7a6e31d 100644 (file)
@@ -875,8 +875,7 @@ static int sequencer_rollback(struct replay_opts *opts)
                return rollback_single_pick();
        }
        if (!f)
-               return error(_("cannot open %s: %s"), git_path_head_file(),
-                                               strerror(errno));
+               return error_errno(_("cannot open %s"), git_path_head_file());
        if (strbuf_getline_lf(&buf, f)) {
                error(_("cannot read %s: %s"), git_path_head_file(),
                      ferror(f) ?  strerror(errno) : _("unexpected end of file"));
index 5a86e297b51261b5f1ae9fa6dad6fa7dc86cd4ba..75dd6774136258c677fe4f4bbe2124c83b4cea68 100644 (file)
@@ -36,7 +36,7 @@ static int update_info_file(char *path, int (*generate)(FILE *))
 
 out:
        if (ret) {
-               error("unable to update %s: %s", path, strerror(errno));
+               error_errno("unable to update %s", path);
                if (fp)
                        fclose(fp);
                else if (fd >= 0)
index d0f2aa029b14451d4ffe669d48602c55060b9f42..d5e11217f523018008b3c4861d5d70068de14c72 100644 (file)
@@ -301,7 +301,7 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,
                        return -1;
                }
        }
-       if (!strcmp_icase(ent->base, normalized_objdir)) {
+       if (!fspathcmp(ent->base, normalized_objdir)) {
                free(ent);
                return -1;
        }
@@ -1107,9 +1107,8 @@ unsigned char *use_pack(struct packed_git *p,
                                PROT_READ, MAP_PRIVATE,
                                p->pack_fd, win->offset);
                        if (win->base == MAP_FAILED)
-                               die("packfile %s cannot be mapped: %s",
-                                       p->pack_name,
-                                       strerror(errno));
+                               die_errno("packfile %s cannot be mapped",
+                                         p->pack_name);
                        if (!win->offset && win->len == p->pack_size
                                && !p->do_not_close)
                                close_pack_fd(p);
@@ -1279,8 +1278,8 @@ static void prepare_packed_git_one(char *objdir, int local)
        dir = opendir(path.buf);
        if (!dir) {
                if (errno != ENOENT)
-                       error("unable to open object pack directory: %s: %s",
-                             path.buf, strerror(errno));
+                       error_errno("unable to open object pack directory: %s",
+                                   path.buf);
                strbuf_release(&path);
                return;
        }
@@ -2984,7 +2983,7 @@ int finalize_object_file(const char *tmpfile, const char *filename)
        unlink_or_warn(tmpfile);
        if (ret) {
                if (ret != EEXIST) {
-                       return error("unable to write sha1 filename %s: %s", filename, strerror(ret));
+                       return error_errno("unable to write sha1 filename %s", filename);
                }
                /* FIXME!!! Collision check here ? */
        }
@@ -2998,7 +2997,7 @@ int finalize_object_file(const char *tmpfile, const char *filename)
 static int write_buffer(int fd, const void *buf, size_t len)
 {
        if (write_in_full(fd, buf, len) < 0)
-               return error("file write error (%s)", strerror(errno));
+               return error_errno("file write error");
        return 0;
 }
 
@@ -3081,7 +3080,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
                if (errno == EACCES)
                        return error("insufficient permission for adding an object to repository database %s", get_object_directory());
                else
-                       return error("unable to create temporary file: %s", strerror(errno));
+                       return error_errno("unable to create temporary file");
        }
 
        /* Set it up */
@@ -3126,8 +3125,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
                utb.actime = mtime;
                utb.modtime = mtime;
                if (utime(tmp_file.buf, &utb) < 0)
-                       warning("failed utime() on %s: %s",
-                               tmp_file.buf, strerror(errno));
+                       warning_errno("failed utime() on %s", tmp_file.buf);
        }
 
        return finalize_object_file(tmp_file.buf, filename);
@@ -3360,7 +3358,7 @@ static int index_core(unsigned char *sha1, int fd, size_t size,
                if (size == read_in_full(fd, buf, size))
                        ret = index_mem(sha1, buf, size, type, path, flags);
                else
-                       ret = error("short read %s", strerror(errno));
+                       ret = error_errno("short read");
                free(buf);
        } else {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
@@ -3425,18 +3423,14 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned
        case S_IFREG:
                fd = open(path, O_RDONLY);
                if (fd < 0)
-                       return error("open(\"%s\"): %s", path,
-                                    strerror(errno));
+                       return error_errno("open(\"%s\")", path);
                if (index_fd(sha1, fd, st, OBJ_BLOB, path, flags) < 0)
                        return error("%s: failed to insert into database",
                                     path);
                break;
        case S_IFLNK:
-               if (strbuf_readlink(&sb, path, st->st_size)) {
-                       char *errstr = strerror(errno);
-                       return error("readlink(\"%s\"): %s", path,
-                                    errstr);
-               }
+               if (strbuf_readlink(&sb, path, st->st_size))
+                       return error_errno("readlink(\"%s\")", path);
                if (!(flags & HASH_WRITE_OBJECT))
                        hash_sha1_file(sb.buf, sb.len, blob_type, sha1);
                else if (write_sha1_file(sb.buf, sb.len, blob_type, sha1))
@@ -3492,7 +3486,7 @@ static int for_each_file_in_obj_subdir(int subdir_nr,
        if (!dir) {
                if (errno == ENOENT)
                        return 0;
-               return error("unable to open %s: %s", path->buf, strerror(errno));
+               return error_errno("unable to open %s", path->buf);
        }
 
        while ((de = readdir(dir))) {
index 776101e8d709b924033a141efa38e3f734837c79..ca7ddd6f2cf7074a1d74e011c2bd260c2ae6ffe6 100644 (file)
@@ -1214,6 +1214,15 @@ int get_sha1(const char *name, unsigned char *sha1)
        return get_sha1_with_context(name, 0, sha1, &unused);
 }
 
+/*
+ * This is like "get_sha1()", but for struct object_id.
+ */
+int get_oid(const char *name, struct object_id *oid)
+{
+       return get_sha1(name, oid->hash);
+}
+
+
 /*
  * Many callers know that the user meant to name a commit-ish by
  * syntactical positions where the object name appears.  Calling this
index 2c08dbb15381351ede663c1d41ebf3b6838a0c12..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)
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 7f67ec0c6a826cca0ba80ce5990d5f0f3a0dcaa6..debab294d421675cdebb129b257808614f607a09 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);
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..4532b11d66f8cdc0abdf003b3a16a632bd923890 100644 (file)
 #include "argv-array.h"
 #include "blob.h"
 #include "thread-utils.h"
+#include "quote.h"
 
 static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
+static int parallel_jobs = 1;
 static struct string_list changed_submodule_paths;
 static int initialized_fetch_ref_tips;
 static struct sha1_array ref_tips_before_fetch;
@@ -169,7 +171,12 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
 
 int submodule_config(const char *var, const char *value, void *cb)
 {
-       if (starts_with(var, "submodule."))
+       if (!strcmp(var, "submodule.fetchjobs")) {
+               parallel_jobs = git_config_int(var, value);
+               if (parallel_jobs < 0)
+                       die(_("negative values not allowed for submodule.fetchJobs"));
+               return 0;
+       } else if (starts_with(var, "submodule."))
                return parse_submodule_config_option(var, value);
        else if (!strcmp(var, "fetch.recursesubmodules")) {
                config_fetch_recurse_submodules = parse_fetch_recurse_submodules_arg(var, value);
@@ -210,6 +217,48 @@ void gitmodules_config(void)
        }
 }
 
+int parse_submodule_update_strategy(const char *value,
+               struct submodule_update_strategy *dst)
+{
+       free((void*)dst->command);
+       dst->command = NULL;
+       if (!strcmp(value, "none"))
+               dst->type = SM_UPDATE_NONE;
+       else if (!strcmp(value, "checkout"))
+               dst->type = SM_UPDATE_CHECKOUT;
+       else if (!strcmp(value, "rebase"))
+               dst->type = SM_UPDATE_REBASE;
+       else if (!strcmp(value, "merge"))
+               dst->type = SM_UPDATE_MERGE;
+       else if (skip_prefix(value, "!", &value)) {
+               dst->type = SM_UPDATE_COMMAND;
+               dst->command = xstrdup(value);
+       } else
+               return -1;
+       return 0;
+}
+
+const char *submodule_strategy_to_string(const struct submodule_update_strategy *s)
+{
+       struct strbuf sb = STRBUF_INIT;
+       switch (s->type) {
+       case SM_UPDATE_CHECKOUT:
+               return "checkout";
+       case SM_UPDATE_MERGE:
+               return "merge";
+       case SM_UPDATE_REBASE:
+               return "rebase";
+       case SM_UPDATE_NONE:
+               return "none";
+       case SM_UPDATE_UNSPECIFIED:
+               return NULL;
+       case SM_UPDATE_COMMAND:
+               strbuf_addf(&sb, "!%s", s->command);
+               return strbuf_detach(&sb, NULL);
+       }
+       return NULL;
+}
+
 void handle_ignore_submodules_arg(struct diff_options *diffopt,
                                  const char *arg)
 {
@@ -366,7 +415,7 @@ static int submodule_needs_pushing(const char *path, const unsigned char sha1[20
 
                argv[1] = sha1_to_hex(sha1);
                cp.argv = argv;
-               cp.env = local_repo_env;
+               prepare_submodule_repo_env(&cp.env_array);
                cp.git_cmd = 1;
                cp.no_stdin = 1;
                cp.out = -1;
@@ -453,7 +502,7 @@ static int push_submodule(const char *path)
                const char *argv[] = {"push", NULL};
 
                cp.argv = argv;
-               cp.env = local_repo_env;
+               prepare_submodule_repo_env(&cp.env_array);
                cp.git_cmd = 1;
                cp.no_stdin = 1;
                cp.dir = path;
@@ -499,7 +548,7 @@ static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
 
                argv[3] = sha1_to_hex(sha1);
                cp.argv = argv;
-               cp.env = local_repo_env;
+               prepare_submodule_repo_env(&cp.env_array);
                cp.git_cmd = 1;
                cp.no_stdin = 1;
                cp.dir = path;
@@ -682,7 +731,7 @@ static int get_next_submodule(struct child_process *cp,
                if (is_directory(git_dir)) {
                        child_process_init(cp);
                        cp->dir = strbuf_detach(&submodule_path, NULL);
-                       cp->env = local_repo_env;
+                       prepare_submodule_repo_env(&cp->env_array);
                        cp->git_cmd = 1;
                        if (!spf->quiet)
                                strbuf_addf(err, "Fetching submodule %s%s\n",
@@ -750,6 +799,9 @@ int fetch_populated_submodules(const struct argv_array *options,
        argv_array_push(&spf.args, "--recurse-submodules-default");
        /* default value, "--submodule-prefix" and its value are added later */
 
+       if (max_parallel_jobs < 0)
+               max_parallel_jobs = parallel_jobs;
+
        calculate_changed_submodule_paths();
        run_processes_parallel(max_parallel_jobs,
                               get_next_submodule,
@@ -794,7 +846,7 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
                argv[2] = "-uno";
 
        cp.argv = argv;
-       cp.env = local_repo_env;
+       prepare_submodule_repo_env(&cp.env_array);
        cp.git_cmd = 1;
        cp.no_stdin = 1;
        cp.out = -1;
@@ -855,7 +907,7 @@ int submodule_uses_gitfile(const char *path)
 
        /* Now test that all nested submodules use a gitfile too */
        cp.argv = argv;
-       cp.env = local_repo_env;
+       prepare_submodule_repo_env(&cp.env_array);
        cp.git_cmd = 1;
        cp.no_stdin = 1;
        cp.no_stderr = 1;
@@ -888,7 +940,7 @@ int ok_to_remove_submodule(const char *path)
                return 0;
 
        cp.argv = argv;
-       cp.env = local_repo_env;
+       prepare_submodule_repo_env(&cp.env_array);
        cp.git_cmd = 1;
        cp.no_stdin = 1;
        cp.out = -1;
@@ -1094,3 +1146,18 @@ void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)
        strbuf_release(&rel_path);
        free((void *)real_work_tree);
 }
+
+int parallel_submodules(void)
+{
+       return parallel_jobs;
+}
+
+void prepare_submodule_repo_env(struct argv_array *out)
+{
+       const char * const *var;
+
+       for (var = local_repo_env; *var; var++) {
+               if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
+                       argv_array_push(out, *var);
+       }
+}
index e06eaa5ebb30e825fd0721c76e7d194b0b854706..2af9390998194c7d51a3e23b14723574d6696926 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,9 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
                const char *path);
 int submodule_config(const char *var, const char *value, void *cb);
 void gitmodules_config(void);
+int parse_submodule_update_strategy(const char *value,
+               struct submodule_update_strategy *dst);
+const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
 void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
 void show_submodule_summary(FILE *f, const char *path,
                const char *line_prefix,
@@ -42,5 +60,13 @@ int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_nam
                struct string_list *needs_pushing);
 int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name);
 void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
+int parallel_submodules(void);
+
+/*
+ * Prepare the "env_array" parameter of a "struct child_process" for executing
+ * a submodule by clearing any repo-specific envirionment variables, but
+ * retaining any config in the environment.
+ */
+void prepare_submodule_repo_env(struct argv_array *out);
 
 #endif
diff --git a/t/helper/.gitignore b/t/helper/.gitignore
new file mode 100644 (file)
index 0000000..d6e8b36
--- /dev/null
@@ -0,0 +1,33 @@
+/test-chmtime
+/test-ctype
+/test-config
+/test-date
+/test-delta
+/test-dump-cache-tree
+/test-dump-split-index
+/test-dump-untracked-cache
+/test-fake-ssh
+/test-scrap-cache-tree
+/test-genrandom
+/test-hashmap
+/test-index-version
+/test-line-buffer
+/test-match-trees
+/test-mergesort
+/test-mktemp
+/test-parse-options
+/test-path-utils
+/test-prio-queue
+/test-read-cache
+/test-regex
+/test-revision-walking
+/test-run-command
+/test-sha1
+/test-sha1-array
+/test-sigchain
+/test-string-list
+/test-submodule-config
+/test-subprocess
+/test-svn-fe
+/test-urlmatch-normalization
+/test-wildmatch
diff --git a/t/helper/test-chmtime.c b/t/helper/test-chmtime.c
new file mode 100644 (file)
index 0000000..dfe8a83
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * This program can either change modification time of the given
+ * file(s) or just print it. The program does not change atime or
+ * ctime (their values are explicitly preserved).
+ *
+ * The mtime can be changed to an absolute value:
+ *
+ *     test-chmtime =<seconds> file...
+ *
+ * Relative to the current time as returned by time(3):
+ *
+ *     test-chmtime =+<seconds> (or =-<seconds>) file...
+ *
+ * Or relative to the current mtime of the file:
+ *
+ *     test-chmtime <seconds> file...
+ *     test-chmtime +<seconds> (or -<seconds>) file...
+ *
+ * Examples:
+ *
+ * To just print the mtime use --verbose and set the file mtime offset to 0:
+ *
+ *     test-chmtime -v +0 file
+ *
+ * To set the mtime to current time:
+ *
+ *     test-chmtime =+0 file
+ *
+ */
+#include "git-compat-util.h"
+#include <utime.h>
+
+static const char usage_str[] = "-v|--verbose (+|=|=+|=-|-)<seconds> <file>...";
+
+static int timespec_arg(const char *arg, long int *set_time, int *set_eq)
+{
+       char *test;
+       const char *timespec = arg;
+       *set_eq = (*timespec == '=') ? 1 : 0;
+       if (*set_eq) {
+               timespec++;
+               if (*timespec == '+') {
+                       *set_eq = 2; /* relative "in the future" */
+                       timespec++;
+               }
+       }
+       *set_time = strtol(timespec, &test, 10);
+       if (*test) {
+               fprintf(stderr, "Not a base-10 integer: %s\n", arg + 1);
+               return 0;
+       }
+       if ((*set_eq && *set_time < 0) || *set_eq == 2) {
+               time_t now = time(NULL);
+               *set_time += now;
+       }
+       return 1;
+}
+
+int main(int argc, char *argv[])
+{
+       static int verbose;
+
+       int i = 1;
+       /* no mtime change by default */
+       int set_eq = 0;
+       long int set_time = 0;
+
+       if (argc < 3)
+               goto usage;
+
+       if (strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0) {
+               verbose = 1;
+               ++i;
+       }
+       if (timespec_arg(argv[i], &set_time, &set_eq))
+               ++i;
+       else
+               goto usage;
+
+       for (; i < argc; i++) {
+               struct stat sb;
+               struct utimbuf utb;
+
+               if (stat(argv[i], &sb) < 0) {
+                       fprintf(stderr, "Failed to stat %s: %s\n",
+                               argv[i], strerror(errno));
+                       return 1;
+               }
+
+#ifdef GIT_WINDOWS_NATIVE
+               if (!(sb.st_mode & S_IWUSR) &&
+                               chmod(argv[i], sb.st_mode | S_IWUSR)) {
+                       fprintf(stderr, "Could not make user-writable %s: %s",
+                               argv[i], strerror(errno));
+                       return 1;
+               }
+#endif
+
+               utb.actime = sb.st_atime;
+               utb.modtime = set_eq ? set_time : sb.st_mtime + set_time;
+
+               if (verbose) {
+                       uintmax_t mtime = utb.modtime < 0 ? 0: utb.modtime;
+                       printf("%"PRIuMAX"\t%s\n", mtime, argv[i]);
+               }
+
+               if (utb.modtime != sb.st_mtime && utime(argv[i], &utb) < 0) {
+                       fprintf(stderr, "Failed to modify time on %s: %s\n",
+                               argv[i], strerror(errno));
+                       return 1;
+               }
+       }
+
+       return 0;
+
+usage:
+       fprintf(stderr, "usage: %s %s\n", argv[0], usage_str);
+       return 1;
+}
diff --git a/t/helper/test-config.c b/t/helper/test-config.c
new file mode 100644 (file)
index 0000000..6a77552
--- /dev/null
@@ -0,0 +1,152 @@
+#include "cache.h"
+#include "string-list.h"
+
+/*
+ * This program exposes the C API of the configuration mechanism
+ * as a set of simple commands in order to facilitate testing.
+ *
+ * Reads stdin and prints result of command to stdout:
+ *
+ * get_value -> prints the value with highest priority for the entered key
+ *
+ * get_value_multi -> prints all values for the entered key in increasing order
+ *                  of priority
+ *
+ * get_int -> print integer value for the entered key or die
+ *
+ * get_bool -> print bool value for the entered key or die
+ *
+ * get_string -> print string value for the entered key or die
+ *
+ * configset_get_value -> returns value with the highest priority for the entered key
+ *                     from a config_set constructed from files entered as arguments.
+ *
+ * configset_get_value_multi -> returns value_list for the entered key sorted in
+ *                             ascending order of priority from a config_set
+ *                             constructed from files entered as arguments.
+ *
+ * Examples:
+ *
+ * To print the value with highest priority for key "foo.bAr Baz.rock":
+ *     test-config get_value "foo.bAr Baz.rock"
+ *
+ */
+
+
+int main(int argc, char **argv)
+{
+       int i, val;
+       const char *v;
+       const struct string_list *strptr;
+       struct config_set cs;
+       git_configset_init(&cs);
+
+       if (argc < 2) {
+               fprintf(stderr, "Please, provide a command name on the command-line\n");
+               goto exit1;
+       } else if (argc == 3 && !strcmp(argv[1], "get_value")) {
+               if (!git_config_get_value(argv[2], &v)) {
+                       if (!v)
+                               printf("(NULL)\n");
+                       else
+                               printf("%s\n", v);
+                       goto exit0;
+               } else {
+                       printf("Value not found for \"%s\"\n", argv[2]);
+                       goto exit1;
+               }
+       } else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) {
+               strptr = git_config_get_value_multi(argv[2]);
+               if (strptr) {
+                       for (i = 0; i < strptr->nr; i++) {
+                               v = strptr->items[i].string;
+                               if (!v)
+                                       printf("(NULL)\n");
+                               else
+                                       printf("%s\n", v);
+                       }
+                       goto exit0;
+               } else {
+                       printf("Value not found for \"%s\"\n", argv[2]);
+                       goto exit1;
+               }
+       } else if (argc == 3 && !strcmp(argv[1], "get_int")) {
+               if (!git_config_get_int(argv[2], &val)) {
+                       printf("%d\n", val);
+                       goto exit0;
+               } else {
+                       printf("Value not found for \"%s\"\n", argv[2]);
+                       goto exit1;
+               }
+       } else if (argc == 3 && !strcmp(argv[1], "get_bool")) {
+               if (!git_config_get_bool(argv[2], &val)) {
+                       printf("%d\n", val);
+                       goto exit0;
+               } else {
+                       printf("Value not found for \"%s\"\n", argv[2]);
+                       goto exit1;
+               }
+       } else if (argc == 3 && !strcmp(argv[1], "get_string")) {
+               if (!git_config_get_string_const(argv[2], &v)) {
+                       printf("%s\n", v);
+                       goto exit0;
+               } else {
+                       printf("Value not found for \"%s\"\n", argv[2]);
+                       goto exit1;
+               }
+       } else if (!strcmp(argv[1], "configset_get_value")) {
+               for (i = 3; i < argc; i++) {
+                       int err;
+                       if ((err = git_configset_add_file(&cs, argv[i]))) {
+                               fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
+                               goto exit2;
+                       }
+               }
+               if (!git_configset_get_value(&cs, argv[2], &v)) {
+                       if (!v)
+                               printf("(NULL)\n");
+                       else
+                               printf("%s\n", v);
+                       goto exit0;
+               } else {
+                       printf("Value not found for \"%s\"\n", argv[2]);
+                       goto exit1;
+               }
+       } else if (!strcmp(argv[1], "configset_get_value_multi")) {
+               for (i = 3; i < argc; i++) {
+                       int err;
+                       if ((err = git_configset_add_file(&cs, argv[i]))) {
+                               fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
+                               goto exit2;
+                       }
+               }
+               strptr = git_configset_get_value_multi(&cs, argv[2]);
+               if (strptr) {
+                       for (i = 0; i < strptr->nr; i++) {
+                               v = strptr->items[i].string;
+                               if (!v)
+                                       printf("(NULL)\n");
+                               else
+                                       printf("%s\n", v);
+                       }
+                       goto exit0;
+               } else {
+                       printf("Value not found for \"%s\"\n", argv[2]);
+                       goto exit1;
+               }
+       }
+
+       die("%s: Please check the syntax and the function name", argv[0]);
+
+exit0:
+       git_configset_clear(&cs);
+       return 0;
+
+exit1:
+       git_configset_clear(&cs);
+       return 1;
+
+exit2:
+       git_configset_clear(&cs);
+       return 2;
+}
diff --git a/t/helper/test-ctype.c b/t/helper/test-ctype.c
new file mode 100644 (file)
index 0000000..707a821
--- /dev/null
@@ -0,0 +1,42 @@
+#include "cache.h"
+
+static int rc;
+
+static void report_error(const char *class, int ch)
+{
+       printf("%s classifies char %d (0x%02x) wrongly\n", class, ch, ch);
+       rc = 1;
+}
+
+static int is_in(const char *s, int ch)
+{
+       /* We can't find NUL using strchr.  It's classless anyway. */
+       if (ch == '\0')
+               return 0;
+       return !!strchr(s, ch);
+}
+
+#define TEST_CLASS(t,s) {                      \
+       int i;                                  \
+       for (i = 0; i < 256; i++) {             \
+               if (is_in(s, i) != t(i))        \
+                       report_error(#t, i);    \
+       }                                       \
+}
+
+#define DIGIT "0123456789"
+#define LOWER "abcdefghijklmnopqrstuvwxyz"
+#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+int main(int argc, char **argv)
+{
+       TEST_CLASS(isdigit, DIGIT);
+       TEST_CLASS(isspace, " \n\r\t");
+       TEST_CLASS(isalpha, LOWER UPPER);
+       TEST_CLASS(isalnum, LOWER UPPER DIGIT);
+       TEST_CLASS(is_glob_special, "*?[\\");
+       TEST_CLASS(is_regex_special, "$()*+.?[\\^{|");
+       TEST_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~");
+
+       return rc;
+}
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
new file mode 100644 (file)
index 0000000..63f3735
--- /dev/null
@@ -0,0 +1,73 @@
+#include "cache.h"
+
+static const char *usage_msg = "\n"
+"  test-date show [time_t]...\n"
+"  test-date parse [date]...\n"
+"  test-date approxidate [date]...\n";
+
+static void show_dates(char **argv, struct timeval *now)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       for (; *argv; argv++) {
+               time_t t = atoi(*argv);
+               show_date_relative(t, 0, now, &buf);
+               printf("%s -> %s\n", *argv, buf.buf);
+       }
+       strbuf_release(&buf);
+}
+
+static void parse_dates(char **argv, struct timeval *now)
+{
+       struct strbuf result = STRBUF_INIT;
+
+       for (; *argv; argv++) {
+               unsigned long t;
+               int tz;
+
+               strbuf_reset(&result);
+               parse_date(*argv, &result);
+               if (sscanf(result.buf, "%lu %d", &t, &tz) == 2)
+                       printf("%s -> %s\n",
+                              *argv, show_date(t, tz, DATE_MODE(ISO8601)));
+               else
+                       printf("%s -> bad\n", *argv);
+       }
+       strbuf_release(&result);
+}
+
+static void parse_approxidate(char **argv, struct timeval *now)
+{
+       for (; *argv; argv++) {
+               time_t t;
+               t = approxidate_relative(*argv, now);
+               printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601)));
+       }
+}
+
+int main(int argc, char **argv)
+{
+       struct timeval now;
+       const char *x;
+
+       x = getenv("TEST_DATE_NOW");
+       if (x) {
+               now.tv_sec = atoi(x);
+               now.tv_usec = 0;
+       }
+       else
+               gettimeofday(&now, NULL);
+
+       argv++;
+       if (!*argv)
+               usage(usage_msg);
+       if (!strcmp(*argv, "show"))
+               show_dates(argv+1, &now);
+       else if (!strcmp(*argv, "parse"))
+               parse_dates(argv+1, &now);
+       else if (!strcmp(*argv, "approxidate"))
+               parse_approxidate(argv+1, &now);
+       else
+               usage(usage_msg);
+       return 0;
+}
diff --git a/t/helper/test-delta.c b/t/helper/test-delta.c
new file mode 100644 (file)
index 0000000..4595cd6
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * test-delta.c: test code to exercise diff-delta.c and patch-delta.c
+ *
+ * (C) 2005 Nicolas Pitre <nico@fluxnic.net>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "git-compat-util.h"
+#include "delta.h"
+#include "cache.h"
+
+static const char usage_str[] =
+       "test-delta (-d|-p) <from_file> <data_file> <out_file>";
+
+int main(int argc, char *argv[])
+{
+       int fd;
+       struct stat st;
+       void *from_buf, *data_buf, *out_buf;
+       unsigned long from_size, data_size, out_size;
+
+       if (argc != 5 || (strcmp(argv[1], "-d") && strcmp(argv[1], "-p"))) {
+               fprintf(stderr, "usage: %s\n", usage_str);
+               return 1;
+       }
+
+       fd = open(argv[2], O_RDONLY);
+       if (fd < 0 || fstat(fd, &st)) {
+               perror(argv[2]);
+               return 1;
+       }
+       from_size = st.st_size;
+       from_buf = mmap(NULL, from_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (from_buf == MAP_FAILED) {
+               perror(argv[2]);
+               close(fd);
+               return 1;
+       }
+       close(fd);
+
+       fd = open(argv[3], O_RDONLY);
+       if (fd < 0 || fstat(fd, &st)) {
+               perror(argv[3]);
+               return 1;
+       }
+       data_size = st.st_size;
+       data_buf = mmap(NULL, data_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (data_buf == MAP_FAILED) {
+               perror(argv[3]);
+               close(fd);
+               return 1;
+       }
+       close(fd);
+
+       if (argv[1][1] == 'd')
+               out_buf = diff_delta(from_buf, from_size,
+                                    data_buf, data_size,
+                                    &out_size, 0);
+       else
+               out_buf = patch_delta(from_buf, from_size,
+                                     data_buf, data_size,
+                                     &out_size);
+       if (!out_buf) {
+               fprintf(stderr, "delta operation failed (returned NULL)\n");
+               return 1;
+       }
+
+       fd = open (argv[4], O_WRONLY|O_CREAT|O_TRUNC, 0666);
+       if (fd < 0 || write_in_full(fd, out_buf, out_size) != out_size) {
+               perror(argv[4]);
+               return 1;
+       }
+
+       return 0;
+}
diff --git a/t/helper/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c
new file mode 100644 (file)
index 0000000..bb53c0a
--- /dev/null
@@ -0,0 +1,67 @@
+#include "cache.h"
+#include "tree.h"
+#include "cache-tree.h"
+
+
+static void dump_one(struct cache_tree *it, const char *pfx, const char *x)
+{
+       if (it->entry_count < 0)
+               printf("%-40s %s%s (%d subtrees)\n",
+                      "invalid", x, pfx, it->subtree_nr);
+       else
+               printf("%s %s%s (%d entries, %d subtrees)\n",
+                      sha1_to_hex(it->sha1), x, pfx,
+                      it->entry_count, it->subtree_nr);
+}
+
+static int dump_cache_tree(struct cache_tree *it,
+                          struct cache_tree *ref,
+                          const char *pfx)
+{
+       int i;
+       int errs = 0;
+
+       if (!it || !ref)
+               /* missing in either */
+               return 0;
+
+       if (it->entry_count < 0) {
+               /* invalid */
+               dump_one(it, pfx, "");
+               dump_one(ref, pfx, "#(ref) ");
+       }
+       else {
+               dump_one(it, pfx, "");
+               if (hashcmp(it->sha1, ref->sha1) ||
+                   ref->entry_count != it->entry_count ||
+                   ref->subtree_nr != it->subtree_nr) {
+                       /* claims to be valid but is lying */
+                       dump_one(ref, pfx, "#(ref) ");
+                       errs = 1;
+               }
+       }
+
+       for (i = 0; i < it->subtree_nr; i++) {
+               char path[PATH_MAX];
+               struct cache_tree_sub *down = it->down[i];
+               struct cache_tree_sub *rdwn;
+
+               rdwn = cache_tree_sub(ref, down->name);
+               xsnprintf(path, sizeof(path), "%s%.*s/", pfx, down->namelen, down->name);
+               if (dump_cache_tree(down->cache_tree, rdwn->cache_tree, path))
+                       errs = 1;
+       }
+       return errs;
+}
+
+int main(int ac, char **av)
+{
+       struct index_state istate;
+       struct cache_tree *another = cache_tree();
+       if (read_cache() < 0)
+               die("unable to read index file");
+       istate = the_index;
+       istate.cache_tree = another;
+       cache_tree_update(&istate, WRITE_TREE_DRY_RUN);
+       return dump_cache_tree(active_cache_tree, another, "");
+}
diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c
new file mode 100644 (file)
index 0000000..861d28c
--- /dev/null
@@ -0,0 +1,36 @@
+#include "cache.h"
+#include "split-index.h"
+#include "ewah/ewok.h"
+
+static void show_bit(size_t pos, void *data)
+{
+       printf(" %d", (int)pos);
+}
+
+int main(int ac, char **av)
+{
+       struct split_index *si;
+       int i;
+
+       do_read_index(&the_index, av[1], 1);
+       printf("own %s\n", sha1_to_hex(the_index.sha1));
+       si = the_index.split_index;
+       if (!si) {
+               printf("not a split index\n");
+               return 0;
+       }
+       printf("base %s\n", sha1_to_hex(si->base_sha1));
+       for (i = 0; i < the_index.cache_nr; i++) {
+               struct cache_entry *ce = the_index.cache[i];
+               printf("%06o %s %d\t%s\n", ce->ce_mode,
+                      sha1_to_hex(ce->sha1), ce_stage(ce), ce->name);
+       }
+       printf("replacements:");
+       if (si->replace_bitmap)
+               ewah_each_bit(si->replace_bitmap, show_bit, NULL);
+       printf("\ndeletions:");
+       if (si->delete_bitmap)
+               ewah_each_bit(si->delete_bitmap, show_bit, NULL);
+       printf("\n");
+       return 0;
+}
diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c
new file mode 100644 (file)
index 0000000..0a1c285
--- /dev/null
@@ -0,0 +1,66 @@
+#include "cache.h"
+#include "dir.h"
+
+static int compare_untracked(const void *a_, const void *b_)
+{
+       const char *const *a = a_;
+       const char *const *b = b_;
+       return strcmp(*a, *b);
+}
+
+static int compare_dir(const void *a_, const void *b_)
+{
+       const struct untracked_cache_dir *const *a = a_;
+       const struct untracked_cache_dir *const *b = b_;
+       return strcmp((*a)->name, (*b)->name);
+}
+
+static void dump(struct untracked_cache_dir *ucd, struct strbuf *base)
+{
+       int i, len;
+       qsort(ucd->untracked, ucd->untracked_nr, sizeof(*ucd->untracked),
+             compare_untracked);
+       qsort(ucd->dirs, ucd->dirs_nr, sizeof(*ucd->dirs),
+             compare_dir);
+       len = base->len;
+       strbuf_addf(base, "%s/", ucd->name);
+       printf("%s %s", base->buf,
+              sha1_to_hex(ucd->exclude_sha1));
+       if (ucd->recurse)
+               fputs(" recurse", stdout);
+       if (ucd->check_only)
+               fputs(" check_only", stdout);
+       if (ucd->valid)
+               fputs(" valid", stdout);
+       printf("\n");
+       for (i = 0; i < ucd->untracked_nr; i++)
+               printf("%s\n", ucd->untracked[i]);
+       for (i = 0; i < ucd->dirs_nr; i++)
+               dump(ucd->dirs[i], base);
+       strbuf_setlen(base, len);
+}
+
+int main(int ac, char **av)
+{
+       struct untracked_cache *uc;
+       struct strbuf base = STRBUF_INIT;
+
+       /* Hack to avoid modifying the untracked cache when we read it */
+       ignore_untracked_cache_config = 1;
+
+       setup_git_directory();
+       if (read_cache() < 0)
+               die("unable to read index file");
+       uc = the_index.untracked;
+       if (!uc) {
+               printf("no untracked cache\n");
+               return 0;
+       }
+       printf("info/exclude %s\n", sha1_to_hex(uc->ss_info_exclude.sha1));
+       printf("core.excludesfile %s\n", sha1_to_hex(uc->ss_excludes_file.sha1));
+       printf("exclude_per_dir %s\n", uc->exclude_per_dir);
+       printf("flags %08x\n", uc->dir_flags);
+       if (uc->root)
+               dump(uc->root, &base);
+       return 0;
+}
diff --git a/t/helper/test-fake-ssh.c b/t/helper/test-fake-ssh.c
new file mode 100644 (file)
index 0000000..980de21
--- /dev/null
@@ -0,0 +1,30 @@
+#include "git-compat-util.h"
+#include "run-command.h"
+#include "strbuf.h"
+
+int main(int argc, char **argv)
+{
+       const char *trash_directory = getenv("TRASH_DIRECTORY");
+       struct strbuf buf = STRBUF_INIT;
+       FILE *f;
+       int i;
+       const char *child_argv[] = { NULL, NULL };
+
+       /* First, print all parameters into $TRASH_DIRECTORY/ssh-output */
+       if (!trash_directory)
+               die("Need a TRASH_DIRECTORY!");
+       strbuf_addf(&buf, "%s/ssh-output", trash_directory);
+       f = fopen(buf.buf, "w");
+       if (!f)
+               die("Could not write to %s", buf.buf);
+       for (i = 0; i < argc; i++)
+               fprintf(f, "%s%s", i > 0 ? " " : "", i > 0 ? argv[i] : "ssh:");
+       fprintf(f, "\n");
+       fclose(f);
+
+       /* Now, evaluate the *last* parameter */
+       if (argc < 2)
+               return 0;
+       child_argv[0] = argv[argc - 1];
+       return run_command_v_opt(child_argv, RUN_USING_SHELL);
+}
diff --git a/t/helper/test-genrandom.c b/t/helper/test-genrandom.c
new file mode 100644 (file)
index 0000000..54824d0
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Simple random data generator used to create reproducible test files.
+ * This is inspired from POSIX.1-2001 implementation example for rand().
+ * Copyright (C) 2007 by Nicolas Pitre, licensed under the GPL version 2.
+ */
+
+#include "git-compat-util.h"
+
+int main(int argc, char *argv[])
+{
+       unsigned long count, next = 0;
+       unsigned char *c;
+
+       if (argc < 2 || argc > 3) {
+               fprintf(stderr, "usage: %s <seed_string> [<size>]\n", argv[0]);
+               return 1;
+       }
+
+       c = (unsigned char *) argv[1];
+       do {
+               next = next * 11 + *c;
+       } while (*c++);
+
+       count = (argc == 3) ? strtoul(argv[2], NULL, 0) : -1L;
+
+       while (count--) {
+               next = next * 1103515245 + 12345;
+               if (putchar((next >> 16) & 0xff) == EOF)
+                       return -1;
+       }
+
+       return 0;
+}
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
new file mode 100644 (file)
index 0000000..cc2891d
--- /dev/null
@@ -0,0 +1,264 @@
+#include "git-compat-util.h"
+#include "hashmap.h"
+
+struct test_entry
+{
+       struct hashmap_entry ent;
+       /* key and value as two \0-terminated strings */
+       char key[FLEX_ARRAY];
+};
+
+static const char *get_value(const struct test_entry *e)
+{
+       return e->key + strlen(e->key) + 1;
+}
+
+static int test_entry_cmp(const struct test_entry *e1,
+               const struct test_entry *e2, const char* key)
+{
+       return strcmp(e1->key, key ? key : e2->key);
+}
+
+static int test_entry_cmp_icase(const struct test_entry *e1,
+               const struct test_entry *e2, const char* key)
+{
+       return strcasecmp(e1->key, key ? key : e2->key);
+}
+
+static struct test_entry *alloc_test_entry(int hash, char *key, int klen,
+               char *value, int vlen)
+{
+       struct test_entry *entry = malloc(sizeof(struct test_entry) + klen
+                       + vlen + 2);
+       hashmap_entry_init(entry, hash);
+       memcpy(entry->key, key, klen + 1);
+       memcpy(entry->key + klen + 1, value, vlen + 1);
+       return entry;
+}
+
+#define HASH_METHOD_FNV 0
+#define HASH_METHOD_I 1
+#define HASH_METHOD_IDIV10 2
+#define HASH_METHOD_0 3
+#define HASH_METHOD_X2 4
+#define TEST_SPARSE 8
+#define TEST_ADD 16
+#define TEST_SIZE 100000
+
+static unsigned int hash(unsigned int method, unsigned int i, const char *key)
+{
+       unsigned int hash = 0;
+       switch (method & 3)
+       {
+       case HASH_METHOD_FNV:
+               hash = strhash(key);
+               break;
+       case HASH_METHOD_I:
+               hash = i;
+               break;
+       case HASH_METHOD_IDIV10:
+               hash = i / 10;
+               break;
+       case HASH_METHOD_0:
+               hash = 0;
+               break;
+       }
+
+       if (method & HASH_METHOD_X2)
+               hash = 2 * hash;
+       return hash;
+}
+
+/*
+ * Test performance of hashmap.[ch]
+ * Usage: time echo "perfhashmap method rounds" | test-hashmap
+ */
+static void perf_hashmap(unsigned int method, unsigned int rounds)
+{
+       struct hashmap map;
+       char buf[16];
+       struct test_entry **entries;
+       unsigned int *hashes;
+       unsigned int i, j;
+
+       entries = malloc(TEST_SIZE * sizeof(struct test_entry *));
+       hashes = malloc(TEST_SIZE * sizeof(int));
+       for (i = 0; i < TEST_SIZE; i++) {
+               snprintf(buf, sizeof(buf), "%i", i);
+               entries[i] = alloc_test_entry(0, buf, strlen(buf), "", 0);
+               hashes[i] = hash(method, i, entries[i]->key);
+       }
+
+       if (method & TEST_ADD) {
+               /* test adding to the map */
+               for (j = 0; j < rounds; j++) {
+                       hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, 0);
+
+                       /* add entries */
+                       for (i = 0; i < TEST_SIZE; i++) {
+                               hashmap_entry_init(entries[i], hashes[i]);
+                               hashmap_add(&map, entries[i]);
+                       }
+
+                       hashmap_free(&map, 0);
+               }
+       } else {
+               /* test map lookups */
+               hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, 0);
+
+               /* fill the map (sparsely if specified) */
+               j = (method & TEST_SPARSE) ? TEST_SIZE / 10 : TEST_SIZE;
+               for (i = 0; i < j; i++) {
+                       hashmap_entry_init(entries[i], hashes[i]);
+                       hashmap_add(&map, entries[i]);
+               }
+
+               for (j = 0; j < rounds; j++) {
+                       for (i = 0; i < TEST_SIZE; i++) {
+                               hashmap_get_from_hash(&map, hashes[i],
+                                                     entries[i]->key);
+                       }
+               }
+
+               hashmap_free(&map, 0);
+       }
+}
+
+#define DELIM " \t\r\n"
+
+/*
+ * Read stdin line by line and print result of commands to stdout:
+ *
+ * hash key -> strhash(key) memhash(key) strihash(key) memihash(key)
+ * put key value -> NULL / old value
+ * get key -> NULL / value
+ * remove key -> NULL / old value
+ * iterate -> key1 value1\nkey2 value2\n...
+ * size -> tablesize numentries
+ *
+ * perfhashmap method rounds -> test hashmap.[ch] performance
+ */
+int main(int argc, char *argv[])
+{
+       char line[1024];
+       struct hashmap map;
+       int icase;
+
+       /* init hash map */
+       icase = argc > 1 && !strcmp("ignorecase", argv[1]);
+       hashmap_init(&map, (hashmap_cmp_fn) (icase ? test_entry_cmp_icase
+                       : test_entry_cmp), 0);
+
+       /* process commands from stdin */
+       while (fgets(line, sizeof(line), stdin)) {
+               char *cmd, *p1 = NULL, *p2 = NULL;
+               int l1 = 0, l2 = 0, hash = 0;
+               struct test_entry *entry;
+
+               /* break line into command and up to two parameters */
+               cmd = strtok(line, DELIM);
+               /* ignore empty lines */
+               if (!cmd || *cmd == '#')
+                       continue;
+
+               p1 = strtok(NULL, DELIM);
+               if (p1) {
+                       l1 = strlen(p1);
+                       hash = icase ? strihash(p1) : strhash(p1);
+                       p2 = strtok(NULL, DELIM);
+                       if (p2)
+                               l2 = strlen(p2);
+               }
+
+               if (!strcmp("hash", cmd) && l1) {
+
+                       /* print results of different hash functions */
+                       printf("%u %u %u %u\n", strhash(p1), memhash(p1, l1),
+                                       strihash(p1), memihash(p1, l1));
+
+               } else if (!strcmp("add", cmd) && l1 && l2) {
+
+                       /* create entry with key = p1, value = p2 */
+                       entry = alloc_test_entry(hash, p1, l1, p2, l2);
+
+                       /* add to hashmap */
+                       hashmap_add(&map, entry);
+
+               } else if (!strcmp("put", cmd) && l1 && l2) {
+
+                       /* create entry with key = p1, value = p2 */
+                       entry = alloc_test_entry(hash, p1, l1, p2, l2);
+
+                       /* add / replace entry */
+                       entry = hashmap_put(&map, entry);
+
+                       /* print and free replaced entry, if any */
+                       puts(entry ? get_value(entry) : "NULL");
+                       free(entry);
+
+               } else if (!strcmp("get", cmd) && l1) {
+
+                       /* lookup entry in hashmap */
+                       entry = hashmap_get_from_hash(&map, hash, p1);
+
+                       /* print result */
+                       if (!entry)
+                               puts("NULL");
+                       while (entry) {
+                               puts(get_value(entry));
+                               entry = hashmap_get_next(&map, entry);
+                       }
+
+               } else if (!strcmp("remove", cmd) && l1) {
+
+                       /* setup static key */
+                       struct hashmap_entry key;
+                       hashmap_entry_init(&key, hash);
+
+                       /* remove entry from hashmap */
+                       entry = hashmap_remove(&map, &key, p1);
+
+                       /* print result and free entry*/
+                       puts(entry ? get_value(entry) : "NULL");
+                       free(entry);
+
+               } else if (!strcmp("iterate", cmd)) {
+
+                       struct hashmap_iter iter;
+                       hashmap_iter_init(&map, &iter);
+                       while ((entry = hashmap_iter_next(&iter)))
+                               printf("%s %s\n", entry->key, get_value(entry));
+
+               } else if (!strcmp("size", cmd)) {
+
+                       /* print table sizes */
+                       printf("%u %u\n", map.tablesize, map.size);
+
+               } else if (!strcmp("intern", cmd) && l1) {
+
+                       /* test that strintern works */
+                       const char *i1 = strintern(p1);
+                       const char *i2 = strintern(p1);
+                       if (strcmp(i1, p1))
+                               printf("strintern(%s) returns %s\n", p1, i1);
+                       else if (i1 == p1)
+                               printf("strintern(%s) returns input pointer\n", p1);
+                       else if (i1 != i2)
+                               printf("strintern(%s) != strintern(%s)", i1, i2);
+                       else
+                               printf("%s\n", i1);
+
+               } else if (!strcmp("perfhashmap", cmd) && l1 && l2) {
+
+                       perf_hashmap(atoi(p1), atoi(p2));
+
+               } else {
+
+                       printf("Unknown command %s\n", cmd);
+
+               }
+       }
+
+       hashmap_free(&map, 1);
+       return 0;
+}
diff --git a/t/helper/test-index-version.c b/t/helper/test-index-version.c
new file mode 100644 (file)
index 0000000..05d4699
--- /dev/null
@@ -0,0 +1,14 @@
+#include "cache.h"
+
+int main(int argc, char **argv)
+{
+       struct cache_header hdr;
+       int version;
+
+       memset(&hdr,0,sizeof(hdr));
+       if (read(0, &hdr, sizeof(hdr)) != sizeof(hdr))
+               return 0;
+       version = ntohl(hdr.hdr_version);
+       printf("%d\n", version);
+       return 0;
+}
diff --git a/t/helper/test-line-buffer.c b/t/helper/test-line-buffer.c
new file mode 100644 (file)
index 0000000..1e58f04
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * test-line-buffer.c: code to exercise the svn importer's input helper
+ */
+
+#include "git-compat-util.h"
+#include "strbuf.h"
+#include "vcs-svn/line_buffer.h"
+
+static uint32_t strtouint32(const char *s)
+{
+       char *end;
+       uintmax_t n = strtoumax(s, &end, 10);
+       if (*s == '\0' || *end != '\0')
+               die("invalid count: %s", s);
+       return (uint32_t) n;
+}
+
+static void handle_command(const char *command, const char *arg, struct line_buffer *buf)
+{
+       switch (*command) {
+       case 'b':
+               if (starts_with(command, "binary ")) {
+                       struct strbuf sb = STRBUF_INIT;
+                       strbuf_addch(&sb, '>');
+                       buffer_read_binary(buf, &sb, strtouint32(arg));
+                       fwrite(sb.buf, 1, sb.len, stdout);
+                       strbuf_release(&sb);
+                       return;
+               }
+       case 'c':
+               if (starts_with(command, "copy ")) {
+                       buffer_copy_bytes(buf, strtouint32(arg));
+                       return;
+               }
+       case 's':
+               if (starts_with(command, "skip ")) {
+                       buffer_skip_bytes(buf, strtouint32(arg));
+                       return;
+               }
+       default:
+               die("unrecognized command: %s", command);
+       }
+}
+
+static void handle_line(const char *line, struct line_buffer *stdin_buf)
+{
+       const char *arg = strchr(line, ' ');
+       if (!arg)
+               die("no argument in line: %s", line);
+       handle_command(line, arg + 1, stdin_buf);
+}
+
+int main(int argc, char *argv[])
+{
+       struct line_buffer stdin_buf = LINE_BUFFER_INIT;
+       struct line_buffer file_buf = LINE_BUFFER_INIT;
+       struct line_buffer *input = &stdin_buf;
+       const char *filename;
+       char *s;
+
+       if (argc == 1)
+               filename = NULL;
+       else if (argc == 2)
+               filename = argv[1];
+       else
+               usage("test-line-buffer [file | &fd] < script");
+
+       if (buffer_init(&stdin_buf, NULL))
+               die_errno("open error");
+       if (filename) {
+               if (*filename == '&') {
+                       if (buffer_fdinit(&file_buf, strtouint32(filename + 1)))
+                               die_errno("error opening fd %s", filename + 1);
+               } else {
+                       if (buffer_init(&file_buf, filename))
+                               die_errno("error opening %s", filename);
+               }
+               input = &file_buf;
+       }
+
+       while ((s = buffer_read_line(&stdin_buf)))
+               handle_line(s, input);
+
+       if (filename && buffer_deinit(&file_buf))
+               die("error reading from %s", filename);
+       if (buffer_deinit(&stdin_buf))
+               die("input error");
+       if (ferror(stdout))
+               die("output error");
+       return 0;
+}
diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c
new file mode 100644 (file)
index 0000000..d446b8e
--- /dev/null
@@ -0,0 +1,26 @@
+#include "cache.h"
+#include "tree.h"
+
+int main(int ac, char **av)
+{
+       struct object_id hash1, hash2, shifted;
+       struct tree *one, *two;
+
+       setup_git_directory();
+
+       if (get_oid(av[1], &hash1))
+               die("cannot parse %s as an object name", av[1]);
+       if (get_oid(av[2], &hash2))
+               die("cannot parse %s as an object name", av[2]);
+       one = parse_tree_indirect(hash1.hash);
+       if (!one)
+               die("not a tree-ish %s", av[1]);
+       two = parse_tree_indirect(hash2.hash);
+       if (!two)
+               die("not a tree-ish %s", av[2]);
+
+       shift_tree(&one->object.oid, &two->object.oid, &shifted, -1);
+       printf("shifted: %s\n", oid_to_hex(&shifted));
+
+       exit(0);
+}
diff --git a/t/helper/test-mergesort.c b/t/helper/test-mergesort.c
new file mode 100644 (file)
index 0000000..ea3b959
--- /dev/null
@@ -0,0 +1,52 @@
+#include "cache.h"
+#include "mergesort.h"
+
+struct line {
+       char *text;
+       struct line *next;
+};
+
+static void *get_next(const void *a)
+{
+       return ((const struct line *)a)->next;
+}
+
+static void set_next(void *a, void *b)
+{
+       ((struct line *)a)->next = b;
+}
+
+static int compare_strings(const void *a, const void *b)
+{
+       const struct line *x = a, *y = b;
+       return strcmp(x->text, y->text);
+}
+
+int main(int argc, char **argv)
+{
+       struct line *line, *p = NULL, *lines = NULL;
+       struct strbuf sb = STRBUF_INIT;
+
+       for (;;) {
+               if (strbuf_getwholeline(&sb, stdin, '\n'))
+                       break;
+               line = xmalloc(sizeof(struct line));
+               line->text = strbuf_detach(&sb, NULL);
+               if (p) {
+                       line->next = p->next;
+                       p->next = line;
+               } else {
+                       line->next = NULL;
+                       lines = line;
+               }
+               p = line;
+       }
+
+       lines = llist_mergesort(lines, get_next, set_next, compare_strings);
+
+       while (lines) {
+               printf("%s", lines->text);
+               lines = lines->next;
+       }
+       return 0;
+}
diff --git a/t/helper/test-mktemp.c b/t/helper/test-mktemp.c
new file mode 100644 (file)
index 0000000..c8c5421
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * test-mktemp.c: code to exercise the creation of temporary files
+ */
+#include "git-compat-util.h"
+
+int main(int argc, char *argv[])
+{
+       if (argc != 2)
+               usage("Expected 1 parameter defining the temporary file template");
+
+       xmkstemp(xstrdup(argv[1]));
+
+       return 0;
+}
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
new file mode 100644 (file)
index 0000000..8a1235d
--- /dev/null
@@ -0,0 +1,179 @@
+#include "cache.h"
+#include "parse-options.h"
+#include "string-list.h"
+
+static int boolean = 0;
+static int integer = 0;
+static unsigned long magnitude = 0;
+static unsigned long timestamp;
+static int abbrev = 7;
+static int verbose = -1; /* unspecified */
+static int dry_run = 0, quiet = 0;
+static char *string = NULL;
+static char *file = NULL;
+static int ambiguous;
+static struct string_list list;
+
+static struct {
+       int called;
+       const char *arg;
+       int unset;
+} length_cb;
+
+static int length_callback(const struct option *opt, const char *arg, int unset)
+{
+       length_cb.called = 1;
+       length_cb.arg = arg;
+       length_cb.unset = unset;
+
+       if (unset)
+               return 1; /* do not support unset */
+
+       *(int *)opt->value = strlen(arg);
+       return 0;
+}
+
+static int number_callback(const struct option *opt, const char *arg, int unset)
+{
+       *(int *)opt->value = strtol(arg, NULL, 10);
+       return 0;
+}
+
+static int collect_expect(const struct option *opt, const char *arg, int unset)
+{
+       struct string_list *expect;
+       struct string_list_item *item;
+       struct strbuf label = STRBUF_INIT;
+       const char *colon;
+
+       if (!arg || unset)
+               die("malformed --expect option");
+
+       expect = (struct string_list *)opt->value;
+       colon = strchr(arg, ':');
+       if (!colon)
+               die("malformed --expect option, lacking a colon");
+       strbuf_add(&label, arg, colon - arg);
+       item = string_list_insert(expect, strbuf_detach(&label, NULL));
+       if (item->util)
+               die("malformed --expect option, duplicate %s", label.buf);
+       item->util = (void *)arg;
+       return 0;
+}
+
+__attribute__((format (printf,3,4)))
+static void show(struct string_list *expect, int *status, const char *fmt, ...)
+{
+       struct string_list_item *item;
+       struct strbuf buf = STRBUF_INIT;
+       va_list args;
+
+       va_start(args, fmt);
+       strbuf_vaddf(&buf, fmt, args);
+       va_end(args);
+
+       if (!expect->nr)
+               printf("%s\n", buf.buf);
+       else {
+               char *colon = strchr(buf.buf, ':');
+               if (!colon)
+                       die("malformed output format, output lacking colon: %s", fmt);
+               *colon = '\0';
+               item = string_list_lookup(expect, buf.buf);
+               *colon = ':';
+               if (!item)
+                       ; /* not among entries being checked */
+               else {
+                       if (strcmp((const char *)item->util, buf.buf)) {
+                               printf("-%s\n", (char *)item->util);
+                               printf("+%s\n", buf.buf);
+                               *status = 1;
+                       }
+               }
+       }
+       strbuf_release(&buf);
+}
+
+int main(int argc, char **argv)
+{
+       const char *prefix = "prefix/";
+       const char *usage[] = {
+               "test-parse-options <options>",
+               NULL
+       };
+       struct string_list expect = STRING_LIST_INIT_NODUP;
+       struct option options[] = {
+               OPT_BOOL(0, "yes", &boolean, "get a boolean"),
+               OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"),
+               { OPTION_SET_INT, 'B', "no-fear", &boolean, NULL,
+                 "be brave", PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
+               OPT_COUNTUP('b', "boolean", &boolean, "increment by one"),
+               OPT_BIT('4', "or4", &boolean,
+                       "bitwise-or boolean with ...0100", 4),
+               OPT_NEGBIT(0, "neg-or4", &boolean, "same as --no-or4", 4),
+               OPT_GROUP(""),
+               OPT_INTEGER('i', "integer", &integer, "get a integer"),
+               OPT_INTEGER('j', NULL, &integer, "get a integer, too"),
+               OPT_MAGNITUDE('m', "magnitude", &magnitude, "get a magnitude"),
+               OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23),
+               OPT_DATE('t', NULL, &timestamp, "get timestamp of <time>"),
+               OPT_CALLBACK('L', "length", &integer, "str",
+                       "get length of <str>", length_callback),
+               OPT_FILENAME('F', "file", &file, "set file to <file>"),
+               OPT_GROUP("String options"),
+               OPT_STRING('s', "string", &string, "string", "get a string"),
+               OPT_STRING(0, "string2", &string, "str", "get another string"),
+               OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"),
+               OPT_STRING('o', NULL, &string, "str", "get another string"),
+               OPT_NOOP_NOARG(0, "obsolete"),
+               OPT_STRING_LIST(0, "list", &list, "str", "add str to list"),
+               OPT_GROUP("Magic arguments"),
+               OPT_ARGUMENT("quux", "means --quux"),
+               OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
+                       number_callback),
+               { OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b",
+                 PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH },
+               { OPTION_COUNTUP, 0, "ambiguous", &ambiguous, NULL,
+                 "positive ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG },
+               { OPTION_COUNTUP, 0, "no-ambiguous", &ambiguous, NULL,
+                 "negative ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG },
+               OPT_GROUP("Standard options"),
+               OPT__ABBREV(&abbrev),
+               OPT__VERBOSE(&verbose, "be verbose"),
+               OPT__DRY_RUN(&dry_run, "dry run"),
+               OPT__QUIET(&quiet, "be quiet"),
+               OPT_CALLBACK(0, "expect", &expect, "string",
+                            "expected output in the variable dump",
+                            collect_expect),
+               OPT_END(),
+       };
+       int i;
+       int ret = 0;
+
+       argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0);
+
+       if (length_cb.called) {
+               const char *arg = length_cb.arg;
+               int unset = length_cb.unset;
+               show(&expect, &ret, "Callback: \"%s\", %d",
+                    (arg ? arg : "not set"), unset);
+       }
+       show(&expect, &ret, "boolean: %d", boolean);
+       show(&expect, &ret, "integer: %d", integer);
+       show(&expect, &ret, "magnitude: %lu", magnitude);
+       show(&expect, &ret, "timestamp: %lu", timestamp);
+       show(&expect, &ret, "string: %s", string ? string : "(not set)");
+       show(&expect, &ret, "abbrev: %d", abbrev);
+       show(&expect, &ret, "verbose: %d", verbose);
+       show(&expect, &ret, "quiet: %d", quiet);
+       show(&expect, &ret, "dry run: %s", dry_run ? "yes" : "no");
+       show(&expect, &ret, "file: %s", file ? file : "(not set)");
+
+       for (i = 0; i < list.nr; i++)
+               show(&expect, &ret, "list: %s", list.items[i].string);
+
+       for (i = 0; i < argc; i++)
+               show(&expect, &ret, "arg %02d: %s", i, argv[i]);
+
+       return ret;
+}
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
new file mode 100644 (file)
index 0000000..ba805b3
--- /dev/null
@@ -0,0 +1,262 @@
+#include "cache.h"
+#include "string-list.h"
+
+/*
+ * A "string_list_each_func_t" function that normalizes an entry from
+ * GIT_CEILING_DIRECTORIES.  If the path is unusable for some reason,
+ * die with an explanation.
+ */
+static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
+{
+       char *ceil = item->string;
+
+       if (!*ceil)
+               die("Empty path is not supported");
+       if (!is_absolute_path(ceil))
+               die("Path \"%s\" is not absolute", ceil);
+       if (normalize_path_copy(ceil, ceil) < 0)
+               die("Path \"%s\" could not be normalized", ceil);
+       return 1;
+}
+
+static void normalize_argv_string(const char **var, const char *input)
+{
+       if (!strcmp(input, "<null>"))
+               *var = NULL;
+       else if (!strcmp(input, "<empty>"))
+               *var = "";
+       else
+               *var = input;
+
+       if (*var && (**var == '<' || **var == '('))
+               die("Bad value: %s\n", input);
+}
+
+struct test_data {
+       const char *from;  /* input:  transform from this ... */
+       const char *to;    /* output: ... to this.            */
+       const char *alternative; /* output: ... or this.      */
+};
+
+static int test_function(struct test_data *data, char *(*func)(char *input),
+       const char *funcname)
+{
+       int failed = 0, i;
+       char buffer[1024];
+       char *to;
+
+       for (i = 0; data[i].to; i++) {
+               if (!data[i].from)
+                       to = func(NULL);
+               else {
+                       xsnprintf(buffer, sizeof(buffer), "%s", data[i].from);
+                       to = func(buffer);
+               }
+               if (!strcmp(to, data[i].to))
+                       continue;
+               if (!data[i].alternative)
+                       error("FAIL: %s(%s) => '%s' != '%s'\n",
+                               funcname, data[i].from, to, data[i].to);
+               else if (!strcmp(to, data[i].alternative))
+                       continue;
+               else
+                       error("FAIL: %s(%s) => '%s' != '%s', '%s'\n",
+                               funcname, data[i].from, to, data[i].to,
+                               data[i].alternative);
+               failed = 1;
+       }
+       return failed;
+}
+
+static struct test_data basename_data[] = {
+       /* --- POSIX type paths --- */
+       { NULL,              "."    },
+       { "",                "."    },
+       { ".",               "."    },
+       { "..",              ".."   },
+       { "/",               "/"    },
+       { "//",              "/", "//" },
+       { "///",             "/", "//" },
+       { "////",            "/", "//" },
+       { "usr",             "usr"  },
+       { "/usr",            "usr"  },
+       { "/usr/",           "usr"  },
+       { "/usr//",          "usr"  },
+       { "/usr/lib",        "lib"  },
+       { "usr/lib",         "lib"  },
+       { "usr/lib///",      "lib"  },
+
+#if defined(__MINGW32__) || defined(_MSC_VER)
+       /* --- win32 type paths --- */
+       { "\\usr",           "usr"  },
+       { "\\usr\\",         "usr"  },
+       { "\\usr\\\\",       "usr"  },
+       { "\\usr\\lib",      "lib"  },
+       { "usr\\lib",        "lib"  },
+       { "usr\\lib\\\\\\",  "lib"  },
+       { "C:/usr",          "usr"  },
+       { "C:/usr",          "usr"  },
+       { "C:/usr/",         "usr"  },
+       { "C:/usr//",        "usr"  },
+       { "C:/usr/lib",      "lib"  },
+       { "C:usr/lib",       "lib"  },
+       { "C:usr/lib///",    "lib"  },
+       { "C:",              "."    },
+       { "C:a",             "a"    },
+       { "C:/",             "/"    },
+       { "C:///",           "/"    },
+       { "\\",              "\\", "/" },
+       { "\\\\",            "\\", "/" },
+       { "\\\\\\",          "\\", "/" },
+#endif
+       { NULL,              NULL   }
+};
+
+static struct test_data dirname_data[] = {
+       /* --- POSIX type paths --- */
+       { NULL,              "."      },
+       { "",                "."      },
+       { ".",               "."      },
+       { "..",              "."      },
+       { "/",               "/"      },
+       { "//",              "/", "//" },
+       { "///",             "/", "//" },
+       { "////",            "/", "//" },
+       { "usr",             "."      },
+       { "/usr",            "/"      },
+       { "/usr/",           "/"      },
+       { "/usr//",          "/"      },
+       { "/usr/lib",        "/usr"   },
+       { "usr/lib",         "usr"    },
+       { "usr/lib///",      "usr"    },
+
+#if defined(__MINGW32__) || defined(_MSC_VER)
+       /* --- win32 type paths --- */
+       { "\\",              "\\"     },
+       { "\\\\",            "\\\\"   },
+       { "\\usr",           "\\"     },
+       { "\\usr\\",         "\\"     },
+       { "\\usr\\\\",       "\\"     },
+       { "\\usr\\lib",      "\\usr"  },
+       { "usr\\lib",        "usr"    },
+       { "usr\\lib\\\\\\",  "usr"    },
+       { "C:a",             "C:."    },
+       { "C:/",             "C:/"    },
+       { "C:///",           "C:/"    },
+       { "C:/usr",          "C:/"    },
+       { "C:/usr/",         "C:/"    },
+       { "C:/usr//",        "C:/"    },
+       { "C:/usr/lib",      "C:/usr" },
+       { "C:usr/lib",       "C:usr"  },
+       { "C:usr/lib///",    "C:usr"  },
+       { "\\\\\\",          "\\"     },
+       { "\\\\\\\\",        "\\"     },
+       { "C:",              "C:.", "." },
+#endif
+       { NULL,              NULL     }
+};
+
+int main(int argc, char **argv)
+{
+       if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
+               char *buf = xmallocz(strlen(argv[2]));
+               int rv = normalize_path_copy(buf, argv[2]);
+               if (rv)
+                       buf = "++failed++";
+               puts(buf);
+               return 0;
+       }
+
+       if (argc >= 2 && !strcmp(argv[1], "real_path")) {
+               while (argc > 2) {
+                       puts(real_path(argv[2]));
+                       argc--;
+                       argv++;
+               }
+               return 0;
+       }
+
+       if (argc >= 2 && !strcmp(argv[1], "absolute_path")) {
+               while (argc > 2) {
+                       puts(absolute_path(argv[2]));
+                       argc--;
+                       argv++;
+               }
+               return 0;
+       }
+
+       if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) {
+               int len;
+               struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
+               char *path = xstrdup(argv[2]);
+
+               /*
+                * We have to normalize the arguments because under
+                * Windows, bash mangles arguments that look like
+                * absolute POSIX paths or colon-separate lists of
+                * absolute POSIX paths into DOS paths (e.g.,
+                * "/foo:/foo/bar" might be converted to
+                * "D:\Src\msysgit\foo;D:\Src\msysgit\foo\bar"),
+                * whereas longest_ancestor_length() requires paths
+                * that use forward slashes.
+                */
+               if (normalize_path_copy(path, path))
+                       die("Path \"%s\" could not be normalized", argv[2]);
+               string_list_split(&ceiling_dirs, argv[3], PATH_SEP, -1);
+               filter_string_list(&ceiling_dirs, 0,
+                                  normalize_ceiling_entry, NULL);
+               len = longest_ancestor_length(path, &ceiling_dirs);
+               string_list_clear(&ceiling_dirs, 0);
+               free(path);
+               printf("%d\n", len);
+               return 0;
+       }
+
+       if (argc >= 4 && !strcmp(argv[1], "prefix_path")) {
+               char *prefix = argv[2];
+               int prefix_len = strlen(prefix);
+               int nongit_ok;
+               setup_git_directory_gently(&nongit_ok);
+               while (argc > 3) {
+                       puts(prefix_path(prefix, prefix_len, argv[3]));
+                       argc--;
+                       argv++;
+               }
+               return 0;
+       }
+
+       if (argc == 4 && !strcmp(argv[1], "strip_path_suffix")) {
+               char *prefix = strip_path_suffix(argv[2], argv[3]);
+               printf("%s\n", prefix ? prefix : "(null)");
+               return 0;
+       }
+
+       if (argc == 3 && !strcmp(argv[1], "print_path")) {
+               puts(argv[2]);
+               return 0;
+       }
+
+       if (argc == 4 && !strcmp(argv[1], "relative_path")) {
+               struct strbuf sb = STRBUF_INIT;
+               const char *in, *prefix, *rel;
+               normalize_argv_string(&in, argv[2]);
+               normalize_argv_string(&prefix, argv[3]);
+               rel = relative_path(in, prefix, &sb);
+               if (!rel)
+                       puts("(null)");
+               else
+                       puts(strlen(rel) > 0 ? rel : "(empty)");
+               strbuf_release(&sb);
+               return 0;
+       }
+
+       if (argc == 2 && !strcmp(argv[1], "basename"))
+               return test_function(basename_data, basename, argv[1]);
+
+       if (argc == 2 && !strcmp(argv[1], "dirname"))
+               return test_function(dirname_data, dirname, argv[1]);
+
+       fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
+               argv[1] ? argv[1] : "(there was none)");
+       return 1;
+}
diff --git a/t/helper/test-prio-queue.c b/t/helper/test-prio-queue.c
new file mode 100644 (file)
index 0000000..7be72f0
--- /dev/null
@@ -0,0 +1,39 @@
+#include "cache.h"
+#include "prio-queue.h"
+
+static int intcmp(const void *va, const void *vb, void *data)
+{
+       const int *a = va, *b = vb;
+       return *a - *b;
+}
+
+static void show(int *v)
+{
+       if (!v)
+               printf("NULL\n");
+       else
+               printf("%d\n", *v);
+       free(v);
+}
+
+int main(int argc, char **argv)
+{
+       struct prio_queue pq = { intcmp };
+
+       while (*++argv) {
+               if (!strcmp(*argv, "get"))
+                       show(prio_queue_get(&pq));
+               else if (!strcmp(*argv, "dump")) {
+                       int *v;
+                       while ((v = prio_queue_get(&pq)))
+                              show(v);
+               }
+               else {
+                       int *v = malloc(sizeof(*v));
+                       *v = atoi(*argv);
+                       prio_queue_put(&pq, v);
+               }
+       }
+
+       return 0;
+}
diff --git a/t/helper/test-read-cache.c b/t/helper/test-read-cache.c
new file mode 100644 (file)
index 0000000..b25bcf1
--- /dev/null
@@ -0,0 +1,13 @@
+#include "cache.h"
+
+int main (int argc, char **argv)
+{
+       int i, cnt = 1;
+       if (argc == 2)
+               cnt = strtol(argv[1], NULL, 0);
+       for (i = 0; i < cnt; i++) {
+               read_cache();
+               discard_cache();
+       }
+       return 0;
+}
diff --git a/t/helper/test-regex.c b/t/helper/test-regex.c
new file mode 100644 (file)
index 0000000..0dc598e
--- /dev/null
@@ -0,0 +1,20 @@
+#include "git-compat-util.h"
+
+int main(int argc, char **argv)
+{
+       char *pat = "[^={} \t]+";
+       char *str = "={}\nfred";
+       regex_t r;
+       regmatch_t m[1];
+
+       if (regcomp(&r, pat, REG_EXTENDED | REG_NEWLINE))
+               die("failed regcomp() for pattern '%s'", pat);
+       if (regexec(&r, str, 1, m, 0))
+               die("no match of pattern '%s' to string '%s'", pat, str);
+
+       /* http://sourceware.org/bugzilla/show_bug.cgi?id=3957  */
+       if (m[0].rm_so == 3) /* matches '\n' when it should not */
+               die("regex bug confirmed: re-build git with NO_REGEX=1");
+
+       exit(0);
+}
diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c
new file mode 100644 (file)
index 0000000..3d03133
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * test-revision-walking.c: test revision walking API.
+ *
+ * (C) 2012 Heiko Voigt <hvoigt@hvoigt.net>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+
+static void print_commit(struct commit *commit)
+{
+       struct strbuf sb = STRBUF_INIT;
+       struct pretty_print_context ctx = {0};
+       ctx.date_mode.type = DATE_NORMAL;
+       format_commit_message(commit, " %m %s", &sb, &ctx);
+       printf("%s\n", sb.buf);
+       strbuf_release(&sb);
+}
+
+static int run_revision_walk(void)
+{
+       struct rev_info rev;
+       struct commit *commit;
+       const char *argv[] = {NULL, "--all", NULL};
+       int argc = ARRAY_SIZE(argv) - 1;
+       int got_revision = 0;
+
+       init_revisions(&rev, NULL);
+       setup_revisions(argc, argv, &rev, NULL);
+       if (prepare_revision_walk(&rev))
+               die("revision walk setup failed");
+
+       while ((commit = get_revision(&rev)) != NULL) {
+               print_commit(commit);
+               got_revision = 1;
+       }
+
+       reset_revision_walk();
+       return got_revision;
+}
+
+int main(int argc, char **argv)
+{
+       if (argc < 2)
+               return 1;
+
+       setup_git_directory();
+
+       if (!strcmp(argv[1], "run-twice")) {
+               printf("1st\n");
+               if (!run_revision_walk())
+                       return 1;
+               printf("2nd\n");
+               if (!run_revision_walk())
+                       return 1;
+
+               return 0;
+       }
+
+       fprintf(stderr, "check usage\n");
+       return 1;
+}
diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c
new file mode 100644 (file)
index 0000000..30a64a9
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * test-run-command.c: test run command API.
+ *
+ * (C) 2009 Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "git-compat-util.h"
+#include "run-command.h"
+#include "argv-array.h"
+#include "strbuf.h"
+#include <string.h>
+#include <errno.h>
+
+static int number_callbacks;
+static int parallel_next(struct child_process *cp,
+                        struct strbuf *err,
+                        void *cb,
+                        void **task_cb)
+{
+       struct child_process *d = cb;
+       if (number_callbacks >= 4)
+               return 0;
+
+       argv_array_pushv(&cp->args, d->argv);
+       strbuf_addf(err, "preloaded output of a child\n");
+       number_callbacks++;
+       return 1;
+}
+
+static int no_job(struct child_process *cp,
+                 struct strbuf *err,
+                 void *cb,
+                 void **task_cb)
+{
+       strbuf_addf(err, "no further jobs available\n");
+       return 0;
+}
+
+static int task_finished(int result,
+                        struct strbuf *err,
+                        void *pp_cb,
+                        void *pp_task_cb)
+{
+       strbuf_addf(err, "asking for a quick stop\n");
+       return 1;
+}
+
+int main(int argc, char **argv)
+{
+       struct child_process proc = CHILD_PROCESS_INIT;
+       int jobs;
+
+       if (argc < 3)
+               return 1;
+       proc.argv = (const char **)argv + 2;
+
+       if (!strcmp(argv[1], "start-command-ENOENT")) {
+               if (start_command(&proc) < 0 && errno == ENOENT)
+                       return 0;
+               fprintf(stderr, "FAIL %s\n", argv[1]);
+               return 1;
+       }
+       if (!strcmp(argv[1], "run-command"))
+               exit(run_command(&proc));
+
+       jobs = atoi(argv[2]);
+       proc.argv = (const char **)argv + 3;
+
+       if (!strcmp(argv[1], "run-command-parallel"))
+               exit(run_processes_parallel(jobs, parallel_next,
+                                           NULL, NULL, &proc));
+
+       if (!strcmp(argv[1], "run-command-abort"))
+               exit(run_processes_parallel(jobs, parallel_next,
+                                           NULL, task_finished, &proc));
+
+       if (!strcmp(argv[1], "run-command-no-jobs"))
+               exit(run_processes_parallel(jobs, no_job,
+                                           NULL, task_finished, &proc));
+
+       fprintf(stderr, "check usage\n");
+       return 1;
+}
diff --git a/t/helper/test-scrap-cache-tree.c b/t/helper/test-scrap-cache-tree.c
new file mode 100644 (file)
index 0000000..6efee31
--- /dev/null
@@ -0,0 +1,17 @@
+#include "cache.h"
+#include "lockfile.h"
+#include "tree.h"
+#include "cache-tree.h"
+
+static struct lock_file index_lock;
+
+int main(int ac, char **av)
+{
+       hold_locked_index(&index_lock, 1);
+       if (read_cache() < 0)
+               die("unable to read index file");
+       active_cache_tree = NULL;
+       if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
+               die("unable to write index file");
+       return 0;
+}
diff --git a/t/helper/test-sha1-array.c b/t/helper/test-sha1-array.c
new file mode 100644 (file)
index 0000000..60ea1d5
--- /dev/null
@@ -0,0 +1,34 @@
+#include "cache.h"
+#include "sha1-array.h"
+
+static void print_sha1(const unsigned char sha1[20], void *data)
+{
+       puts(sha1_to_hex(sha1));
+}
+
+int main(int argc, char **argv)
+{
+       struct sha1_array array = SHA1_ARRAY_INIT;
+       struct strbuf line = STRBUF_INIT;
+
+       while (strbuf_getline(&line, stdin) != EOF) {
+               const char *arg;
+               unsigned char sha1[20];
+
+               if (skip_prefix(line.buf, "append ", &arg)) {
+                       if (get_sha1_hex(arg, sha1))
+                               die("not a hexadecimal SHA1: %s", arg);
+                       sha1_array_append(&array, sha1);
+               } else if (skip_prefix(line.buf, "lookup ", &arg)) {
+                       if (get_sha1_hex(arg, sha1))
+                               die("not a hexadecimal SHA1: %s", arg);
+                       printf("%d\n", sha1_array_lookup(&array, sha1));
+               } else if (!strcmp(line.buf, "clear"))
+                       sha1_array_clear(&array);
+               else if (!strcmp(line.buf, "for_each_unique"))
+                       sha1_array_for_each_unique(&array, print_sha1, NULL);
+               else
+                       die("unknown command: %s", line.buf);
+       }
+       return 0;
+}
diff --git a/t/helper/test-sha1.c b/t/helper/test-sha1.c
new file mode 100644 (file)
index 0000000..e57eae1
--- /dev/null
@@ -0,0 +1,56 @@
+#include "cache.h"
+
+int main(int ac, char **av)
+{
+       git_SHA_CTX ctx;
+       unsigned char sha1[20];
+       unsigned bufsz = 8192;
+       int binary = 0;
+       char *buffer;
+
+       if (ac == 2) {
+               if (!strcmp(av[1], "-b"))
+                       binary = 1;
+               else
+                       bufsz = strtoul(av[1], NULL, 10) * 1024 * 1024;
+       }
+
+       if (!bufsz)
+               bufsz = 8192;
+
+       while ((buffer = malloc(bufsz)) == NULL) {
+               fprintf(stderr, "bufsz %u is too big, halving...\n", bufsz);
+               bufsz /= 2;
+               if (bufsz < 1024)
+                       die("OOPS");
+       }
+
+       git_SHA1_Init(&ctx);
+
+       while (1) {
+               ssize_t sz, this_sz;
+               char *cp = buffer;
+               unsigned room = bufsz;
+               this_sz = 0;
+               while (room) {
+                       sz = xread(0, cp, room);
+                       if (sz == 0)
+                               break;
+                       if (sz < 0)
+                               die_errno("test-sha1");
+                       this_sz += sz;
+                       cp += sz;
+                       room -= sz;
+               }
+               if (this_sz == 0)
+                       break;
+               git_SHA1_Update(&ctx, buffer, this_sz);
+       }
+       git_SHA1_Final(sha1, &ctx);
+
+       if (binary)
+               fwrite(sha1, 1, 20, stdout);
+       else
+               puts(sha1_to_hex(sha1));
+       exit(0);
+}
diff --git a/t/helper/test-sha1.sh b/t/helper/test-sha1.sh
new file mode 100755 (executable)
index 0000000..750b95a
--- /dev/null
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+dd if=/dev/zero bs=1048576 count=100 2>/dev/null |
+/usr/bin/time t/helper/test-sha1 >/dev/null
+
+while read expect cnt pfx
+do
+       case "$expect" in '#'*) continue ;; esac
+       actual=$(
+               {
+                       test -z "$pfx" || echo "$pfx"
+                       dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null |
+                       perl -pe 'y/\000/g/'
+               } | ./t/helper/test-sha1 $cnt
+       )
+       if test "$expect" = "$actual"
+       then
+               echo "OK: $expect $cnt $pfx"
+       else
+               echo >&2 "OOPS: $cnt"
+               echo >&2 "expect: $expect"
+               echo >&2 "actual: $actual"
+               exit 1
+       fi
+done <<EOF
+da39a3ee5e6b4b0d3255bfef95601890afd80709 0
+3f786850e387550fdab836ed7e6dc881de23001b 0 a
+5277cbb45a15902137d332d97e89cf8136545485 0 ab
+03cfd743661f07975fa2f1220c5194cbaff48451 0 abc
+3330b4373640f9e4604991e73c7e86bfd8da2dc3 0 abcd
+ec11312386ad561674f724b8cca7cf1796e26d1d 0 abcde
+bdc37c074ec4ee6050d68bc133c6b912f36474df 0 abcdef
+69bca99b923859f2dc486b55b87f49689b7358c7 0 abcdefg
+e414af7161c9554089f4106d6f1797ef14a73666 0 abcdefgh
+0707f2970043f9f7c22029482db27733deaec029 0 abcdefghi
+a4dd8aa74a5636728fe52451636e2e17726033aa 1
+9986b45e2f4d7086372533bb6953a8652fa3644a 1 frotz
+23d8d4f788e8526b4877548a32577543cbaaf51f 10
+8cd23f822ab44c7f481b8c92d591f6d1fcad431c 10 frotz
+f3b5604a4e604899c1233edb3bf1cc0ede4d8c32 512
+b095bd837a371593048136e429e9ac4b476e1bb3 512 frotz
+08fa81d6190948de5ccca3966340cc48c10cceac 1200 xyzzy
+e33a291f42c30a159733dd98b8b3e4ff34158ca0 4090 4G
+#a3bf783bc20caa958f6cb24dd140a7b21984838d 9999 nitfol
+EOF
+
+exit
+
+# generating test vectors
+# inputs are number of megabytes followed by some random string to prefix.
+
+while read cnt pfx
+do
+       actual=$(
+               {
+                       test -z "$pfx" || echo "$pfx"
+                       dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null |
+                       perl -pe 'y/\000/g/'
+               } | sha1sum |
+               sed -e 's/ .*//'
+       )
+       echo "$actual $cnt $pfx"
+done <<EOF
+0
+0 a
+0 ab
+0 abc
+0 abcd
+0 abcde
+0 abcdef
+0 abcdefg
+0 abcdefgh
+0 abcdefghi
+1
+1 frotz
+10
+10 frotz
+512
+512 frotz
+1200 xyzzy
+4090 4G
+9999 nitfol
+EOF
diff --git a/t/helper/test-sigchain.c b/t/helper/test-sigchain.c
new file mode 100644 (file)
index 0000000..e499fce
--- /dev/null
@@ -0,0 +1,22 @@
+#include "cache.h"
+#include "sigchain.h"
+
+#define X(f) \
+static void f(int sig) { \
+       puts(#f); \
+       fflush(stdout); \
+       sigchain_pop(sig); \
+       raise(sig); \
+}
+X(one)
+X(two)
+X(three)
+#undef X
+
+int main(int argc, char **argv) {
+       sigchain_push(SIGTERM, one);
+       sigchain_push(SIGTERM, two);
+       sigchain_push(SIGTERM, three);
+       raise(SIGTERM);
+       return 0;
+}
diff --git a/t/helper/test-string-list.c b/t/helper/test-string-list.c
new file mode 100644 (file)
index 0000000..14bdf9d
--- /dev/null
@@ -0,0 +1,103 @@
+#include "cache.h"
+#include "string-list.h"
+
+/*
+ * Parse an argument into a string list.  arg should either be a
+ * ':'-separated list of strings, or "-" to indicate an empty string
+ * list (as opposed to "", which indicates a string list containing a
+ * single empty string).  list->strdup_strings must be set.
+ */
+static void parse_string_list(struct string_list *list, const char *arg)
+{
+       if (!strcmp(arg, "-"))
+               return;
+
+       (void)string_list_split(list, arg, ':', -1);
+}
+
+static void write_list(const struct string_list *list)
+{
+       int i;
+       for (i = 0; i < list->nr; i++)
+               printf("[%d]: \"%s\"\n", i, list->items[i].string);
+}
+
+static void write_list_compact(const struct string_list *list)
+{
+       int i;
+       if (!list->nr)
+               printf("-\n");
+       else {
+               printf("%s", list->items[0].string);
+               for (i = 1; i < list->nr; i++)
+                       printf(":%s", list->items[i].string);
+               printf("\n");
+       }
+}
+
+static int prefix_cb(struct string_list_item *item, void *cb_data)
+{
+       const char *prefix = (const char *)cb_data;
+       return starts_with(item->string, prefix);
+}
+
+int main(int argc, char **argv)
+{
+       if (argc == 5 && !strcmp(argv[1], "split")) {
+               struct string_list list = STRING_LIST_INIT_DUP;
+               int i;
+               const char *s = argv[2];
+               int delim = *argv[3];
+               int maxsplit = atoi(argv[4]);
+
+               i = string_list_split(&list, s, delim, maxsplit);
+               printf("%d\n", i);
+               write_list(&list);
+               string_list_clear(&list, 0);
+               return 0;
+       }
+
+       if (argc == 5 && !strcmp(argv[1], "split_in_place")) {
+               struct string_list list = STRING_LIST_INIT_NODUP;
+               int i;
+               char *s = xstrdup(argv[2]);
+               int delim = *argv[3];
+               int maxsplit = atoi(argv[4]);
+
+               i = string_list_split_in_place(&list, s, delim, maxsplit);
+               printf("%d\n", i);
+               write_list(&list);
+               string_list_clear(&list, 0);
+               free(s);
+               return 0;
+       }
+
+       if (argc == 4 && !strcmp(argv[1], "filter")) {
+               /*
+                * Retain only the items that have the specified prefix.
+                * Arguments: list|- prefix
+                */
+               struct string_list list = STRING_LIST_INIT_DUP;
+               const char *prefix = argv[3];
+
+               parse_string_list(&list, argv[2]);
+               filter_string_list(&list, 0, prefix_cb, (void *)prefix);
+               write_list_compact(&list);
+               string_list_clear(&list, 0);
+               return 0;
+       }
+
+       if (argc == 3 && !strcmp(argv[1], "remove_duplicates")) {
+               struct string_list list = STRING_LIST_INIT_DUP;
+
+               parse_string_list(&list, argv[2]);
+               string_list_remove_duplicates(&list, 0);
+               write_list_compact(&list);
+               string_list_clear(&list, 0);
+               return 0;
+       }
+
+       fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
+               argv[1] ? argv[1] : "(there was none)");
+       return 1;
+}
diff --git a/t/helper/test-submodule-config.c b/t/helper/test-submodule-config.c
new file mode 100644 (file)
index 0000000..dab8c27
--- /dev/null
@@ -0,0 +1,76 @@
+#include "cache.h"
+#include "submodule-config.h"
+#include "submodule.h"
+
+static void die_usage(int argc, char **argv, const char *msg)
+{
+       fprintf(stderr, "%s\n", msg);
+       fprintf(stderr, "Usage: %s [<commit> <submodulepath>] ...\n", argv[0]);
+       exit(1);
+}
+
+static int git_test_config(const char *var, const char *value, void *cb)
+{
+       return parse_submodule_config_option(var, value);
+}
+
+int main(int argc, char **argv)
+{
+       char **arg = argv;
+       int my_argc = argc;
+       int output_url = 0;
+       int lookup_name = 0;
+
+       arg++;
+       my_argc--;
+       while (starts_with(arg[0], "--")) {
+               if (!strcmp(arg[0], "--url"))
+                       output_url = 1;
+               if (!strcmp(arg[0], "--name"))
+                       lookup_name = 1;
+               arg++;
+               my_argc--;
+       }
+
+       if (my_argc % 2 != 0)
+               die_usage(argc, argv, "Wrong number of arguments.");
+
+       setup_git_directory();
+       gitmodules_config();
+       git_config(git_test_config, NULL);
+
+       while (*arg) {
+               unsigned char commit_sha1[20];
+               const struct submodule *submodule;
+               const char *commit;
+               const char *path_or_name;
+
+               commit = arg[0];
+               path_or_name = arg[1];
+
+               if (commit[0] == '\0')
+                       hashcpy(commit_sha1, null_sha1);
+               else if (get_sha1(commit, commit_sha1) < 0)
+                       die_usage(argc, argv, "Commit not found.");
+
+               if (lookup_name) {
+                       submodule = submodule_from_name(commit_sha1, path_or_name);
+               } else
+                       submodule = submodule_from_path(commit_sha1, path_or_name);
+               if (!submodule)
+                       die_usage(argc, argv, "Submodule not found.");
+
+               if (output_url)
+                       printf("Submodule url: '%s' for path '%s'\n",
+                                       submodule->url, submodule->path);
+               else
+                       printf("Submodule name: '%s' for path '%s'\n",
+                                       submodule->name, submodule->path);
+
+               arg += 2;
+       }
+
+       submodule_free();
+
+       return 0;
+}
diff --git a/t/helper/test-subprocess.c b/t/helper/test-subprocess.c
new file mode 100644 (file)
index 0000000..56881a0
--- /dev/null
@@ -0,0 +1,19 @@
+#include "cache.h"
+#include "run-command.h"
+
+int main(int argc, char **argv)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+       int nogit = 0;
+
+       setup_git_directory_gently(&nogit);
+       if (nogit)
+               die("No git repo found");
+       if (argc > 1 && !strcmp(argv[1], "--setup-work-tree")) {
+               setup_work_tree();
+               argv++;
+       }
+       cp.git_cmd = 1;
+       cp.argv = (const char **)argv + 1;
+       return run_command(&cp);
+}
diff --git a/t/helper/test-svn-fe.c b/t/helper/test-svn-fe.c
new file mode 100644 (file)
index 0000000..120ec96
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * test-svn-fe: Code to exercise the svn import lib
+ */
+
+#include "git-compat-util.h"
+#include "vcs-svn/svndump.h"
+#include "vcs-svn/svndiff.h"
+#include "vcs-svn/sliding_window.h"
+#include "vcs-svn/line_buffer.h"
+
+static const char test_svnfe_usage[] =
+       "test-svn-fe (<dumpfile> | [-d] <preimage> <delta> <len>)";
+
+static int apply_delta(int argc, char *argv[])
+{
+       struct line_buffer preimage = LINE_BUFFER_INIT;
+       struct line_buffer delta = LINE_BUFFER_INIT;
+       struct sliding_view preimage_view = SLIDING_VIEW_INIT(&preimage, -1);
+
+       if (argc != 5)
+               usage(test_svnfe_usage);
+
+       if (buffer_init(&preimage, argv[2]))
+               die_errno("cannot open preimage");
+       if (buffer_init(&delta, argv[3]))
+               die_errno("cannot open delta");
+       if (svndiff0_apply(&delta, (off_t) strtoumax(argv[4], NULL, 0),
+                                       &preimage_view, stdout))
+               return 1;
+       if (buffer_deinit(&preimage))
+               die_errno("cannot close preimage");
+       if (buffer_deinit(&delta))
+               die_errno("cannot close delta");
+       strbuf_release(&preimage_view.buf);
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       if (argc == 2) {
+               if (svndump_init(argv[1]))
+                       return 1;
+               svndump_read(NULL, "refs/heads/master", "refs/notes/svn/revs");
+               svndump_deinit();
+               svndump_reset();
+               return 0;
+       }
+
+       if (argc >= 2 && !strcmp(argv[1], "-d"))
+               return apply_delta(argc, argv);
+       usage(test_svnfe_usage);
+}
diff --git a/t/helper/test-urlmatch-normalization.c b/t/helper/test-urlmatch-normalization.c
new file mode 100644 (file)
index 0000000..090bf21
--- /dev/null
@@ -0,0 +1,50 @@
+#include "git-compat-util.h"
+#include "urlmatch.h"
+
+int main(int argc, char **argv)
+{
+       const char usage[] = "test-urlmatch-normalization [-p | -l] <url1> | <url1> <url2>";
+       char *url1, *url2;
+       int opt_p = 0, opt_l = 0;
+
+       /*
+        * For one url, succeed if url_normalize succeeds on it, fail otherwise.
+        * For two urls, succeed only if url_normalize succeeds on both and
+        * the results compare equal with strcmp.  If -p is given (one url only)
+        * and url_normalize succeeds, print the result followed by "\n".  If
+        * -l is given (one url only) and url_normalize succeeds, print the
+        * returned length in decimal followed by "\n".
+        */
+
+       if (argc > 1 && !strcmp(argv[1], "-p")) {
+               opt_p = 1;
+               argc--;
+               argv++;
+       } else if (argc > 1 && !strcmp(argv[1], "-l")) {
+               opt_l = 1;
+               argc--;
+               argv++;
+       }
+
+       if (argc < 2 || argc > 3)
+               die("%s", usage);
+
+       if (argc == 2) {
+               struct url_info info;
+               url1 = url_normalize(argv[1], &info);
+               if (!url1)
+                       return 1;
+               if (opt_p)
+                       printf("%s\n", url1);
+               if (opt_l)
+                       printf("%u\n", (unsigned)info.url_len);
+               return 0;
+       }
+
+       if (opt_p || opt_l)
+               die("%s", usage);
+
+       url1 = url_normalize(argv[1], NULL);
+       url2 = url_normalize(argv[2], NULL);
+       return (url1 && url2 && !strcmp(url1, url2)) ? 0 : 1;
+}
diff --git a/t/helper/test-wildmatch.c b/t/helper/test-wildmatch.c
new file mode 100644 (file)
index 0000000..578b164
--- /dev/null
@@ -0,0 +1,21 @@
+#include "cache.h"
+
+int main(int argc, char **argv)
+{
+       int i;
+       for (i = 2; i < argc; i++) {
+               if (argv[i][0] == '/')
+                       die("Forward slash is not allowed at the beginning of the\n"
+                           "pattern because Windows does not like it. Use `XXX/' instead.");
+               else if (!strncmp(argv[i], "XXX/", 4))
+                       argv[i] += 3;
+       }
+       if (!strcmp(argv[1], "wildmatch"))
+               return !!wildmatch(argv[3], argv[2], WM_PATHNAME, NULL);
+       else if (!strcmp(argv[1], "iwildmatch"))
+               return !!wildmatch(argv[3], argv[2], WM_PATHNAME | WM_CASEFOLD, NULL);
+       else if (!strcmp(argv[1], "pathmatch"))
+               return !!wildmatch(argv[3], argv[2], 0, NULL);
+       else
+               return 1;
+}
index 012d40e74695f8c195236a22802fd3ca6d60a8d8..54fd5a6ca02757f77004d0f8babfcc6d616162c0 100644 (file)
@@ -33,7 +33,7 @@ fi
 # Older versions of perforce were available compiled natively for
 # cygwin.  Those do not accept native windows paths, so make sure
 # not to convert for them.
-native_path() {
+native_path () {
        path="$1" &&
        if test_have_prereq CYGWIN && ! p4 -V | grep -q CYGWIN
        then
@@ -49,7 +49,7 @@ native_path() {
 # Attention: This function is not safe again against time offset updates
 # at runtime (e.g. via NTP). The 'clock_gettime(CLOCK_MONOTONIC)'
 # function could fix that but it is not in Python until 3.3.
-time_in_seconds() {
+time_in_seconds () {
        (cd / && "$PYTHON_PATH" -c 'import time; print(int(time.time()))')
 }
 
@@ -75,7 +75,7 @@ git="$TRASH_DIRECTORY/git"
 pidfile="$TRASH_DIRECTORY/p4d.pid"
 
 # Sometimes "prove" seems to hang on exit because p4d is still running
-cleanup() {
+cleanup () {
        if test -f "$pidfile"
        then
                kill -9 $(cat "$pidfile") 2>/dev/null && exit 255
@@ -89,7 +89,7 @@ trap cleanup EXIT
 TMPDIR="$TRASH_DIRECTORY"
 export TMPDIR
 
-start_p4d() {
+start_p4d () {
        mkdir -p "$db" "$cli" "$git" &&
        rm -f "$pidfile" &&
        (
@@ -151,7 +151,7 @@ start_p4d() {
        return 0
 }
 
-p4_add_user() {
+p4_add_user () {
        name=$1 &&
        p4 user -f -i <<-EOF
        User: $name
@@ -160,7 +160,16 @@ p4_add_user() {
        EOF
 }
 
-retry_until_success() {
+p4_add_job () {
+       p4 job -f -i <<-EOF
+       Job: $1
+       Status: open
+       User: dummy
+       Description:
+       EOF
+}
+
+retry_until_success () {
        timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
        until "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
        do
@@ -168,7 +177,7 @@ retry_until_success() {
        done
 }
 
-retry_until_fail() {
+retry_until_fail () {
        timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
        until ! "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
        do
@@ -176,7 +185,7 @@ retry_until_fail() {
        done
 }
 
-kill_p4d() {
+kill_p4d () {
        pid=$(cat "$pidfile")
        retry_until_fail kill $pid
        retry_until_fail kill -9 $pid
@@ -186,13 +195,13 @@ kill_p4d() {
        retry_until_fail kill -9 $watchdog_pid
 }
 
-cleanup_git() {
+cleanup_git () {
        retry_until_success rm -r "$git"
        test_must_fail test -d "$git" &&
        retry_until_success mkdir "$git"
 }
 
-marshal_dump() {
+marshal_dump () {
        what=$1 &&
        line=${2:-1} &&
        cat >"$TRASH_DIRECTORY/marshal-dump.py" <<-EOF &&
@@ -209,7 +218,7 @@ marshal_dump() {
 #
 # Construct a client with this list of View lines
 #
-client_view() {
+client_view () {
        (
                cat <<-EOF &&
                Client: $P4CLIENT
@@ -223,7 +232,7 @@ client_view() {
        ) | p4 client -i
 }
 
-is_cli_file_writeable() {
+is_cli_file_writeable () {
        # cygwin version of p4 does not set read-only attr,
        # will be marked 444 but -w is true
        file="$1" &&
index 6a50b8793e4ff840e67d20c169159f474cb9d297..fb8823224e9ed2452c32e847bf53dbc4da116f1b 100644 (file)
@@ -1,8 +1,5 @@
 . ./test-lib.sh
 
-remotes_git_svn=remotes/git""-svn
-git_svn_id=git""-svn-id
-
 if test -n "$NO_SVN_TESTS"
 then
        skip_all='skipping git svn tests, NO_SVN_TESTS defined'
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 9317ba08582d2a5d06ef5a2a8d8e63c72a146b3b..018a83a5a18431f120672b7cd8874bfb72e8d8fb 100644 (file)
@@ -102,6 +102,10 @@ Alias /auth/dumb/ www/auth/dumb/
        SetEnv GIT_HTTP_EXPORT_ALL
        Header set Set-Cookie name=value
 </LocationMatch>
+<LocationMatch /smart_headers/>
+       SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+       SetEnv GIT_HTTP_EXPORT_ALL
+</LocationMatch>
 ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
 ScriptAlias /broken_smart/ broken-smart-http.sh/
 ScriptAlias /error/ error.sh/
@@ -128,6 +132,18 @@ RewriteRule ^/ftp-redir/(.*)$ ftp://localhost:1000/$1 [R=302]
 RewriteRule ^/loop-redir/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-(.*) /$1 [R=302]
 RewriteRule ^/loop-redir/(.*)$ /loop-redir/x-$1 [R=302]
 
+# Apache 2.2 does not understand <RequireAll>, so we use RewriteCond.
+# And as RewriteCond does not allow testing for non-matches, we match
+# the desired case first (one has abra, two has cadabra), and let it
+# pass by marking the RewriteRule as [L], "last rule, do not process
+# any other matching RewriteRules after this"), and then have another
+# RewriteRule that matches all other cases and lets them fail via '[F]',
+# "fail the request".
+RewriteCond %{HTTP:x-magic-one} =abra
+RewriteCond %{HTTP:x-magic-two} =cadabra
+RewriteRule ^/smart_headers/.* - [L]
+RewriteRule ^/smart_headers/.* - [F]
+
 <IfDefine SSL>
 LoadModule ssl_module modules/mod_ssl.so
 
diff --git a/t/perf/p3404-rebase-interactive.sh b/t/perf/p3404-rebase-interactive.sh
new file mode 100755 (executable)
index 0000000..88f47de
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='Tests rebase -i performance'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+# This commit merges a sufficiently long topic branch for reasonable
+# performance testing
+branch_merge=ba5312da19c6fdb6c6747d479f58932aae6e900c^{commit}
+export branch_merge
+
+git rev-parse --verify $branch_merge >/dev/null 2>&1 || {
+       skip_all='skipping because $branch_merge was not found'
+       test_done
+}
+
+write_script swap-first-two.sh <<\EOF
+case "$1" in
+*/COMMIT_EDITMSG)
+       mv "$1" "$1".bak &&
+       sed -e '1{h;d}' -e 2G <"$1".bak >"$1"
+       ;;
+esac
+EOF
+
+test_expect_success 'setup' '
+       git config core.editor "\"$PWD"/swap-first-two.sh\" &&
+       git checkout -f $branch_merge^2
+'
+
+test_perf 'rebase -i' '
+       git rebase -i $branch_merge^
+'
+
+test_done
index 5cf74eddec7552140c5bef14f23fc637a2ad9818..18c363ea7f384ee625b0efc162dc081f51a3780f 100644 (file)
@@ -80,23 +80,29 @@ test_perf_create_repo_from () {
        error "bug in the test script: not 2 parameters to test-create-repo"
        repo="$1"
        source="$2"
-       source_git=$source/$(cd "$source" && git rev-parse --git-dir)
+       source_git="$(git -C "$source" rev-parse --git-dir)"
+       objects_dir="$(git -C "$source" rev-parse --git-path objects)"
        mkdir -p "$repo/.git"
        (
-               cd "$repo/.git" &&
-               { cp -Rl "$source_git/objects" . 2>/dev/null ||
-                       cp -R "$source_git/objects" .; } &&
+               cd "$source" &&
+               { cp -Rl "$objects_dir" "$repo/.git/" 2>/dev/null ||
+                       cp -R "$objects_dir" "$repo/.git/"; } &&
                for stuff in "$source_git"/*; do
                        case "$stuff" in
-                               */objects|*/hooks|*/config)
+                               */objects|*/hooks|*/config|*/commondir)
                                        ;;
                                *)
-                                       cp -R "$stuff" . || exit 1
+                                       cp -R "$stuff" "$repo/.git/" || exit 1
                                        ;;
                        esac
-               done &&
-               cd .. &&
-               git init -q &&
+               done
+       ) &&
+       (
+               cd "$repo" &&
+               git init -q && {
+                       test_have_prereq SYMLINKS ||
+                       git config core.symlinks false
+               } &&
                mv .git/hooks .git/hooks-disabled 2>/dev/null
        ) || error "failed to copy repository '$source' to '$repo'"
 }
index 89544dd8332793af7c792a45e6566f207ef57e38..b425f3a0d21cc9e99ce5de0c436e090e32e8f7d9 100755 (executable)
@@ -605,7 +605,7 @@ cat <<-EOF >expected-verbose
        a/b/.gitignore:8:!on*   a/b/one
        a/b/.gitignore:8:!on*   a/b/one one
        a/b/.gitignore:8:!on*   a/b/one two
-       a/b/.gitignore:8:!on*   "a/b/one\"three"
+       a/b/.gitignore:8:!on*   "a/b/one\\"three"
        a/b/.gitignore:9:!two   a/b/two
        a/.gitignore:1:two*     a/b/twooo
        $global_excludes:2:!globaltwo   globaltwo
@@ -686,7 +686,7 @@ cat <<-EOF >expected-all
        a/b/.gitignore:8:!on*   b/one
        a/b/.gitignore:8:!on*   b/one one
        a/b/.gitignore:8:!on*   b/one two
-       a/b/.gitignore:8:!on*   "b/one\"three"
+       a/b/.gitignore:8:!on*   "b/one\\"three"
        a/b/.gitignore:9:!two   b/two
        ::      b/not-ignored
        a/.gitignore:1:two*     b/twooo
index f33962b178238126d4cbe88cfbda7dd7bb747490..93725895a4050dca835fd61ef5eee75aa1174ed5 100755 (executable)
@@ -12,7 +12,7 @@ fi
 
 compare_files () {
        tr '\015\000' QN <"$1" >"$1".expect &&
-       tr '\015\000' QN <"$2" >"$2".actual &&
+       tr '\015\000' QN <"$2" | tr -d 'Z' >"$2".actual &&
        test_cmp "$1".expect "$2".actual &&
        rm "$1".expect "$2".actual
 }
@@ -52,14 +52,17 @@ create_gitattributes () {
 create_NNO_files () {
        for crlf in false true input
        do
-               for attr in "" auto text -text lf crlf
+               for attr in "" auto text -text
                do
-                       pfx=NNO_${crlf}_attr_${attr} &&
-                       cp CRLF_mix_LF ${pfx}_LF.txt &&
-                       cp CRLF_mix_LF ${pfx}_CRLF.txt &&
-                       cp CRLF_mix_LF ${pfx}_CRLF_mix_LF.txt &&
-                       cp CRLF_mix_LF ${pfx}_LF_mix_CR.txt &&
-                       cp CRLF_mix_LF ${pfx}_CRLF_nul.txt
+                       for aeol in "" lf crlf
+                       do
+                               pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf}
+                               cp CRLF_mix_LF ${pfx}_LF.txt &&
+                               cp CRLF_mix_LF ${pfx}_CRLF.txt &&
+                               cp CRLF_mix_LF ${pfx}_CRLF_mix_LF.txt &&
+                               cp CRLF_mix_LF ${pfx}_LF_mix_CR.txt &&
+                               cp CRLF_mix_LF ${pfx}_CRLF_nul.txt
+                       done
                done
        done
 }
@@ -100,20 +103,22 @@ commit_check_warn () {
 }
 
 commit_chk_wrnNNO () {
-       crlf=$1
-       attr=$2
-       lfwarn=$3
-       crlfwarn=$4
-       lfmixcrlf=$5
-       lfmixcr=$6
-       crlfnul=$7
-       pfx=NNO_${crlf}_attr_${attr}
+       attr=$1 ; shift
+       aeol=$1 ; shift
+       crlf=$1 ; shift
+       lfwarn=$1 ; shift
+       crlfwarn=$1 ; shift
+       lfmixcrlf=$1 ; shift
+       lfmixcr=$1 ; shift
+       crlfnul=$1 ; shift
+       pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf}
        #Commit files on top of existing file
-       create_gitattributes "$attr" &&
+       create_gitattributes "$attr" $aeol &&
        for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
        do
                fname=${pfx}_$f.txt &&
                cp $f $fname &&
+               printf Z >>"$fname" &&
                git -c core.autocrlf=$crlf add $fname 2>/dev/null &&
                git -c core.autocrlf=$crlf commit -m "commit_$fname" $fname >"${pfx}_$f.err" 2>&1
        done
@@ -121,19 +126,19 @@ commit_chk_wrnNNO () {
        test_expect_success "commit NNO files crlf=$crlf attr=$attr LF" '
                check_warning "$lfwarn" ${pfx}_LF.err
        '
-       test_expect_success "commit NNO files crlf=$crlf attr=$attr CRLF" '
+       test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf CRLF" '
                check_warning "$crlfwarn" ${pfx}_CRLF.err
        '
 
-       test_expect_success "commit NNO files crlf=$crlf attr=$attr CRLF_mix_LF" '
+       test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf CRLF_mix_LF" '
                check_warning "$lfmixcrlf" ${pfx}_CRLF_mix_LF.err
        '
 
-       test_expect_success "commit NNO files crlf=$crlf attr=$attr LF_mix_cr" '
+       test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf LF_mix_cr" '
                check_warning "$lfmixcr" ${pfx}_LF_mix_CR.err
        '
 
-       test_expect_success "commit NNO files crlf=$crlf attr=$attr CRLF_nul" '
+       test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf CRLF_nul" '
                check_warning "$crlfnul" ${pfx}_CRLF_nul.err
        '
 }
@@ -162,6 +167,7 @@ stats_ascii () {
 
 # contruct the attr/ returned by git ls-files --eol
 # Take none (=empty), one or two args
+# convert.c: eol=XX overrides text=auto
 attr_ascii () {
        case $1,$2 in
        -text,*)   echo "-text" ;;
@@ -169,8 +175,8 @@ attr_ascii () {
        text,lf)   echo "text eol=lf" ;;
        text,crlf) echo "text eol=crlf" ;;
        auto,)     echo "text=auto" ;;
-       auto,lf)   echo "text=auto eol=lf" ;;
-       auto,crlf) echo "text=auto eol=crlf" ;;
+       auto,lf)   echo "text eol=lf" ;;
+       auto,crlf) echo "text eol=crlf" ;;
        lf,)       echo "text eol=lf" ;;
        crlf,)     echo "text eol=crlf" ;;
        ,) echo "" ;;
@@ -195,28 +201,29 @@ check_files_in_repo () {
 }
 
 check_in_repo_NNO () {
-       crlf=$1
-       attr=$2
-       lfname=$3
-       crlfname=$4
-       lfmixcrlf=$5
-       lfmixcr=$6
-       crlfnul=$7
-       pfx=NNO_${crlf}_attr_${attr}_
-       test_expect_success "compare_files $lfname ${pfx}LF.txt" '
-               compare_files $lfname ${pfx}LF.txt
+       attr=$1 ; shift
+       aeol=$1 ; shift
+       crlf=$1 ; shift
+       lfname=$1 ; shift
+       crlfname=$1 ; shift
+       lfmixcrlf=$1 ; shift
+       lfmixcr=$1 ; shift
+       crlfnul=$1 ; shift
+       pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf}
+       test_expect_success "compare_files $lfname ${pfx}_LF.txt" '
+               compare_files $lfname ${pfx}_LF.txt
        '
-       test_expect_success "compare_files $crlfname ${pfx}CRLF.txt" '
-               compare_files $crlfname ${pfx}CRLF.txt
+       test_expect_success "compare_files $crlfname ${pfx}_CRLF.txt" '
+               compare_files $crlfname ${pfx}_CRLF.txt
        '
-       test_expect_success "compare_files $lfmixcrlf ${pfx}CRLF_mix_LF.txt" '
-               compare_files $lfmixcrlf ${pfx}CRLF_mix_LF.txt
+       test_expect_success "compare_files $lfmixcrlf ${pfx}_CRLF_mix_LF.txt" '
+               compare_files $lfmixcrlf ${pfx}_CRLF_mix_LF.txt
        '
-       test_expect_success "compare_files $lfmixcr ${pfx}LF_mix_CR.txt" '
-               compare_files $lfmixcr ${pfx}LF_mix_CR.txt
+       test_expect_success "compare_files $lfmixcr ${pfx}_LF_mix_CR.txt" '
+               compare_files $lfmixcr ${pfx}_LF_mix_CR.txt
        '
-       test_expect_success "compare_files $crlfnul ${pfx}CRLF_nul.txt" '
-               compare_files $crlfnul ${pfx}CRLF_nul.txt
+       test_expect_success "compare_files $crlfnul ${pfx}_CRLF_nul.txt" '
+               compare_files $crlfnul ${pfx}_CRLF_nul.txt
        '
 }
 
@@ -231,7 +238,7 @@ checkout_files () {
        lfmixcrlf=$1 ; shift
        lfmixcr=$1 ; shift
        crlfnul=$1 ; shift
-       create_gitattributes "$attr" "$ident" &&
+       create_gitattributes "$attr" $ident $aeol &&
        git config core.autocrlf $crlf &&
        pfx=eol_${ceol}_crlf_${crlf}_attr_${attr}_ &&
        for f in LF CRLF LF_mix_CR CRLF_mix_LF LF_nul
@@ -244,7 +251,7 @@ checkout_files () {
                fi
        done
 
-       test_expect_success "ls-files --eol attr=$attr $ident $aeol core.autocrlf=$crlf core.eol=$ceol" '
+       test_expect_success "ls-files --eol attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol" '
                test_when_finished "rm expect actual" &&
                sort <<-EOF >expect &&
                i/crlf w/$(stats_ascii $crlfname) attr/$(attr_ascii $attr $aeol) crlf_false_attr__CRLF.txt
@@ -259,19 +266,19 @@ checkout_files () {
                sort >actual &&
                test_cmp expect actual
        '
-       test_expect_success "checkout $ident $attr $aeol core.autocrlf=$crlf core.eol=$ceol file=LF" "
+       test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=LF" "
                compare_ws_file $pfx $lfname    crlf_false_attr__LF.txt
        "
-       test_expect_success "checkout $ident $attr $aeol core.autocrlf=$crlf core.eol=$ceol file=CRLF" "
+       test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=CRLF" "
                compare_ws_file $pfx $crlfname  crlf_false_attr__CRLF.txt
        "
-       test_expect_success "checkout $ident $attr $aeol core.autocrlf=$crlf core.eol=$ceol file=CRLF_mix_LF" "
+       test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=CRLF_mix_LF" "
                compare_ws_file $pfx $lfmixcrlf crlf_false_attr__CRLF_mix_LF.txt
        "
-       test_expect_success "checkout $ident $attr $aeol core.autocrlf=$crlf core.eol=$ceol file=LF_mix_CR" "
+       test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=LF_mix_CR" "
                compare_ws_file $pfx $lfmixcr   crlf_false_attr__LF_mix_CR.txt
        "
-       test_expect_success "checkout $ident $attr $aeol core.autocrlf=$crlf core.eol=$ceol file=LF_nul" "
+       test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=LF_nul" "
                compare_ws_file $pfx $crlfnul   crlf_false_attr__LF_nul.txt
        "
 }
@@ -385,31 +392,31 @@ test_expect_success 'commit files attr=crlf' '
        commit_check_warn input "crlf" "LF_CRLF" ""        "LF_CRLF" "LF_CRLF" ""
 '
 
-#                       attr   LF        CRLF      CRLFmixLF    LF_mix_CR   CRLFNUL
-commit_chk_wrnNNO false ""     ""        ""        ""           ""              ""
-commit_chk_wrnNNO true  ""     "LF_CRLF" ""        ""           ""              ""
-commit_chk_wrnNNO input ""     ""        ""        ""           ""              ""
-
+#                 attr                    LF        CRLF      CRLFmixLF   LF_mix_CR   CRLFNUL
+commit_chk_wrnNNO ""      ""      false   ""        ""        ""          ""          ""
+commit_chk_wrnNNO ""      ""      true    LF_CRLF   ""        ""          ""          ""
+commit_chk_wrnNNO ""      ""      input   ""        ""        ""          ""          ""
 
-commit_chk_wrnNNO false "auto" "$WILC"   "$WICL"   "$WAMIX"     ""              ""
-commit_chk_wrnNNO true  "auto" "LF_CRLF" ""        "LF_CRLF"    ""              ""
-commit_chk_wrnNNO input "auto" ""        "CRLF_LF" "CRLF_LF"    ""              ""
+commit_chk_wrnNNO "auto"  ""      false   "$WILC"   "$WICL"   "$WAMIX"    ""          ""
+commit_chk_wrnNNO "auto"  ""      true    LF_CRLF   ""        LF_CRLF     ""          ""
+commit_chk_wrnNNO "auto"  ""      input   ""        CRLF_LF   CRLF_LF     ""          ""
 
-commit_chk_wrnNNO false "text" "$WILC"   "$WICL"   "$WAMIX"     "$WILC"         "$WICL"
-commit_chk_wrnNNO true  "text" "LF_CRLF" ""        "LF_CRLF"    "LF_CRLF"       ""
-commit_chk_wrnNNO input "text" ""        "CRLF_LF" "CRLF_LF"    ""              "CRLF_LF"
-
-commit_chk_wrnNNO false "-text" ""       ""        ""           ""              ""
-commit_chk_wrnNNO true  "-text" ""       ""        ""           ""              ""
-commit_chk_wrnNNO input "-text" ""       ""        ""           ""              ""
-
-commit_chk_wrnNNO false "lf"    ""       "CRLF_LF" "CRLF_LF"     ""             "CRLF_LF"
-commit_chk_wrnNNO true  "lf"    ""       "CRLF_LF" "CRLF_LF"     ""             "CRLF_LF"
-commit_chk_wrnNNO input "lf"    ""       "CRLF_LF" "CRLF_LF"     ""             "CRLF_LF"
+for crlf in true false input
+do
+       commit_chk_wrnNNO -text ""      $crlf   ""        ""        ""          ""          ""
+       commit_chk_wrnNNO -text lf      $crlf   ""        ""        ""          ""          ""
+       commit_chk_wrnNNO -text crlf    $crlf   ""        ""        ""          ""          ""
+       commit_chk_wrnNNO ""    lf      $crlf   ""       CRLF_LF    CRLF_LF      ""         CRLF_LF
+       commit_chk_wrnNNO ""    crlf    $crlf   LF_CRLF   ""        LF_CRLF     LF_CRLF     ""
+       commit_chk_wrnNNO auto  lf      $crlf   ""       CRLF_LF    CRLF_LF     ""          CRLF_LF
+       commit_chk_wrnNNO auto  crlf    $crlf   LF_CRLF   ""        LF_CRLF     LF_CRLF     ""
+       commit_chk_wrnNNO text  lf      $crlf   ""       CRLF_LF    CRLF_LF     ""          CRLF_LF
+       commit_chk_wrnNNO text  crlf    $crlf   LF_CRLF   ""        LF_CRLF     LF_CRLF     ""
+done
 
-commit_chk_wrnNNO false "crlf" "LF_CRLF" ""        "LF_CRLF"    "LF_CRLF"       ""
-commit_chk_wrnNNO true  "crlf" "LF_CRLF" ""        "LF_CRLF"    "LF_CRLF"       ""
-commit_chk_wrnNNO input "crlf" "LF_CRLF" ""        "LF_CRLF"    "LF_CRLF"       ""
+commit_chk_wrnNNO "text"  ""      false   "$WILC"   "$WICL"   "$WAMIX"    "$WILC"     "$WICL"
+commit_chk_wrnNNO "text"  ""      true    LF_CRLF   ""        LF_CRLF     LF_CRLF     ""
+commit_chk_wrnNNO "text"  ""      input   ""        CRLF_LF   CRLF_LF     ""          CRLF_LF
 
 test_expect_success 'create files cleanup' '
        rm -f *.txt &&
@@ -440,24 +447,20 @@ test_expect_success 'commit -text' '
        check_files_in_repo input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
 '
 
-#                       attr    LF        CRLF      CRLF_mix_LF  LF_mix_CR     CRLFNUL
-check_in_repo_NNO false ""      LF        CRLF      CRLF_mix_LF  LF_mix_CR     CRLF_nul
-check_in_repo_NNO true  ""      LF        CRLF      CRLF_mix_LF  LF_mix_CR     CRLF_nul
-check_in_repo_NNO input ""      LF        CRLF      CRLF_mix_LF  LF_mix_CR     CRLF_nul
-
-check_in_repo_NNO false "auto"  LF        LF        LF           LF_mix_CR     CRLF_nul
-check_in_repo_NNO true  "auto"  LF        LF        LF           LF_mix_CR     CRLF_nul
-check_in_repo_NNO input "auto"  LF        LF        LF           LF_mix_CR     CRLF_nul
-
-check_in_repo_NNO false "text"  LF        LF        LF           LF_mix_CR     LF_nul
-check_in_repo_NNO true  "text"  LF        LF        LF           LF_mix_CR     LF_nul
-check_in_repo_NNO input "text"  LF        LF        LF           LF_mix_CR     LF_nul
-
-check_in_repo_NNO false "-text" LF        CRLF      CRLF_mix_LF  LF_mix_CR     CRLF_nul
-check_in_repo_NNO true  "-text" LF        CRLF      CRLF_mix_LF  LF_mix_CR     CRLF_nul
-check_in_repo_NNO input "-text" LF        CRLF      CRLF_mix_LF  LF_mix_CR     CRLF_nul
-
-
+for crlf in true false input
+do
+       #                 attr  aeol           LF  CRLF  CRLF_mix_LF  LF_mix_CR  CRLFNUL
+       check_in_repo_NNO ""    ""     $crlf   LF  CRLF  CRLF_mix_LF  LF_mix_CR  CRLF_nul
+       check_in_repo_NNO -text ""     $crlf   LF  CRLF  CRLF_mix_LF  LF_mix_CR  CRLF_nul
+       check_in_repo_NNO -text lf     $crlf   LF  CRLF  CRLF_mix_LF  LF_mix_CR  CRLF_nul
+       check_in_repo_NNO -text crlf   $crlf   LF  CRLF  CRLF_mix_LF  LF_mix_CR  CRLF_nul
+       check_in_repo_NNO auto  ""     $crlf   LF  LF    LF           LF_mix_CR  CRLF_nul
+       check_in_repo_NNO auto  lf     $crlf   LF  LF    LF           LF_mix_CR  LF_nul
+       check_in_repo_NNO auto  crlf   $crlf   LF  LF    LF           LF_mix_CR  LF_nul
+       check_in_repo_NNO text  ""     $crlf   LF  LF    LF           LF_mix_CR  LF_nul
+       check_in_repo_NNO text  lf     $crlf   LF  LF    LF           LF_mix_CR  LF_nul
+       check_in_repo_NNO text  crlf   $crlf   LF  LF    LF           LF_mix_CR  LF_nul
+done
 ################################################################################
 # Check how files in the repo are changed when they are checked out
 # How to read the table below:
@@ -489,89 +492,47 @@ LFNUL=LF_nul
 fi
 export CRLF_MIX_LF_CR MIX NL
 
-checkout_files ""      ""       ""    false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    false  crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    false  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    false  native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    input  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    true   ""       CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    true   crlf     CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    true   lf       CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    true   native   CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    false  crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    false  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    false  native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    input  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    true   ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    true   crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    true   lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    true   native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    false  ""       $NL   CRLF  $MIX_CRLF_LF LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    false  crlf     CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    false  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    false  native   $NL   CRLF  $MIX_CRLF_LF LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    input  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    true   ""       CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    true   crlf     CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    true   lf       CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    true   native   CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    false  crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    false  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    false  native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    input  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    true   ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    true   crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    true   lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    true   native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-
-for id in "" ident;
+# Same handling with and without ident
+for id in "" ident
 do
-       checkout_files "crlf"  "$id" ""    false  ""       CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    false  crlf     CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    false  lf       CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    false  native   CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    input  ""       CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    input  lf       CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    true   ""       CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    true   crlf     CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    true   lf       CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    true   native   CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "lf"    "$id" ""    false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    false  crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    false  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    false  native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    input  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    true   ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    true   crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    true   lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    true   native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "text"  "$id" ""    false  ""       $NL   CRLF  $MIX_CRLF_LF $MIX_LF_CR   $LFNUL
-       checkout_files "text"  "$id" ""    false  crlf     CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "text"  "$id" ""    false  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "text"  "$id" ""    false  native   $NL   CRLF  $MIX_CRLF_LF $MIX_LF_CR   $LFNUL
-       checkout_files "text"  "$id" ""    input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "text"  "$id" ""    input  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "text"  "$id" ""    true   ""       CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "text"  "$id" ""    true   crlf     CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "text"  "$id" ""    true   lf       CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "text"  "$id" ""    true   native   CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "-text" "$id" ""    false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    false  crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    false  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    false  native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    input  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    true   ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    true   crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    true   lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    true   native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+       for ceol in lf crlf native
+       do
+               for crlf in true false input
+               do
+                       # -text overrides core.autocrlf and core.eol
+                       # text and eol=crlf or eol=lf override core.autocrlf and core.eol
+                       checkout_files -text "$id" ""     "$crlf" "$ceol"  LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+                       checkout_files -text "$id" "lf"   "$crlf" "$ceol"  LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+                       checkout_files -text "$id" "crlf" "$crlf" "$ceol"  LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+                       # text
+                       checkout_files text  "$id" "lf"   "$crlf" "$ceol"  LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+                       checkout_files text  "$id" "crlf" "$crlf" "$ceol"  CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+                       # currently the same as text, eol=XXX
+                       checkout_files auto  "$id" "lf"   "$crlf" "$ceol"  LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+                       checkout_files auto  "$id" "crlf" "$crlf" "$ceol"  CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+               done
+
+               # core.autocrlf false, different core.eol
+               checkout_files   ""    "$id" ""     false   "$ceol"  LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+               # core.autocrlf true
+               checkout_files   ""    "$id" ""     true    "$ceol"  CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+               # text: core.autocrlf = true overrides core.eol
+               checkout_files   auto  "$id" ""     true    "$ceol"  CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
+               checkout_files   text  "$id" ""     true    "$ceol"  CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+               # text: core.autocrlf = input overrides core.eol
+               checkout_files   text  "$id" ""     input   "$ceol"  LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+               checkout_files   auto  "$id" ""     input   "$ceol"  LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+               # text=auto + eol=XXX
+       done
+       # text: core.autocrlf=false uses core.eol
+       checkout_files     text  "$id" ""     false   crlf     CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+       checkout_files     text  "$id" ""     false   lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+       # text: core.autocrlf=false and core.eol unset(or native) uses native eol
+       checkout_files     text  "$id" ""     false   ""       $NL   CRLF  $MIX_CRLF_LF $MIX_LF_CR   $LFNUL
+       checkout_files     text  "$id" ""     false   native   $NL   CRLF  $MIX_CRLF_LF $MIX_LF_CR   $LFNUL
+       # auto: core.autocrlf=false and core.eol unset(or native) uses native eol
+       checkout_files     auto  "$id" ""     false   ""       $NL   CRLF  $MIX_CRLF_LF LF_mix_CR    LF_nul
+       checkout_files     auto  "$id" ""     false   native   $NL   CRLF  $MIX_CRLF_LF LF_mix_CR    LF_nul
 done
 
 # Should be the last test case: remove some files from the worktree
index 9be6411104999959ff93276ec2bbfb065bfa71e7..db5f60d0c5882fc491ca571eb6385554148bbf3a 100755 (executable)
@@ -7,7 +7,7 @@ test_description='our own option parser'
 
 . ./test-lib.sh
 
-cat > expect << EOF
+cat >expect <<\EOF
 usage: test-parse-options <options>
 
     --yes                 get a boolean
@@ -45,63 +45,24 @@ Standard options
     -v, --verbose         be verbose
     -n, --dry-run         dry run
     -q, --quiet           be quiet
+    --expect <string>     expected output in the variable dump
 
 EOF
 
 test_expect_success 'test help' '
-       test_must_fail test-parse-options -h > output 2> output.err &&
+       test_must_fail test-parse-options -h >output 2>output.err &&
        test_must_be_empty output.err &&
        test_i18ncmp expect output
 '
 
 mv expect expect.err
 
-cat >expect.template <<EOF
-boolean: 0
-integer: 0
-magnitude: 0
-timestamp: 0
-string: (not set)
-abbrev: 7
-verbose: 0
-quiet: no
-dry run: no
-file: (not set)
-EOF
-
-check() {
+check () {
        what="$1" &&
        shift &&
        expect="$1" &&
        shift &&
-       sed "s/^$what .*/$what $expect/" <expect.template >expect &&
-       test-parse-options $* >output 2>output.err &&
-       test_must_be_empty output.err &&
-       test_cmp expect output
-}
-
-check_i18n() {
-       what="$1" &&
-       shift &&
-       expect="$1" &&
-       shift &&
-       sed "s/^$what .*/$what $expect/" <expect.template >expect &&
-       test-parse-options $* >output 2>output.err &&
-       test_must_be_empty output.err &&
-       test_i18ncmp expect output
-}
-
-check_unknown() {
-       case "$1" in
-       --*)
-               echo error: unknown option \`${1#--}\' >expect ;;
-       -*)
-               echo error: unknown switch \`${1#-}\' >expect ;;
-       esac &&
-       cat expect.err >>expect &&
-       test_must_fail test-parse-options $* >output 2>output.err &&
-       test_must_be_empty output &&
-       test_cmp expect output.err
+       test-parse-options --expect="$what $expect" "$@"
 }
 
 check_unknown_i18n() {
@@ -156,7 +117,7 @@ test_expect_success 'OPT_MAGNITUDE() 3giga' '
        check magnitude: 3221225472 -m 3g
 '
 
-cat > expect << EOF
+cat >expect <<\EOF
 boolean: 2
 integer: 1729
 magnitude: 16384
@@ -164,7 +125,7 @@ timestamp: 0
 string: 123
 abbrev: 7
 verbose: 2
-quiet: no
+quiet: 0
 dry run: yes
 file: prefix/my.file
 EOF
@@ -176,7 +137,7 @@ test_expect_success 'short options' '
        test_must_be_empty output.err
 '
 
-cat > expect << EOF
+cat >expect <<\EOF
 boolean: 2
 integer: 1729
 magnitude: 16384
@@ -184,7 +145,7 @@ timestamp: 0
 string: 321
 abbrev: 10
 verbose: 2
-quiet: no
+quiet: 0
 dry run: no
 file: prefix/fi.le
 EOF
@@ -204,15 +165,15 @@ test_expect_success 'missing required value' '
        test_expect_code 129 test-parse-options --file
 '
 
-cat > expect << EOF
+cat >expect <<\EOF
 boolean: 1
 integer: 13
 magnitude: 0
 timestamp: 0
 string: 123
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 arg 00: a1
@@ -222,32 +183,32 @@ EOF
 
 test_expect_success 'intermingled arguments' '
        test-parse-options a1 --string 123 b1 --boolean -j 13 -- --boolean \
-               > output 2> output.err &&
+               >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
-cat > expect << EOF
+cat >expect <<\EOF
 boolean: 0
 integer: 2
 magnitude: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 EOF
 
 test_expect_success 'unambiguously abbreviated option' '
-       test-parse-options --int 2 --boolean --no-bo > output 2> output.err &&
+       test-parse-options --int 2 --boolean --no-bo >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
 test_expect_success 'unambiguously abbreviated option with "="' '
-       test-parse-options --int=2 > output 2> output.err &&
+       test-parse-options --int=2 >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
@@ -256,74 +217,74 @@ test_expect_success 'ambiguously abbreviated option' '
        test_expect_code 129 test-parse-options --strin 123
 '
 
-cat > expect << EOF
+cat >expect <<\EOF
 boolean: 0
 integer: 0
 magnitude: 0
 timestamp: 0
 string: 123
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 EOF
 
 test_expect_success 'non ambiguous option (after two options it abbreviates)' '
-       test-parse-options --st 123 > output 2> output.err &&
+       test-parse-options --st 123 >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
-cat > typo.err << EOF
-error: did you mean \`--boolean\` (with two dashes ?)
+cat >typo.err <<\EOF
+error: did you mean `--boolean` (with two dashes ?)
 EOF
 
 test_expect_success 'detect possible typos' '
-       test_must_fail test-parse-options -boolean > output 2> output.err &&
+       test_must_fail test-parse-options -boolean >output 2>output.err &&
        test_must_be_empty output &&
        test_cmp typo.err output.err
 '
 
-cat > typo.err << EOF
-error: did you mean \`--ambiguous\` (with two dashes ?)
+cat >typo.err <<\EOF
+error: did you mean `--ambiguous` (with two dashes ?)
 EOF
 
 test_expect_success 'detect possible typos' '
-       test_must_fail test-parse-options -ambiguous > output 2> output.err &&
+       test_must_fail test-parse-options -ambiguous >output 2>output.err &&
        test_must_be_empty output &&
        test_cmp typo.err output.err
 '
 
-cat > expect <<EOF
+cat >expect <<\EOF
 boolean: 0
 integer: 0
 magnitude: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 arg 00: --quux
 EOF
 
 test_expect_success 'keep some options as arguments' '
-       test-parse-options --quux > output 2> output.err &&
+       test-parse-options --quux >output 2>output.err &&
        test_must_be_empty output.err &&
-        test_cmp expect output
+       test_cmp expect output
 '
 
-cat > expect <<EOF
+cat >expect <<\EOF
 boolean: 0
 integer: 0
 magnitude: 0
 timestamp: 1
 string: (not set)
 abbrev: 7
-verbose: 0
-quiet: yes
+verbose: -1
+quiet: 1
 dry run: no
 file: (not set)
 arg 00: foo
@@ -331,12 +292,12 @@ EOF
 
 test_expect_success 'OPT_DATE() works' '
        test-parse-options -t "1970-01-01 00:00:01 +0000" \
-               foo -q > output 2> output.err &&
+               foo -q >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
-cat > expect <<EOF
+cat >expect <<\EOF
 Callback: "four", 0
 boolean: 5
 integer: 4
@@ -344,112 +305,110 @@ magnitude: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 EOF
 
 test_expect_success 'OPT_CALLBACK() and OPT_BIT() work' '
-       test-parse-options --length=four -b -4 > output 2> output.err &&
+       test-parse-options --length=four -b -4 >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
-cat > expect <<EOF
-Callback: "not set", 1
-EOF
+>expect
 
 test_expect_success 'OPT_CALLBACK() and callback errors work' '
-       test_must_fail test-parse-options --no-length > output 2> output.err &&
+       test_must_fail test-parse-options --no-length >output 2>output.err &&
        test_i18ncmp expect output &&
        test_i18ncmp expect.err output.err
 '
 
-cat > expect <<EOF
+cat >expect <<\EOF
 boolean: 1
 integer: 23
 magnitude: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 EOF
 
 test_expect_success 'OPT_BIT() and OPT_SET_INT() work' '
-       test-parse-options --set23 -bbbbb --no-or4 > output 2> output.err &&
+       test-parse-options --set23 -bbbbb --no-or4 >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
 test_expect_success 'OPT_NEGBIT() and OPT_SET_INT() work' '
-       test-parse-options --set23 -bbbbb --neg-or4 > output 2> output.err &&
+       test-parse-options --set23 -bbbbb --neg-or4 >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
-cat > expect <<EOF
+cat >expect <<\EOF
 boolean: 6
 integer: 0
 magnitude: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 EOF
 
 test_expect_success 'OPT_BIT() works' '
-       test-parse-options -bb --or4 > output 2> output.err &&
+       test-parse-options -bb --or4 >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
 test_expect_success 'OPT_NEGBIT() works' '
-       test-parse-options -bb --no-neg-or4 > output 2> output.err &&
+       test-parse-options -bb --no-neg-or4 >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
 test_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' '
-       test-parse-options + + + + + + > output 2> output.err &&
+       test-parse-options + + + + + + >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
-cat > expect <<EOF
+cat >expect <<\EOF
 boolean: 0
 integer: 12345
 magnitude: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 EOF
 
 test_expect_success 'OPT_NUMBER_CALLBACK() works' '
-       test-parse-options -12345 > output 2> output.err &&
+       test-parse-options -12345 >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
-cat >expect <<EOF
+cat >expect <<\EOF
 boolean: 0
 integer: 0
 magnitude: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 EOF
@@ -460,7 +419,7 @@ test_expect_success 'negation of OPT_NONEG flags is not ambiguous' '
        test_cmp expect output
 '
 
-cat >>expect <<'EOF'
+cat >>expect <<\EOF
 list: foo
 list: bar
 list: baz
@@ -476,4 +435,118 @@ test_expect_success '--no-list resets list' '
        test_cmp expect output
 '
 
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: -1
+quiet: 3
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'multiple quiet levels' '
+       test-parse-options -q -q -q >output 2>output.err &&
+       test_must_be_empty output.err &&
+       test_cmp expect output
+'
+
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 3
+quiet: 0
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'multiple verbose levels' '
+       test-parse-options -v -v -v >output 2>output.err &&
+       test_must_be_empty output.err &&
+       test_cmp expect output
+'
+
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: -1
+quiet: 0
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success '--no-quiet sets --quiet to 0' '
+       test-parse-options --no-quiet >output 2>output.err &&
+       test_must_be_empty output.err &&
+       test_cmp expect output
+'
+
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: -1
+quiet: 0
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success '--no-quiet resets multiple -q to 0' '
+       test-parse-options -q -q -q --no-quiet >output 2>output.err &&
+       test_must_be_empty output.err &&
+       test_cmp expect output
+'
+
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: 0
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success '--no-verbose sets verbose to 0' '
+       test-parse-options --no-verbose >output 2>output.err &&
+       test_must_be_empty output.err &&
+       test_cmp expect output
+'
+
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: 0
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success '--no-verbose resets multiple verbose to 0' '
+       test-parse-options -v -v -v --no-verbose >output 2>output.err &&
+       test_must_be_empty output.err &&
+       test_cmp expect output
+'
+
 test_done
index 8532a028e7c814057fe0bb7fd87addbd5dc90392..bf2deee10926f0d8ab7ba7d2645eb17a35b9a218 100755 (executable)
@@ -19,6 +19,13 @@ relative_path() {
        "test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'"
 }
 
+test_submodule_relative_url() {
+       test_expect_success "test_submodule_relative_url: $1 $2 $3 => $4" "
+               actual=\$(git submodule--helper resolve-relative-url-test '$1' '$2' '$3') &&
+               test \"\$actual\" = '$4'
+       "
+}
+
 test_git_path() {
        test_expect_success "git-path $1 $2 => $3" "
                $1 git rev-parse --git-path $2 >actual &&
@@ -298,4 +305,43 @@ test_git_path GIT_COMMON_DIR=bar config                   bar/config
 test_git_path GIT_COMMON_DIR=bar packed-refs              bar/packed-refs
 test_git_path GIT_COMMON_DIR=bar shallow                  bar/shallow
 
+# In the tests below, the distinction between $PWD and $(pwd) is important:
+# on Windows, $PWD is POSIX style (/c/foo), $(pwd) has drive letter (c:/foo).
+
+test_submodule_relative_url "../" "../foo" "../submodule" "../../submodule"
+test_submodule_relative_url "../" "../foo/bar" "../submodule" "../../foo/submodule"
+test_submodule_relative_url "../" "../foo/submodule" "../submodule" "../../foo/submodule"
+test_submodule_relative_url "../" "./foo" "../submodule" "../submodule"
+test_submodule_relative_url "../" "./foo/bar" "../submodule" "../foo/submodule"
+test_submodule_relative_url "../../../" "../foo/bar" "../sub/a/b/c" "../../../../foo/sub/a/b/c"
+test_submodule_relative_url "../" "$PWD/addtest" "../repo" "$(pwd)/repo"
+test_submodule_relative_url "../" "foo/bar" "../submodule" "../foo/submodule"
+test_submodule_relative_url "../" "foo" "../submodule" "../submodule"
+
+test_submodule_relative_url "(null)" "../foo/bar" "../sub/a/b/c" "../foo/sub/a/b/c"
+test_submodule_relative_url "(null)" "../foo/bar" "../submodule" "../foo/submodule"
+test_submodule_relative_url "(null)" "../foo/submodule" "../submodule" "../foo/submodule"
+test_submodule_relative_url "(null)" "../foo" "../submodule" "../submodule"
+test_submodule_relative_url "(null)" "./foo/bar" "../submodule" "foo/submodule"
+test_submodule_relative_url "(null)" "./foo" "../submodule" "submodule"
+test_submodule_relative_url "(null)" "//somewhere else/repo" "../subrepo" "//somewhere else/subrepo"
+test_submodule_relative_url "(null)" "$PWD/subsuper_update_r" "../subsubsuper_update_r" "$(pwd)/subsubsuper_update_r"
+test_submodule_relative_url "(null)" "$PWD/super_update_r2" "../subsuper_update_r" "$(pwd)/subsuper_update_r"
+test_submodule_relative_url "(null)" "$PWD/." "../." "$(pwd)/."
+test_submodule_relative_url "(null)" "$PWD" "./." "$(pwd)/."
+test_submodule_relative_url "(null)" "$PWD/addtest" "../repo" "$(pwd)/repo"
+test_submodule_relative_url "(null)" "$PWD" "./Ã¥ Ã¤Ã¶" "$(pwd)/Ã¥ Ã¤Ã¶"
+test_submodule_relative_url "(null)" "$PWD/." "../submodule" "$(pwd)/submodule"
+test_submodule_relative_url "(null)" "$PWD/submodule" "../submodule" "$(pwd)/submodule"
+test_submodule_relative_url "(null)" "$PWD/home2/../remote" "../bundle1" "$(pwd)/home2/../bundle1"
+test_submodule_relative_url "(null)" "$PWD/submodule_update_repo" "./." "$(pwd)/submodule_update_repo/."
+test_submodule_relative_url "(null)" "file:///tmp/repo" "../subrepo" "file:///tmp/subrepo"
+test_submodule_relative_url "(null)" "foo/bar" "../submodule" "foo/submodule"
+test_submodule_relative_url "(null)" "foo" "../submodule" "submodule"
+test_submodule_relative_url "(null)" "helper:://hostname/repo" "../subrepo" "helper:://hostname/subrepo"
+test_submodule_relative_url "(null)" "ssh://hostname/repo" "../subrepo" "ssh://hostname/subrepo"
+test_submodule_relative_url "(null)" "ssh://hostname:22/repo" "../subrepo" "ssh://hostname:22/subrepo"
+test_submodule_relative_url "(null)" "user@host:path/to/repo" "../subrepo" "user@host:path/to/subrepo"
+test_submodule_relative_url "(null)" "user@host:repo" "../subrepo" "user@host:subrepo"
+
 test_done
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 3d6f1db9da95cefa02391792500b9577d4ca9902..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 &&
diff --git a/t/t1350-config-hooks-path.sh b/t/t1350-config-hooks-path.sh
new file mode 100755 (executable)
index 0000000..5e3fb3a
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+test_description='Test the core.hooksPath configuration variable'
+
+. ./test-lib.sh
+
+test_expect_success 'set up a pre-commit hook in core.hooksPath' '
+       mkdir -p .git/custom-hooks .git/hooks &&
+       write_script .git/custom-hooks/pre-commit <<-\EOF &&
+       echo CUSTOM >>actual
+       EOF
+       write_script .git/hooks/pre-commit <<-\EOF
+       echo NORMAL >>actual
+       EOF
+'
+
+test_expect_success 'Check that various forms of specifying core.hooksPath work' '
+       test_commit no_custom_hook &&
+       git config core.hooksPath .git/custom-hooks &&
+       test_commit have_custom_hook &&
+       git config core.hooksPath .git/custom-hooks/ &&
+       test_commit have_custom_hook_trailing_slash &&
+       git config core.hooksPath "$PWD/.git/custom-hooks" &&
+       test_commit have_custom_hook_abs_path &&
+       git config core.hooksPath "$PWD/.git/custom-hooks/" &&
+       test_commit have_custom_hook_abs_path_trailing_slash &&
+       cat >expect <<-\EOF &&
+       NORMAL
+       CUSTOM
+       CUSTOM
+       CUSTOM
+       CUSTOM
+       EOF
+       test_cmp expect actual
+'
+
+test_done
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 48ee07779d64147c3eb8325a3b3f9579f8ec41d8..038e24c401451d61e9c93fb5c233d132a3d51acd 100755 (executable)
@@ -3,85 +3,88 @@
 test_description='test git rev-parse'
 . ./test-lib.sh
 
-test_rev_parse() {
-       name=$1
-       shift
-
-       test_expect_success "$name: is-bare-repository" \
-       "test '$1' = \"\$(git rev-parse --is-bare-repository)\""
-       shift
-       [ $# -eq 0 ] && return
-
-       test_expect_success "$name: is-inside-git-dir" \
-       "test '$1' = \"\$(git rev-parse --is-inside-git-dir)\""
-       shift
-       [ $# -eq 0 ] && return
+# usage: [options] label is-bare is-inside-git is-inside-work prefix git-dir
+test_rev_parse () {
+       d=
+       bare=
+       gitdir=
+       while :
+       do
+               case "$1" in
+               -C) d="$2"; shift; shift ;;
+               -b) case "$2" in
+                   [tfu]*) bare="$2"; shift; shift ;;
+                   *) error "test_rev_parse: bogus core.bare value '$2'" ;;
+                   esac ;;
+               -g) gitdir="$2"; shift; shift ;;
+               -*) error "test_rev_parse: unrecognized option '$1'" ;;
+               *) break ;;
+               esac
+       done
 
-       test_expect_success "$name: is-inside-work-tree" \
-       "test '$1' = \"\$(git rev-parse --is-inside-work-tree)\""
-       shift
-       [ $# -eq 0 ] && return
-
-       test_expect_success "$name: prefix" \
-       "test '$1' = \"\$(git rev-parse --show-prefix)\""
+       name=$1
        shift
-       [ $# -eq 0 ] && return
 
-       test_expect_success "$name: git-dir" \
-       "test '$1' = \"\$(git rev-parse --git-dir)\""
-       shift
-       [ $# -eq 0 ] && return
+       for o in --is-bare-repository \
+                --is-inside-git-dir \
+                --is-inside-work-tree \
+                --show-prefix \
+                --git-dir
+       do
+               test $# -eq 0 && break
+               expect="$1"
+               test_expect_success "$name: $o" '
+                       if test -n "$gitdir"
+                       then
+                               test_when_finished "unset GIT_DIR" &&
+                               GIT_DIR="$gitdir" &&
+                               export GIT_DIR
+                       fi &&
+
+                       case "$bare" in
+                       t*) test_config ${d:+-C} ${d:+"$d"} core.bare true ;;
+                       f*) test_config ${d:+-C} ${d:+"$d"} core.bare false ;;
+                       u*) test_unconfig ${d:+-C} ${d:+"$d"} core.bare ;;
+                       esac &&
+
+                       echo "$expect" >expect &&
+                       git ${d:+-C} ${d:+"$d"} rev-parse $o >actual &&
+                       test_cmp expect actual
+               '
+               shift
+       done
 }
 
-# label is-bare is-inside-git is-inside-work prefix git-dir
-
 ROOT=$(pwd)
 
+test_expect_success 'setup' '
+       mkdir -p sub/dir work &&
+       cp -R .git repo.git
+'
+
 test_rev_parse toplevel false false true '' .git
 
-cd .git || exit 1
-test_rev_parse .git/ false true false '' .
-cd objects || exit 1
-test_rev_parse .git/objects/ false true false '' "$ROOT/.git"
-cd ../.. || exit 1
+test_rev_parse -C .git .git/ false true false '' .
+test_rev_parse -C .git/objects .git/objects/ false true false '' "$ROOT/.git"
 
-mkdir -p sub/dir || exit 1
-cd sub/dir || exit 1
-test_rev_parse subdirectory false false true sub/dir/ "$ROOT/.git"
-cd ../.. || exit 1
+test_rev_parse -C sub/dir subdirectory false false true sub/dir/ "$ROOT/.git"
 
-git config core.bare true
-test_rev_parse 'core.bare = true' true false false
+test_rev_parse -b t 'core.bare = true' true false false
 
-git config --unset core.bare
-test_rev_parse 'core.bare undefined' false false true
+test_rev_parse -b u 'core.bare undefined' false false true
 
-mkdir work || exit 1
-cd work || exit 1
-GIT_DIR=../.git
-GIT_CONFIG="$(pwd)"/../.git/config
-export GIT_DIR GIT_CONFIG
 
-git config core.bare false
-test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true ''
+test_rev_parse -C work -g ../.git -b f 'GIT_DIR=../.git, core.bare = false' false false true ''
 
-git config core.bare true
-test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false false ''
+test_rev_parse -C work -g ../.git -b t 'GIT_DIR=../.git, core.bare = true' true false false ''
 
-git config --unset core.bare
-test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true ''
+test_rev_parse -C work -g ../.git -b u 'GIT_DIR=../.git, core.bare undefined' false false true ''
 
-mv ../.git ../repo.git || exit 1
-GIT_DIR=../repo.git
-GIT_CONFIG="$(pwd)"/../repo.git/config
 
-git config core.bare false
-test_rev_parse 'GIT_DIR=../repo.git, core.bare = false' false false true ''
+test_rev_parse -C work -g ../repo.git -b f 'GIT_DIR=../repo.git, core.bare = false' false false true ''
 
-git config core.bare true
-test_rev_parse 'GIT_DIR=../repo.git, core.bare = true' true false false ''
+test_rev_parse -C work -g ../repo.git -b t 'GIT_DIR=../repo.git, core.bare = true' true false false ''
 
-git config --unset core.bare
-test_rev_parse 'GIT_DIR=../repo.git, core.bare undefined' false false true ''
+test_rev_parse -C work -g ../repo.git -b u 'GIT_DIR=../repo.git, core.bare undefined' false false true ''
 
 test_done
index cbfa41ec61b9f893b4df441e0289a5313ccaf413..3a22fc55fc324fbfbfdcdf39fb71e829c89729b5 100755 (executable)
@@ -4,6 +4,8 @@ test_description='test git worktree add'
 
 . ./test-lib.sh
 
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
 test_expect_success 'setup' '
        test_commit init
 '
@@ -213,4 +215,73 @@ test_expect_success 'local clone from linked checkout' '
        ( cd here-clone && git fsck )
 '
 
+test_expect_success '"add" worktree with --no-checkout' '
+       git worktree add --no-checkout -b swamp swamp &&
+       ! test -e swamp/init.t &&
+       git -C swamp reset --hard &&
+       test_cmp init.t swamp/init.t
+'
+
+test_expect_success '"add" worktree with --checkout' '
+       git worktree add --checkout -b swmap2 swamp2 &&
+       test_cmp init.t swamp2/init.t
+'
+
+test_expect_success 'put a worktree under rebase' '
+       git worktree add under-rebase &&
+       (
+               cd under-rebase &&
+               set_fake_editor &&
+               FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+               git worktree list | grep "under-rebase.*detached HEAD"
+       )
+'
+
+test_expect_success 'add a worktree, checking out a rebased branch' '
+       test_must_fail git worktree add new-rebase under-rebase &&
+       ! test -d new-rebase
+'
+
+test_expect_success 'checking out a rebased branch from another worktree' '
+       git worktree add new-place &&
+       test_must_fail git -C new-place checkout under-rebase
+'
+
+test_expect_success 'not allow to delete a branch under rebase' '
+       (
+               cd under-rebase &&
+               test_must_fail git branch -D under-rebase
+       )
+'
+
+test_expect_success 'rename a branch under rebase not allowed' '
+       test_must_fail git branch -M under-rebase rebase-with-new-name
+'
+
+test_expect_success 'check out from current worktree branch ok' '
+       (
+               cd under-rebase &&
+               git checkout under-rebase &&
+               git checkout - &&
+               git rebase --abort
+       )
+'
+
+test_expect_success 'checkout a branch under bisect' '
+       git worktree add under-bisect &&
+       (
+               cd under-bisect &&
+               git bisect start &&
+               git bisect bad &&
+               git bisect good HEAD~2 &&
+               git worktree list | grep "under-bisect.*detached HEAD" &&
+               test_must_fail git worktree add new-bisect under-bisect &&
+               ! test -d new-bisect
+       )
+'
+
+test_expect_success 'rename a branch under bisect not allowed' '
+       test_must_fail git branch -M under-bisect bisect-with-new-name
+'
+
 test_done
index 9965bc5c92445160895ae3ea426c5caf1b3458b3..cccd7d923aae118b3f80aa52763648a2b4b189f2 100755 (executable)
@@ -8,7 +8,8 @@ test_cd_to_toplevel () {
        test_expect_success $3 "$2" '
                (
                        cd '"'$1'"' &&
-                       . "$(git --exec-path)"/git-sh-setup &&
+                       PATH="$(git --exec-path):$PATH" &&
+                       . git-sh-setup &&
                        cd_to_toplevel &&
                        [ "$(pwd -P)" = "$TOPLEVEL" ]
                )
index 46aadc410bc470d9279ed1fd3ee72583d0963d30..d314599428129d5759e3df5e991e5a1bb90960db 100755 (executable)
@@ -19,6 +19,8 @@ test_expect_success setup '
        test_commit three &&
        git checkout right &&
        test_commit four &&
+       git checkout --orphan newroot &&
+       test_commit five &&
        git checkout master
 '
 
@@ -133,4 +135,18 @@ test_expect_success 'merge FETCH_HEAD octopus non-fast-forward' '
        test_cmp expect actual
 '
 
+# two-project merge
+test_expect_success 'refuse two-project merge by default' '
+       t3033_reset &&
+       git reset --hard four &&
+       test_must_fail git merge five
+'
+
+test_expect_success 'two-project merge with --allow-unrelated-histories' '
+       t3033_reset &&
+       git reset --hard four &&
+       git merge --allow-unrelated-histories five &&
+       git diff --exit-code five
+'
+
 test_done
index 8f64505e4f94432de6cc287f4ab2f3129703536a..488945e0071b1ba4ae988848b5cb42df72737d2b 100755 (executable)
@@ -85,6 +85,15 @@ test_expect_success 'rebase -Xtheirs' '
        ! grep 11 original
 '
 
+test_expect_success 'rebase -Xtheirs from orphan' '
+       git checkout --orphan orphan-conflicting master~2 &&
+       echo "AB $T" >> original &&
+       git commit -morphan-conflicting original &&
+       git rebase -Xtheirs master &&
+       grep AB original &&
+       ! grep 11 original
+'
+
 test_expect_success 'merge and rebase should match' '
        git diff-tree -r test-rebase test-merge >difference &&
        if test -s difference
index d6d65a3a9435a456f8efad15b8c7c63db00a8e5f..66348f11d11598e2d7e550d5216c5b227e85f1c7 100755 (executable)
@@ -62,7 +62,7 @@ test_expect_success 'setup' '
 
 # "exec" commands are ran with the user shell by default, but this may
 # be non-POSIX. For example, if SHELL=zsh then ">file" doesn't work
-# to create a file. Unseting SHELL avoids such non-portable behavior
+# to create a file. Unsetting SHELL avoids such non-portable behavior
 # in tests. It must be exported for it to take effect where needed.
 SHELL=
 export SHELL
@@ -770,7 +770,6 @@ test_expect_success 'rebase-i history with funny messages' '
        test_cmp expect actual
 '
 
-
 test_expect_success 'prepare for rebase -i --exec' '
        git checkout master &&
        git checkout -b execute &&
@@ -779,7 +778,6 @@ test_expect_success 'prepare for rebase -i --exec' '
        test_commit three_exec main.txt three_exec
 '
 
-
 test_expect_success 'running "git rebase -i --exec git show HEAD"' '
        set_fake_editor &&
        git rebase -i --exec "git show HEAD" HEAD~2 >actual &&
@@ -792,7 +790,6 @@ test_expect_success 'running "git rebase -i --exec git show HEAD"' '
        test_cmp expected actual
 '
 
-
 test_expect_success 'running "git rebase --exec git show HEAD -i"' '
        git reset --hard execute &&
        set_fake_editor &&
@@ -806,7 +803,6 @@ test_expect_success 'running "git rebase --exec git show HEAD -i"' '
        test_cmp expected actual
 '
 
-
 test_expect_success 'running "git rebase -ix git show HEAD"' '
        git reset --hard execute &&
        set_fake_editor &&
@@ -834,7 +830,6 @@ test_expect_success 'rebase -ix with several <CMD>' '
        test_cmp expected actual
 '
 
-
 test_expect_success 'rebase -ix with several instances of --exec' '
        git reset --hard execute &&
        set_fake_editor &&
@@ -849,7 +844,6 @@ test_expect_success 'rebase -ix with several instances of --exec' '
        test_cmp expected actual
 '
 
-
 test_expect_success 'rebase -ix with --autosquash' '
        git reset --hard execute &&
        git checkout -b autosquash &&
@@ -875,16 +869,15 @@ test_expect_success 'rebase -ix with --autosquash' '
        test_cmp expected actual
 '
 
-
-test_expect_success 'rebase --exec without -i shows error message' '
+test_expect_success 'rebase --exec works without -i ' '
        git reset --hard execute &&
-       set_fake_editor &&
-       test_must_fail git rebase --exec "git show HEAD" HEAD~2 2>actual &&
-       echo "The --exec option must be used with the --interactive option" >expected &&
-       test_i18ncmp expected actual
+       rm -rf exec_output &&
+       EDITOR="echo >invoked_editor" git rebase --exec "echo a line >>exec_output"  HEAD~2 2>actual &&
+       test_i18ngrep  "Successfully rebased and updated" actual &&
+       test_line_count = 2 exec_output &&
+       test_path_is_missing invoked_editor
 '
 
-
 test_expect_success 'rebase -i --exec without <CMD>' '
        git reset --hard execute &&
        set_fake_editor &&
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 217dd79b2e4b5c02ee27f3b913c37c489ec585f2..49f548cdb93d53d88723adcd1f414a4db4c14a73 100755 (executable)
@@ -73,17 +73,17 @@ do_tests () {
                run git format-patch --stdout --ignore-if-in-upstream master
        "
 
-       test_expect_success $pr 'detect upstream patch' "
+       test_expect_success $pr 'detect upstream patch' '
                git checkout -q master &&
                scramble file &&
                git add file &&
-               git commit -q -m 'change big file again' &&
+               git commit -q -m "change big file again" &&
                git checkout -q other^{} &&
                git rebase master &&
-               test_must_fail test -n \"\$(git rev-list master...HEAD~)\"
-       "
+               test_must_fail test -n "$(git rev-list master...HEAD~)"
+       '
 
-       test_expect_success $pr 'do not drop patch' "
+       test_expect_success $pr 'do not drop patch' '
                git branch -f squashed master &&
                git checkout -q -f squashed &&
                git reset -q --soft HEAD~2 &&
@@ -91,7 +91,7 @@ do_tests () {
                git checkout -q other^{} &&
                test_must_fail git rebase squashed &&
                rm -rf .git/rebase-apply
-       "
+       '
 }
 
 do_tests 500
index 9c55cba198e4ed4cbc01ffce79abeb39504bf544..68fe2003ef5f74073cafa4741bee31ade85cf5c0 100755 (executable)
@@ -253,7 +253,7 @@ test_run_rebase () {
        "
 }
 test_run_rebase success ''
-test_run_rebase failure -m
+test_run_rebase success -m
 test_run_rebase success -i
 test_run_rebase success -p
 
@@ -268,7 +268,7 @@ test_run_rebase () {
        "
 }
 test_run_rebase success ''
-test_run_rebase failure -m
+test_run_rebase success -m
 test_run_rebase success -i
 test_run_rebase failure -p
 
index 831935665e012c04bbe4c57feee92c0310df5c04..26dd5b7f78a7f7e65ddda24c4686d2ae26fc30de 100755 (executable)
@@ -49,12 +49,54 @@ test_expect_success "setup" '
 test_expect_success "setup case mac" '
        git checkout -b mac_os
 '
+# This will test nfd2nfc in git diff
+test_expect_success "git diff f.Adiar" '
+       touch f.$Adiarnfc &&
+       git add f.$Adiarnfc &&
+       echo f.Adiarnfc >f.$Adiarnfc &&
+       git diff f.$Adiarnfd >expect &&
+       git diff f.$Adiarnfc >actual &&
+       test_cmp expect actual &&
+       git reset HEAD f.Adiarnfc &&
+       rm f.$Adiarnfc expect actual
+'
+# This will test nfd2nfc in git diff-files
+test_expect_success "git diff-files f.Adiar" '
+       touch f.$Adiarnfc &&
+       git add f.$Adiarnfc &&
+       echo f.Adiarnfc >f.$Adiarnfc &&
+       git diff-files f.$Adiarnfd >expect &&
+       git diff-files f.$Adiarnfc >actual &&
+       test_cmp expect actual &&
+       git reset HEAD f.Adiarnfc &&
+       rm f.$Adiarnfc expect actual
+'
+# This will test nfd2nfc in git diff-index
+test_expect_success "git diff-index f.Adiar" '
+       touch f.$Adiarnfc &&
+       git add f.$Adiarnfc &&
+       echo f.Adiarnfc >f.$Adiarnfc &&
+       git diff-index HEAD f.$Adiarnfd >expect &&
+       git diff-index HEAD f.$Adiarnfc >actual &&
+       test_cmp expect actual &&
+       git reset HEAD f.Adiarnfc &&
+       rm f.$Adiarnfc expect actual
+'
 # This will test nfd2nfc in readdir()
 test_expect_success "add file Adiarnfc" '
        echo f.Adiarnfc >f.$Adiarnfc &&
        git add f.$Adiarnfc &&
        git commit -m "add f.$Adiarnfc"
 '
+# This will test nfd2nfc in git diff-tree
+test_expect_success "git diff-tree f.Adiar" '
+       echo f.Adiarnfc >>f.$Adiarnfc &&
+       git diff-tree HEAD f.$Adiarnfd >expect &&
+       git diff-tree HEAD f.$Adiarnfc >actual &&
+       test_cmp expect actual &&
+       git checkout f.$Adiarnfc &&
+       rm expect actual
+'
 # This will test nfd2nfc in git stage()
 test_expect_success "stage file d.Adiarnfd/f.Adiarnfd" '
        mkdir d.$Adiarnfd &&
index ed90c6c6f984979bfc250d277486550cb5129e30..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 &&
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 9c52efc9d4c2b02cfe81abcf6d721277d5bdfe5c..805dc9012d5f765eb40a67e58c1572b1fe316bd6 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 &&
@@ -1460,4 +1460,109 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '
        test_path_is_dir patchset
 '
 
+test_expect_success 'format-patch --base' '
+       git checkout side &&
+       git format-patch --stdout --base=HEAD~3 -1 >patch &&
+       grep "^base-commit:" patch >actual &&
+       grep "^prerequisite-patch-id:" patch >>actual &&
+       echo "base-commit: $(git rev-parse HEAD~3)" >expected &&
+       echo "prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --stable | awk "{print \$1}")" >>expected &&
+       echo "prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --stable | awk "{print \$1}")" >>expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'format-patch --base errors out when base commit is in revision list' '
+       test_must_fail git format-patch --base=HEAD -2 &&
+       test_must_fail git format-patch --base=HEAD~1 -2 &&
+       git format-patch --stdout --base=HEAD~2 -2 >patch &&
+       grep "^base-commit:" patch >actual &&
+       echo "base-commit: $(git rev-parse HEAD~2)" >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'format-patch --base errors out when base commit is not ancestor of revision list' '
+       # For history as below:
+       #
+       #    ---Q---P---Z---Y---*---X
+       #        \             /
+       #         ------------W
+       #
+       # If "format-patch Z..X" is given, P and Z can not be specified as the base commit
+       git checkout -b topic1 master &&
+       git rev-parse HEAD >commit-id-base &&
+       test_commit P &&
+       git rev-parse HEAD >commit-id-P &&
+       test_commit Z &&
+       git rev-parse HEAD >commit-id-Z &&
+       test_commit Y &&
+       git checkout -b topic2 master &&
+       test_commit W &&
+       git merge topic1 &&
+       test_commit X &&
+       test_must_fail git format-patch --base=$(cat commit-id-P) -3 &&
+       test_must_fail git format-patch --base=$(cat commit-id-Z) -3 &&
+       git format-patch --stdout --base=$(cat commit-id-base) -3 >patch &&
+       grep "^base-commit:" patch >actual &&
+       echo "base-commit: $(cat commit-id-base)" >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'format-patch --base=auto' '
+       git checkout -b upstream master &&
+       git checkout -b local upstream &&
+       git branch --set-upstream-to=upstream &&
+       test_commit N1 &&
+       test_commit N2 &&
+       git format-patch --stdout --base=auto -2 >patch &&
+       grep "^base-commit:" patch >actual &&
+       echo "base-commit: $(git rev-parse upstream)" >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'format-patch errors out when history involves criss-cross' '
+       # setup criss-cross history
+       #
+       #   B---M1---D
+       #  / \ /
+       # A   X
+       #  \ / \
+       #   C---M2---E
+       #
+       git checkout master &&
+       test_commit A &&
+       git checkout -b xb master &&
+       test_commit B &&
+       git checkout -b xc master &&
+       test_commit C &&
+       git checkout -b xbc xb -- &&
+       git merge xc &&
+       git checkout -b xcb xc -- &&
+       git branch --set-upstream-to=xbc &&
+       git merge xb &&
+       git checkout xbc &&
+       test_commit D &&
+       git checkout xcb &&
+       test_commit E &&
+       test_must_fail  git format-patch --base=auto -1
+'
+
+test_expect_success 'format-patch format.useAutoBaseoption' '
+       test_when_finished "git config --unset format.useAutoBase" &&
+       git checkout local &&
+       git config format.useAutoBase true &&
+       git format-patch --stdout -1 >patch &&
+       grep "^base-commit:" patch >actual &&
+       echo "base-commit: $(git rev-parse upstream)" >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'format-patch --base overrides format.useAutoBase' '
+       test_when_finished "git config --unset format.useAutoBase" &&
+       git config format.useAutoBase true &&
+       git format-patch --stdout --base=HEAD~1 -1 >patch &&
+       grep "^base-commit:" patch >actual &&
+       echo "base-commit: $(git rev-parse HEAD~1)" >expected &&
+       test_cmp expected actual
+'
+
 test_done
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
 '
index baa9d3c82e8a54dc7501d90bc9da0527f2a72b2e..84a809690e786c1278d0885b4b441c6ce3bbe4f0 100755 (executable)
@@ -30,11 +30,11 @@ test_expect_success 'patch-id output is well-formed' '
 
 #calculate patch id. Make sure output is not empty.
 calc_patch_id () {
-       name="$1"
+       patch_name="$1"
        shift
        git patch-id "$@" |
-       sed "s/ .*//" >patch-id_"$name" &&
-       test_line_count -gt 0 patch-id_"$name"
+       sed "s/ .*//" >patch-id_"$patch_name" &&
+       test_line_count -gt 0 patch-id_"$patch_name"
 }
 
 get_top_diff () {
diff --git a/t/t4213-log-tabexpand.sh b/t/t4213-log-tabexpand.sh
new file mode 100755 (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 dc305d67a231a27ed73d04ad6b9b6fe17ded764e..82d913a6a87c45dd8141e3c7384df9943f9b83cb 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' '
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 18372caa151a30ac0ec1f247e793164fe667358a..ded8f98dbed9764744799f7b4bed0c51fc57a158 100755 (executable)
@@ -144,4 +144,25 @@ test_expect_success 'git pull --all --dry-run' '
        )
 '
 
+test_expect_success 'git pull --allow-unrelated-histories' '
+       test_when_finished "rm -fr src dst" &&
+       git init src &&
+       (
+               cd src &&
+               test_commit one &&
+               test_commit two
+       ) &&
+       git clone src dst &&
+       (
+               cd src &&
+               git checkout --orphan side HEAD^ &&
+               test_commit three
+       ) &&
+       (
+               cd dst &&
+               test_must_fail git pull ../src side &&
+               git pull --allow-unrelated-histories ../src side
+       )
+'
+
 test_done
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 64146352ae20e9a5abdcda77be514d0f3264c3b8..3484b6f0f3cf04a9cf831a5d91c31486efebcc38 100755 (executable)
@@ -91,6 +91,55 @@ test_expect_success 'configured username does not override URL' '
        expect_askpass pass user@host
 '
 
+test_expect_success 'set up repo with http submodules' '
+       git init super &&
+       set_askpass user@host pass@host &&
+       (
+               cd super &&
+               git submodule add "$HTTPD_URL/auth/dumb/repo.git" sub &&
+               git commit -m "add submodule"
+       )
+'
+
+test_expect_success 'cmdline credential config passes to submodule via clone' '
+       set_askpass wrong pass@host &&
+       test_must_fail git clone --recursive super super-clone &&
+       rm -rf super-clone &&
+
+       set_askpass wrong pass@host &&
+       git -c "credential.$HTTPD_URL.username=user@host" \
+               clone --recursive super super-clone &&
+       expect_askpass pass user@host
+'
+
+test_expect_success 'cmdline credential config passes submodule via fetch' '
+       set_askpass wrong pass@host &&
+       test_must_fail git -C super-clone fetch --recurse-submodules &&
+
+       set_askpass wrong pass@host &&
+       git -C super-clone \
+           -c "credential.$HTTPD_URL.username=user@host" \
+           fetch --recurse-submodules &&
+       expect_askpass pass user@host
+'
+
+test_expect_success 'cmdline credential config passes submodule update' '
+       # advance the submodule HEAD so that a fetch is required
+       git commit --allow-empty -m foo &&
+       git push "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/repo.git" HEAD &&
+       sha1=$(git rev-parse HEAD) &&
+       git -C super-clone update-index --cacheinfo 160000,$sha1,sub &&
+
+       set_askpass wrong pass@host &&
+       test_must_fail git -C super-clone submodule update &&
+
+       set_askpass wrong pass@host &&
+       git -C super-clone \
+           -c "credential.$HTTPD_URL.username=user@host" \
+           submodule update &&
+       expect_askpass pass user@host
+'
+
 test_expect_success 'fetch changes via http' '
        echo content >>file &&
        git commit -a -m two &&
index 58207d88250f57d448f4e83baf2add3192cb9455..2f375eb94d528baefad038ff5b07baf0fe08d1fc 100755 (executable)
@@ -282,5 +282,22 @@ test_expect_success EXPENSIVE 'http can handle enormous ref negotiation' '
        test_line_count = 100000 tags
 '
 
+test_expect_success 'custom http headers' '
+       test_must_fail git -c http.extraheader="x-magic-two: cadabra" \
+               fetch "$HTTPD_URL/smart_headers/repo.git" &&
+       git -c http.extraheader="x-magic-one: abra" \
+           -c http.extraheader="x-magic-two: cadabra" \
+           fetch "$HTTPD_URL/smart_headers/repo.git" &&
+       git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub &&
+       git config -f .gitmodules submodule.sub.path sub &&
+       git config -f .gitmodules submodule.sub.url \
+               "$HTTPD_URL/smart_headers/repo.git" &&
+       git submodule init sub &&
+       test_must_fail git submodule update sub &&
+       git -c http.extraheader="x-magic-one: abra" \
+           -c http.extraheader="x-magic-two: cadabra" \
+               submodule update sub
+'
+
 stop_httpd
 test_done
index 1789d0be3d2c7b1841d5203f7bc11e05651ad886..a43339420019718b76963d5ced92ebb981b0f0ed 100755 (executable)
@@ -308,7 +308,7 @@ test_expect_success 'clone checking out a tag' '
 
 setup_ssh_wrapper () {
        test_expect_success 'setup ssh wrapper' '
-               cp "$GIT_BUILD_DIR/test-fake-ssh$X" \
+               cp "$GIT_BUILD_DIR/t/helper/test-fake-ssh$X" \
                        "$TRASH_DIRECTORY/ssh-wrapper$X" &&
                GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper$X" &&
                export GIT_SSH &&
diff --git a/t/t5614-clone-submodules.sh b/t/t5614-clone-submodules.sh
new file mode 100755 (executable)
index 0000000..62044c5
--- /dev/null
@@ -0,0 +1,85 @@
+#!/bin/sh
+
+test_description='Test shallow cloning of repos with submodules'
+
+. ./test-lib.sh
+
+pwd=$(pwd)
+
+test_expect_success 'setup' '
+       git checkout -b master &&
+       test_commit commit1 &&
+       test_commit commit2 &&
+       mkdir sub &&
+       (
+               cd sub &&
+               git init &&
+               test_commit subcommit1 &&
+               test_commit subcommit2 &&
+               test_commit subcommit3
+       ) &&
+       git submodule add "file://$pwd/sub" sub &&
+       git commit -m "add submodule"
+'
+
+test_expect_success 'nonshallow clone implies nonshallow submodule' '
+       test_when_finished "rm -rf super_clone" &&
+       git clone --recurse-submodules "file://$pwd/." super_clone &&
+       (
+               cd super_clone &&
+               git log --oneline >lines &&
+               test_line_count = 3 lines
+       ) &&
+       (
+               cd super_clone/sub &&
+               git log --oneline >lines &&
+               test_line_count = 3 lines
+       )
+'
+
+test_expect_success 'shallow clone implies shallow submodule' '
+       test_when_finished "rm -rf super_clone" &&
+       git clone --recurse-submodules --depth 2 "file://$pwd/." super_clone &&
+       (
+               cd super_clone &&
+               git log --oneline >lines &&
+               test_line_count = 2 lines
+       ) &&
+       (
+               cd super_clone/sub &&
+               git log --oneline >lines &&
+               test_line_count = 1 lines
+       )
+'
+
+test_expect_success 'shallow clone with non shallow submodule' '
+       test_when_finished "rm -rf super_clone" &&
+       git clone --recurse-submodules --depth 2 --no-shallow-submodules "file://$pwd/." super_clone &&
+       (
+               cd super_clone &&
+               git log --oneline >lines &&
+               test_line_count = 2 lines
+       ) &&
+       (
+               cd super_clone/sub &&
+               git log --oneline >lines &&
+               test_line_count = 3 lines
+       )
+'
+
+test_expect_success 'non shallow clone with shallow submodule' '
+       test_when_finished "rm -rf super_clone" &&
+       git clone --recurse-submodules --no-local --shallow-submodules "file://$pwd/." super_clone &&
+       (
+               cd super_clone &&
+               git log --oneline >lines &&
+               test_line_count = 3 lines
+       ) &&
+       (
+               cd super_clone/sub &&
+               git log --oneline >lines &&
+               test_line_count = 1 lines
+       )
+'
+
+test_done
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 755d30ce2a5d1c5e34751d8906ad41b02d553b03..3f59e58dfb5171d2e5ef53399c2f8d24fff1e94e 100755 (executable)
@@ -76,7 +76,7 @@ test_expect_success "result contains a conflict" "test_cmp expect a1"
 
 git ls-files --stage > out
 cat > expect << EOF
-100644 439cc46de773d8a83c77799b7cc9191c128bfcff 1      a1
+100644 ec3fe2a791706733f2d8fa7ad45d9a9672031f5e 1      a1
 100644 cf84443e49e1b366fac938711ddf4be2d4d1d9e9 2      a1
 100644 fd7923529855d0b274795ae3349c5e0438333979 3      a1
 EOF
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 &&
index 9d6621c05604e22c258ea21ef0cd549ef4d0d278..18aa88b5c096c14f2f8a3a8d8c6488127fddf055 100755 (executable)
@@ -212,7 +212,8 @@ test_expect_success 'git detects differently handled merges conflict' '
                -L "" \
                -L "Temporary merge branch 1" \
                merged empty merge-me &&
-       test $(git rev-parse :1:new_a) = $(git hash-object merged)
+       sed -e "s/^\([<=>]\)/\1\1\1/" merged >merged-internal &&
+       test $(git rev-parse :1:new_a) = $(git hash-object merged-internal)
 '
 
 #
@@ -298,89 +299,6 @@ test_expect_success 'git detects conflict merging criss-cross+modify/delete, rev
        test $(git rev-parse :3:file) = $(git rev-parse B:file)
 '
 
-#
-# criss-cross + modify/modify with very contrived file contents:
-#
-#      B   D
-#      o---o
-#     / \ / \
-#  A o   X   ? F
-#     \ / \ /
-#      o---o
-#      C   E
-#
-#   Commit A: file with contents 'A\n'
-#   Commit B: file with contents 'B\n'
-#   Commit C: file with contents 'C\n'
-#   Commit D: file with contents 'D\n'
-#   Commit E: file with contents:
-#      <<<<<<< Temporary merge branch 1
-#      C
-#      =======
-#      B
-#      >>>>>>> Temporary merge branch 2
-#
-# Now, when we merge commits D & E, does git detect the conflict?
-
-test_expect_success 'setup differently handled merges of content conflict' '
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       echo A >file &&
-       git add file &&
-       test_tick &&
-       git commit -m A &&
-
-       git branch B &&
-       git checkout -b C &&
-       echo C >file &&
-       git add file &&
-       test_tick &&
-       git commit -m C &&
-
-       git checkout B &&
-       echo B >file &&
-       git add file &&
-       test_tick &&
-       git commit -m B &&
-
-       git checkout B^0 &&
-       test_must_fail git merge C &&
-       echo D >file &&
-       git add file &&
-       test_tick &&
-       git commit -m D &&
-       git tag D &&
-
-       git checkout C^0 &&
-       test_must_fail git merge B &&
-       cat <<EOF >file &&
-<<<<<<< Temporary merge branch 1
-C
-=======
-B
->>>>>>> Temporary merge branch 2
-EOF
-       git add file &&
-       test_tick &&
-       git commit -m E &&
-       git tag E
-'
-
-test_expect_failure 'git detects conflict w/ criss-cross+contrived resolution' '
-       git checkout D^0 &&
-
-       test_must_fail git merge -s recursive E^0 &&
-
-       test 3 -eq $(git ls-files -s | wc -l) &&
-       test 3 -eq $(git ls-files -u | wc -l) &&
-       test 0 -eq $(git ls-files -o | wc -l) &&
-
-       test $(git rev-parse :2:file) = $(git rev-parse D:file) &&
-       test $(git rev-parse :3:file) = $(git rev-parse E:file)
-'
-
 #
 # criss-cross + d/f conflict via add/add:
 #   Commit A: Neither file 'a' nor directory 'a/' exists.
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..0102348
--- /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' '
+       test_seq 1 10 >a &&
+       git add a &&
+       test_tick && git commit -m A &&
+
+       git branch A &&
+       git branch B &&
+       git branch C &&
+       git branch D &&
+       git branch E &&
+
+       git checkout B &&
+       echo b >b &&
+       echo 11 >>a &&
+       git add a b &&
+       test_tick && git commit -m B &&
+
+       git checkout C &&
+       echo c >c &&
+       git add c &&
+       test_tick && git commit -m C &&
+
+       git checkout D &&
+       test_seq 2 10 >a &&
+       echo d >d &&
+       git add a d &&
+       test_tick && git commit -m D &&
+
+       git checkout E &&
+       mkdir subdir &&
+       git mv a subdir/a &&
+       echo e >subdir/e &&
+       git add subdir &&
+       test_tick && git commit -m E
+'
+
+test_expect_success 'ff update' '
+       git reset --hard &&
+       git checkout A^0 &&
+
+       touch random_file && git add random_file &&
+
+       git merge E^0 &&
+
+       test_must_fail git rev-parse HEAD:random_file &&
+       test "$(git diff --name-only --cached E)" = "random_file"
+'
+
+test_expect_success 'ff update, important file modified' '
+       git reset --hard &&
+       git checkout A^0 &&
+
+       mkdir subdir &&
+       touch subdir/e &&
+       git add subdir/e &&
+
+       test_must_fail git merge E^0
+'
+
+test_expect_success 'resolve, trivial' '
+       git reset --hard &&
+       git checkout B^0 &&
+
+       touch random_file && git add random_file &&
+
+       test_must_fail git merge -s resolve C^0
+'
+
+test_expect_success 'resolve, non-trivial' '
+       git reset --hard &&
+       git checkout B^0 &&
+
+       touch random_file && git add random_file &&
+
+       test_must_fail git merge -s resolve D^0
+'
+
+test_expect_success 'recursive' '
+       git reset --hard &&
+       git checkout B^0 &&
+
+       touch random_file && git add random_file &&
+
+       test_must_fail git merge -s recursive C^0
+'
+
+test_expect_success 'octopus, unrelated file touched' '
+       git reset --hard &&
+       git checkout B^0 &&
+
+       touch random_file && git add random_file &&
+
+       test_must_fail git merge C^0 D^0
+'
+
+test_expect_success 'octopus, related file removed' '
+       git reset --hard &&
+       git checkout B^0 &&
+
+       git rm b &&
+
+       test_must_fail git merge C^0 D^0
+'
+
+test_expect_success 'octopus, related file modified' '
+       git reset --hard &&
+       git checkout B^0 &&
+
+       echo 12 >>a && git add a &&
+
+       test_must_fail git merge C^0 D^0
+'
+
+test_expect_success 'ours' '
+       git reset --hard &&
+       git checkout B^0 &&
+
+       touch random_file && git add random_file &&
+
+       test_must_fail git merge -s ours C^0
+'
+
+test_expect_success 'subtree' '
+       git reset --hard &&
+       git checkout B^0 &&
+
+       touch random_file && git add random_file &&
+
+       test_must_fail git merge -s subtree E^0
+'
+
+test_done
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..d0ab09f4bd804730eb049ea982e5724653583f06 100755 (executable)
@@ -5,20 +5,27 @@ test_description='test for-each-refs usage of ref-filter APIs'
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
 
-if ! test_have_prereq GPG
-then
-       skip_all="skipping for-each-ref tests, GPG not available"
-       test_done
-fi
-
 test_expect_success 'setup some history and refs' '
        test_commit one &&
        test_commit two &&
        test_commit three &&
        git checkout -b side &&
        test_commit four &&
-       git tag -s -m "A signed tag message" signed-tag &&
-       git tag -s -m "Annonated doubly" double-tag signed-tag &&
+       git tag -m "An annotated tag" annotated-tag &&
+       git tag -m "Annonated doubly" doubly-annotated-tag annotated-tag &&
+
+       # Note that these "signed" tags might not actually be signed.
+       # Tests which care about the distinction should be marked
+       # with the GPG prereq.
+       if test_have_prereq GPG
+       then
+               sign=-s
+       else
+               sign=
+       fi &&
+       git tag $sign -m "A signed tag" signed-tag &&
+       git tag $sign -m "Signed doubly" doubly-signed-tag signed-tag &&
+
        git checkout master &&
        git update-ref refs/odd/spot master
 '
@@ -36,6 +43,7 @@ test_expect_success 'filtering with --points-at' '
 test_expect_success 'check signed tags with --points-at' '
        sed -e "s/Z$//" >expect <<-\EOF &&
        refs/heads/side Z
+       refs/tags/annotated-tag four
        refs/tags/four Z
        refs/tags/signed-tag four
        EOF
@@ -58,7 +66,9 @@ test_expect_success 'filtering with --merged' '
 test_expect_success 'filtering with --no-merged' '
        cat >expect <<-\EOF &&
        refs/heads/side
-       refs/tags/double-tag
+       refs/tags/annotated-tag
+       refs/tags/doubly-annotated-tag
+       refs/tags/doubly-signed-tag
        refs/tags/four
        refs/tags/signed-tag
        EOF
@@ -71,7 +81,9 @@ test_expect_success 'filtering with --contains' '
        refs/heads/master
        refs/heads/side
        refs/odd/spot
-       refs/tags/double-tag
+       refs/tags/annotated-tag
+       refs/tags/doubly-annotated-tag
+       refs/tags/doubly-signed-tag
        refs/tags/four
        refs/tags/signed-tag
        refs/tags/three
@@ -90,7 +102,9 @@ test_expect_success 'left alignment is default' '
        refname is refs/heads/master  |refs/heads/master
        refname is refs/heads/side    |refs/heads/side
        refname is refs/odd/spot      |refs/odd/spot
-       refname is refs/tags/double-tag|refs/tags/double-tag
+       refname is refs/tags/annotated-tag|refs/tags/annotated-tag
+       refname is refs/tags/doubly-annotated-tag|refs/tags/doubly-annotated-tag
+       refname is refs/tags/doubly-signed-tag|refs/tags/doubly-signed-tag
        refname is refs/tags/four     |refs/tags/four
        refname is refs/tags/one      |refs/tags/one
        refname is refs/tags/signed-tag|refs/tags/signed-tag
@@ -106,7 +120,9 @@ test_expect_success 'middle alignment' '
        | refname is refs/heads/master |refs/heads/master
        |  refname is refs/heads/side  |refs/heads/side
        |   refname is refs/odd/spot   |refs/odd/spot
-       |refname is refs/tags/double-tag|refs/tags/double-tag
+       |refname is refs/tags/annotated-tag|refs/tags/annotated-tag
+       |refname is refs/tags/doubly-annotated-tag|refs/tags/doubly-annotated-tag
+       |refname is refs/tags/doubly-signed-tag|refs/tags/doubly-signed-tag
        |  refname is refs/tags/four   |refs/tags/four
        |   refname is refs/tags/one   |refs/tags/one
        |refname is refs/tags/signed-tag|refs/tags/signed-tag
@@ -122,7 +138,9 @@ test_expect_success 'right alignment' '
        |  refname is refs/heads/master|refs/heads/master
        |    refname is refs/heads/side|refs/heads/side
        |      refname is refs/odd/spot|refs/odd/spot
-       |refname is refs/tags/double-tag|refs/tags/double-tag
+       |refname is refs/tags/annotated-tag|refs/tags/annotated-tag
+       |refname is refs/tags/doubly-annotated-tag|refs/tags/doubly-annotated-tag
+       |refname is refs/tags/doubly-signed-tag|refs/tags/doubly-signed-tag
        |     refname is refs/tags/four|refs/tags/four
        |      refname is refs/tags/one|refs/tags/one
        |refname is refs/tags/signed-tag|refs/tags/signed-tag
@@ -137,7 +155,9 @@ cat >expect <<-\EOF
 |       refname is refs/heads/master       |refs/heads/master
 |        refname is refs/heads/side        |refs/heads/side
 |         refname is refs/odd/spot         |refs/odd/spot
-|     refname is refs/tags/double-tag      |refs/tags/double-tag
+|    refname is refs/tags/annotated-tag    |refs/tags/annotated-tag
+|refname is refs/tags/doubly-annotated-tag |refs/tags/doubly-annotated-tag
+|  refname is refs/tags/doubly-signed-tag  |refs/tags/doubly-signed-tag
 |        refname is refs/tags/four         |refs/tags/four
 |         refname is refs/tags/one         |refs/tags/one
 |     refname is refs/tags/signed-tag      |refs/tags/signed-tag
@@ -182,7 +202,9 @@ test_expect_success 'alignment with format quote' "
        |'      '\''master| A U Thor'\''      '|
        |'       '\''side| A U Thor'\''       '|
        |'     '\''odd/spot| A U Thor'\''     '|
-       |'        '\''double-tag| '\''        '|
+       |'      '\''annotated-tag| '\''       '|
+       |'   '\''doubly-annotated-tag| '\''   '|
+       |'    '\''doubly-signed-tag| '\''     '|
        |'       '\''four| A U Thor'\''       '|
        |'       '\''one| A U Thor'\''        '|
        |'        '\''signed-tag| '\''        '|
@@ -198,7 +220,9 @@ test_expect_success 'nested alignment with quote formatting' "
        |'         master               '|
        |'           side               '|
        |'       odd/spot               '|
-       |'     double-tag               '|
+       |'  annotated-tag               '|
+       |'doubly-annotated-tag          '|
+       |'doubly-signed-tag             '|
        |'           four               '|
        |'            one               '|
        |'     signed-tag               '|
@@ -214,10 +238,12 @@ test_expect_success 'check `%(contents:lines=1)`' '
        master |three
        side |four
        odd/spot |three
-       double-tag |Annonated doubly
+       annotated-tag |An annotated tag
+       doubly-annotated-tag |Annonated doubly
+       doubly-signed-tag |Signed doubly
        four |four
        one |one
-       signed-tag |A signed tag message
+       signed-tag |A signed tag
        three |three
        two |two
        EOF
@@ -230,7 +256,9 @@ test_expect_success 'check `%(contents:lines=0)`' '
        master |
        side |
        odd/spot |
-       double-tag |
+       annotated-tag |
+       doubly-annotated-tag |
+       doubly-signed-tag |
        four |
        one |
        signed-tag |
@@ -246,10 +274,12 @@ test_expect_success 'check `%(contents:lines=99999)`' '
        master |three
        side |four
        odd/spot |three
-       double-tag |Annonated doubly
+       annotated-tag |An annotated tag
+       doubly-annotated-tag |Annonated doubly
+       doubly-signed-tag |Signed doubly
        four |four
        one |one
-       signed-tag |A signed tag message
+       signed-tag |A signed tag
        three |three
        two |two
        EOF
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 4608e713438f93087d12664a17654c07be38c59f..07079a41c4f6b9f89786d7eb9187655102ed9bd0 100755 (executable)
@@ -112,4 +112,17 @@ test_expect_success GPG 'verify signatures with --raw' '
        )
 '
 
+test_expect_success GPG 'verify multiple tags' '
+       tags="fourth-signed sixth-signed seventh-signed" &&
+       for i in $tags
+       do
+               git verify-tag -v --raw $i || return 1
+       done >expect.stdout 2>expect.stderr.1 &&
+       grep "^.GNUPG:." <expect.stderr.1 >expect.stderr &&
+       git verify-tag -v --raw $tags >actual.stdout 2>actual.stderr.1 &&
+       grep "^.GNUPG:." <actual.stderr.1 >actual.stderr &&
+       test_cmp expect.stdout actual.stdout &&
+       test_cmp expect.stderr actual.stderr
+'
+
 test_done
index 75db02389885d74942e0b20da55796130c0d2c62..3570f7bb8c8955a1bf519eda3278392d249e6fd1 100755 (executable)
@@ -22,6 +22,22 @@ test_expect_success 'setup - initial commit' '
        git branch initial
 '
 
+test_expect_success 'submodule init aborts on missing .gitmodules file' '
+       test_when_finished "git update-index --remove sub" &&
+       git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub &&
+       # missing the .gitmodules file here
+       test_must_fail git submodule init 2>actual &&
+       test_i18ngrep "No url found for submodule path" actual
+'
+
+test_expect_success 'submodule update aborts on missing .gitmodules file' '
+       test_when_finished "git update-index --remove sub" &&
+       git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub &&
+       # missing the .gitmodules file here
+       git submodule update sub 2>actual &&
+       test_i18ngrep "Submodule path .sub. not initialized" actual
+'
+
 test_expect_success 'configuration parsing' '
        test_when_finished "rm -f .gitmodules" &&
        cat >.gitmodules <<-\EOF &&
@@ -466,7 +482,7 @@ test_expect_success 'update --init' '
        git config --remove-section submodule.example &&
        test_must_fail git config submodule.example.url &&
 
-       git submodule update init > update.out &&
+       git submodule update init 2> update.out &&
        cat update.out &&
        test_i18ngrep "not initialized" update.out &&
        test_must_fail git rev-parse --resolve-git-dir init/.git &&
@@ -484,7 +500,7 @@ test_expect_success 'update --init from subdirectory' '
        mkdir -p sub &&
        (
                cd sub &&
-               git submodule update ../init >update.out &&
+               git submodule update ../init 2>update.out &&
                cat update.out &&
                test_i18ngrep "not initialized" update.out &&
                test_must_fail git rev-parse --resolve-git-dir ../init/.git &&
@@ -902,7 +918,7 @@ test_expect_success 'submodule deinit works on repository without submodules' '
                git init &&
                >file &&
                git add file &&
-               git commit -m "repo should not be empty"
+               git commit -m "repo should not be empty" &&
                git submodule deinit . &&
                git submodule deinit --all
        )
index e5af4b497646944d0c116f68de5b05bd6dfd7445..5f278799d5a0ceed90b9297ace552b653026e596 100755 (executable)
@@ -108,24 +108,36 @@ pwd=$(pwd)
 
 cat <<EOF >expect
 Submodule path '../super': checked out '$supersha1'
-Submodule 'merging' ($pwd/merging) registered for path '../super/merging'
-Submodule 'none' ($pwd/none) registered for path '../super/none'
-Submodule 'rebasing' ($pwd/rebasing) registered for path '../super/rebasing'
-Submodule 'submodule' ($pwd/submodule) registered for path '../super/submodule'
 Submodule path '../super/merging': checked out '$mergingsha1'
 Submodule path '../super/none': checked out '$nonesha1'
 Submodule path '../super/rebasing': checked out '$rebasingsha1'
 Submodule path '../super/submodule': checked out '$submodulesha1'
 EOF
 
+cat <<EOF >expect2
+Submodule 'merging' ($pwd/merging) registered for path '../super/merging'
+Submodule 'none' ($pwd/none) registered for path '../super/none'
+Submodule 'rebasing' ($pwd/rebasing) registered for path '../super/rebasing'
+Submodule 'submodule' ($pwd/submodule) registered for path '../super/submodule'
+Cloning into '$pwd/recursivesuper/super/merging'...
+done.
+Cloning into '$pwd/recursivesuper/super/none'...
+done.
+Cloning into '$pwd/recursivesuper/super/rebasing'...
+done.
+Cloning into '$pwd/recursivesuper/super/submodule'...
+done.
+EOF
+
 test_expect_success 'submodule update --init --recursive from subdirectory' '
        git -C recursivesuper/super reset --hard HEAD^ &&
        (cd recursivesuper &&
         mkdir tmp &&
         cd tmp &&
-        git submodule update --init --recursive ../super >../../actual
+        git submodule update --init --recursive ../super >../../actual 2>../../actual2
        ) &&
-       test_cmp expect actual
+       test_cmp expect actual &&
+       test_cmp expect2 actual2
 '
 
 apos="'";
@@ -850,4 +862,31 @@ test_expect_success 'submodule update --recursive drops module name before recur
         test_i18ngrep "Submodule path .deeper/submodule/subsubmodule.: checked out" actual
        )
 '
+
+test_expect_success 'submodule update can be run in parallel' '
+       (cd super2 &&
+        GIT_TRACE=$(pwd)/trace.out git submodule update --jobs 7 &&
+        grep "7 tasks" trace.out &&
+        git config submodule.fetchJobs 8 &&
+        GIT_TRACE=$(pwd)/trace.out git submodule update &&
+        grep "8 tasks" trace.out &&
+        GIT_TRACE=$(pwd)/trace.out git submodule update --jobs 9 &&
+        grep "9 tasks" trace.out
+       )
+'
+
+test_expect_success 'git clone passes the parallel jobs config on to submodules' '
+       test_when_finished "rm -rf super4" &&
+       GIT_TRACE=$(pwd)/trace.out git clone --recurse-submodules --jobs 7 . super4 &&
+       grep "7 tasks" trace.out &&
+       rm -rf super4 &&
+       git config --global submodule.fetchJobs 8 &&
+       GIT_TRACE=$(pwd)/trace.out git clone --recurse-submodules . super4 &&
+       grep "8 tasks" trace.out &&
+       rm -rf super4 &&
+       GIT_TRACE=$(pwd)/trace.out git clone --recurse-submodules --jobs 9 . super4 &&
+       grep "9 tasks" trace.out &&
+       rm -rf super4
+'
+
 test_done
index 900f7de05a67424c867e0f149e7e7a448111791c..d84897a67a3c365e280f88b36f43fc49e1ac9d7b 100755 (executable)
@@ -607,4 +607,24 @@ test_expect_success '--only works on to-be-born branch' '
        test_cmp expected actual
 '
 
+test_expect_success '--dry-run with conflicts fixed from a merge' '
+       # setup two branches with conflicting information
+       # in the same file, resolve the conflict,
+       # call commit with --dry-run
+       echo "Initial contents, unimportant" >test-file &&
+       git add test-file &&
+       git commit -m "Initial commit" &&
+       echo "commit-1-state" >test-file &&
+       git commit -m "commit 1" -i test-file &&
+       git tag commit-1 &&
+       git checkout -b branch-2 HEAD^1 &&
+       echo "commit-2-state" >test-file &&
+       git commit -m "commit 2" -i test-file &&
+       ! $(git merge --no-commit commit-1) &&
+       echo "commit-2-state" >test-file &&
+       git add test-file &&
+       git commit --dry-run &&
+       git commit -m "conflicts fixed from merge."
+'
+
 test_done
index 2ddf28c984de99cb17884f76d68a708076f57e12..ed2653d46fe6cd0ed1dd5dc2dfd73f7340b8fe31 100755 (executable)
@@ -3,11 +3,10 @@
 test_description='verbose commit template'
 . ./test-lib.sh
 
-cat >check-for-diff <<EOF
-#!$SHELL_PATH
-exec grep '^diff --git' "\$1"
+write_script "check-for-diff" <<\EOF &&
+grep '^diff --git' "$1" >out
+exit 0
 EOF
-chmod +x check-for-diff
 test_set_editor "$PWD/check-for-diff"
 
 cat >message <<'EOF'
@@ -23,7 +22,8 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'initial commit shows verbose diff' '
-       git commit --amend -v
+       git commit --amend -v &&
+       test_line_count = 1 out
 '
 
 test_expect_success 'second commit' '
@@ -39,13 +39,15 @@ check_message() {
 
 test_expect_success 'verbose diff is stripped out' '
        git commit --amend -v &&
-       check_message message
+       check_message message &&
+       test_line_count = 1 out
 '
 
 test_expect_success 'verbose diff is stripped out (mnemonicprefix)' '
        git config diff.mnemonicprefix true &&
        git commit --amend -v &&
-       check_message message
+       check_message message &&
+       test_line_count = 1 out
 '
 
 cat >diff <<'EOF'
@@ -96,4 +98,60 @@ test_expect_success 'verbose diff is stripped out with set core.commentChar' '
        test_i18ngrep "Aborting commit due to empty commit message." err
 '
 
+test_expect_success 'status does not verbose without --verbose' '
+       git status >actual &&
+       ! grep "^diff --git" actual
+'
+
+test_expect_success 'setup -v -v' '
+       echo dirty >file
+'
+
+for i in true 1
+do
+       test_expect_success "commit.verbose=$i and --verbose omitted" "
+               git -c commit.verbose=$i commit --amend &&
+               test_line_count = 1 out
+       "
+done
+
+for i in false -2 -1 0
+do
+       test_expect_success "commit.verbose=$i and --verbose omitted" "
+               git -c commit.verbose=$i commit --amend &&
+               test_line_count = 0 out
+       "
+done
+
+for i in 2 3
+do
+       test_expect_success "commit.verbose=$i and --verbose omitted" "
+               git -c commit.verbose=$i commit --amend &&
+               test_line_count = 2 out
+       "
+done
+
+for i in true false -2 -1 0 1 2 3
+do
+       test_expect_success "commit.verbose=$i and --verbose" "
+               git -c commit.verbose=$i commit --amend --verbose &&
+               test_line_count = 1 out
+       "
+
+       test_expect_success "commit.verbose=$i and --no-verbose" "
+               git -c commit.verbose=$i commit --amend --no-verbose &&
+               test_line_count = 0 out
+       "
+
+       test_expect_success "commit.verbose=$i and -v -v" "
+               git -c commit.verbose=$i commit --amend -v -v &&
+               test_line_count = 2 out
+       "
+done
+
+test_expect_success "status ignores commit.verbose=true" '
+       git -c commit.verbose=true status >actual &&
+       ! grep "^diff --git actual"
+'
+
 test_done
index 18e5cf06630273a6131d13d9a82f085d33f15153..4177a8609acac433cbc3f87d9ddec9c02672963c 100755 (executable)
@@ -45,12 +45,18 @@ test_expect_success GPG 'create signed commits' '
        git tag seventh-signed &&
 
        echo 8 >file && test_tick && git commit -a -m eighth -SB7227189 &&
-       git tag eighth-signed-alt
+       git tag eighth-signed-alt &&
+
+       # commit.gpgsign is still on but this must not be signed
+       git tag ninth-unsigned $(echo 9 | git commit-tree HEAD^{tree}) &&
+       # explicit -S of course must sign.
+       git tag tenth-signed $(echo 9 | git commit-tree -S HEAD^{tree})
 '
 
 test_expect_success GPG 'verify and show signatures' '
        (
-               for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed
+               for commit in initial second merge fourth-signed \
+                       fifth-signed sixth-signed seventh-signed tenth-signed
                do
                        git verify-commit $commit &&
                        git show --pretty=short --show-signature $commit >actual &&
@@ -60,7 +66,8 @@ test_expect_success GPG 'verify and show signatures' '
                done
        ) &&
        (
-               for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned
+               for commit in merge^2 fourth-unsigned sixth-unsigned \
+                       seventh-unsigned ninth-unsigned
                do
                        test_must_fail git verify-commit $commit &&
                        git show --pretty=short --show-signature $commit >actual &&
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 ff7a9e968f4dde078a131ee65306ae6784e8faa5..7ce4cd753eb0c50f42b68653da79f90b19edd764 100755 (executable)
@@ -419,6 +419,29 @@ run_dir_diff_test 'difftool --dir-diff when worktree file is missing' '
        grep file2 output
 '
 
+run_dir_diff_test 'difftool --dir-diff with unmerged files' '
+       test_when_finished git reset --hard &&
+       test_config difftool.echo.cmd "echo ok" &&
+       git checkout -B conflict-a &&
+       git checkout -B conflict-b &&
+       git checkout conflict-a &&
+       echo a >>file &&
+       git add file &&
+       git commit -m conflict-a &&
+       git checkout conflict-b &&
+       echo b >>file &&
+       git add file &&
+       git commit -m conflict-b &&
+       git checkout master &&
+       git merge conflict-a &&
+       test_must_fail git merge conflict-b &&
+       cat >expect <<-EOF &&
+               ok
+       EOF
+       git difftool --dir-diff $symlinks -t echo >actual &&
+       test_cmp expect actual
+'
+
 write_script .git/CHECK_SYMLINKS <<\EOF
 for f in file file2 sub/sub
 do
index 22d8367ff397fc68683ff1084ce4914cfbfa9d89..28082b134fd54aafa38a4ac2964fd4c367e19fa2 100755 (executable)
@@ -45,13 +45,13 @@ test_expect_success "checkout from svn" 'svn co "$svnrepo" "$SVN_TREE"'
 
 name='try a deep --rmdir with a commit'
 test_expect_success "$name" '
-       git checkout -f -b mybranch ${remotes_git_svn} &&
+       git checkout -f -b mybranch remotes/git-svn &&
        mv dir/a/b/c/d/e/file dir/file &&
        cp dir/file file &&
        git update-index --add --remove dir/a/b/c/d/e/file dir/file file &&
        git commit -m "$name" &&
        git svn set-tree --find-copies-harder --rmdir \
-               ${remotes_git_svn}..mybranch &&
+               remotes/git-svn..mybranch &&
        svn_cmd up "$SVN_TREE" &&
        test -d "$SVN_TREE"/dir && test ! -d "$SVN_TREE"/dir/a'
 
@@ -65,14 +65,14 @@ test_expect_success "$name" "
        git update-index --add dir/file/file &&
        git commit -m '$name' &&
        test_must_fail git svn set-tree --find-copies-harder --rmdir \
-               ${remotes_git_svn}..mybranch
+               remotes/git-svn..mybranch
 "
 
 
 name='detect node change from directory to file #1'
 test_expect_success "$name" '
        rm -rf dir "$GIT_DIR"/index &&
-       git checkout -f -b mybranch2 ${remotes_git_svn} &&
+       git checkout -f -b mybranch2 remotes/git-svn &&
        mv bar/zzz zzz &&
        rm -rf bar &&
        mv zzz bar &&
@@ -80,14 +80,14 @@ test_expect_success "$name" '
        git update-index --add -- bar &&
        git commit -m "$name" &&
        test_must_fail git svn set-tree --find-copies-harder --rmdir \
-               ${remotes_git_svn}..mybranch2
+               remotes/git-svn..mybranch2
 '
 
 
 name='detect node change from file to directory #2'
 test_expect_success "$name" '
        rm -f "$GIT_DIR"/index &&
-       git checkout -f -b mybranch3 ${remotes_git_svn} &&
+       git checkout -f -b mybranch3 remotes/git-svn &&
        rm bar/zzz &&
        git update-index --remove bar/zzz &&
        mkdir bar/zzz &&
@@ -95,7 +95,7 @@ test_expect_success "$name" '
        git update-index --add bar/zzz/yyy &&
        git commit -m "$name" &&
        git svn set-tree --find-copies-harder --rmdir \
-               ${remotes_git_svn}..mybranch3 &&
+               remotes/git-svn..mybranch3 &&
        svn_cmd up "$SVN_TREE" &&
        test -d "$SVN_TREE"/bar/zzz &&
        test -e "$SVN_TREE"/bar/zzz/yyy
@@ -104,7 +104,7 @@ test_expect_success "$name" '
 name='detect node change from directory to file #2'
 test_expect_success "$name" '
        rm -f "$GIT_DIR"/index &&
-       git checkout -f -b mybranch4 ${remotes_git_svn} &&
+       git checkout -f -b mybranch4 remotes/git-svn &&
        rm -rf dir &&
        git update-index --remove -- dir/file &&
        touch dir &&
@@ -112,19 +112,19 @@ test_expect_success "$name" '
        git update-index --add -- dir &&
        git commit -m "$name" &&
        test_must_fail git svn set-tree --find-copies-harder --rmdir \
-               ${remotes_git_svn}..mybranch4
+               remotes/git-svn..mybranch4
 '
 
 
 name='remove executable bit from a file'
 test_expect_success POSIXPERM "$name" '
        rm -f "$GIT_DIR"/index &&
-       git checkout -f -b mybranch5 ${remotes_git_svn} &&
+       git checkout -f -b mybranch5 remotes/git-svn &&
        chmod -x exec.sh &&
        git update-index exec.sh &&
        git commit -m "$name" &&
        git svn set-tree --find-copies-harder --rmdir \
-               ${remotes_git_svn}..mybranch5 &&
+               remotes/git-svn..mybranch5 &&
        svn_cmd up "$SVN_TREE" &&
        test ! -x "$SVN_TREE"/exec.sh'
 
@@ -135,7 +135,7 @@ test_expect_success POSIXPERM "$name" '
        git update-index exec.sh &&
        git commit -m "$name" &&
        git svn set-tree --find-copies-harder --rmdir \
-               ${remotes_git_svn}..mybranch5 &&
+               remotes/git-svn..mybranch5 &&
        svn_cmd up "$SVN_TREE" &&
        test -x "$SVN_TREE"/exec.sh'
 
@@ -147,7 +147,7 @@ test_expect_success SYMLINKS "$name" '
        git update-index exec.sh &&
        git commit -m "$name" &&
        git svn set-tree --find-copies-harder --rmdir \
-               ${remotes_git_svn}..mybranch5 &&
+               remotes/git-svn..mybranch5 &&
        svn_cmd up "$SVN_TREE" &&
        test -h "$SVN_TREE"/exec.sh'
 
@@ -159,7 +159,7 @@ test_expect_success POSIXPERM,SYMLINKS "$name" '
        git update-index --add file exec-2.sh &&
        git commit -m "$name" &&
        git svn set-tree --find-copies-harder --rmdir \
-               ${remotes_git_svn}..mybranch5 &&
+               remotes/git-svn..mybranch5 &&
        svn_cmd up "$SVN_TREE" &&
        test -x "$SVN_TREE"/file &&
        test -h "$SVN_TREE"/exec-2.sh'
@@ -172,7 +172,7 @@ test_expect_success POSIXPERM,SYMLINKS "$name" '
        git update-index exec-2.sh &&
        git commit -m "$name" &&
        git svn set-tree --find-copies-harder --rmdir \
-               ${remotes_git_svn}..mybranch5 &&
+               remotes/git-svn..mybranch5 &&
        svn_cmd up "$SVN_TREE" &&
        test -f "$SVN_TREE"/exec-2.sh &&
        test ! -h "$SVN_TREE"/exec-2.sh &&
@@ -194,7 +194,7 @@ GIT_SVN_ID=alt
 export GIT_SVN_ID
 test_expect_success "$name" \
     'git svn init "$svnrepo" && git svn fetch &&
-     git rev-list --pretty=raw ${remotes_git_svn} | grep ^tree | uniq > a &&
+     git rev-list --pretty=raw remotes/git-svn | grep ^tree | uniq > a &&
      git rev-list --pretty=raw remotes/alt | grep ^tree | uniq > b &&
      test_cmp a b'
 
@@ -217,17 +217,17 @@ EOF
 
 test_expect_success POSIXPERM,SYMLINKS "$name" "test_cmp a expected"
 
-test_expect_success 'exit if remote refs are ambigious' "
+test_expect_success 'exit if remote refs are ambigious' '
         git config --add svn-remote.svn.fetch \
-                              bar:refs/${remotes_git_svn} &&
+               bar:refs/remotes/git-svn &&
        test_must_fail git svn migrate
-"
+'
 
 test_expect_success 'exit if init-ing a would clobber a URL' '
         svnadmin create "${PWD}/svnrepo2" &&
         svn mkdir -m "mkdir bar" "${svnrepo}2/bar" &&
         git config --unset svn-remote.svn.fetch \
-                                "^bar:refs/${remotes_git_svn}$" &&
+               "^bar:refs/remotes/git-svn$" &&
        test_must_fail git svn init "${svnrepo}2/bar"
         '
 
@@ -237,7 +237,7 @@ test_expect_success \
         git config --get svn-remote.svn.fetch \
                               "^bar:refs/remotes/bar$" &&
         git config --get svn-remote.svn.fetch \
-                              "^:refs/${remotes_git_svn}$"
+                             "^:refs/remotes/git-svn$"
         '
 
 test_expect_success 'dcommit $rev does not clobber current branch' '
@@ -259,26 +259,26 @@ test_expect_success 'dcommit $rev does not clobber current branch' '
        git branch -D my-bar
        '
 
-test_expect_success 'able to dcommit to a subdirectory' "
+test_expect_success 'able to dcommit to a subdirectory' '
        git svn fetch -i bar &&
        git checkout -b my-bar refs/remotes/bar &&
        echo abc > d &&
        git update-index --add d &&
-       git commit -m '/bar/d should be in the log' &&
+       git commit -m "/bar/d should be in the log" &&
        git svn dcommit -i bar &&
-       test -z \"\$(git diff refs/heads/my-bar refs/remotes/bar)\" &&
+       test -z "$(git diff refs/heads/my-bar refs/remotes/bar)" &&
        mkdir newdir &&
        echo new > newdir/dir &&
        git update-index --add newdir/dir &&
-       git commit -m 'add a new directory' &&
+       git commit -m "add a new directory" &&
        git svn dcommit -i bar &&
-       test -z \"\$(git diff refs/heads/my-bar refs/remotes/bar)\" &&
+       test -z "$(git diff refs/heads/my-bar refs/remotes/bar)" &&
        echo foo >> newdir/dir &&
        git update-index newdir/dir &&
-       git commit -m 'modify a file in new directory' &&
+       git commit -m "modify a file in new directory" &&
        git svn dcommit -i bar &&
-       test -z \"\$(git diff refs/heads/my-bar refs/remotes/bar)\"
-       "
+       test -z "$(git diff refs/heads/my-bar refs/remotes/bar)"
+'
 
 test_expect_success 'dcommit should not fail with a touched file' '
        test_commit "commit-new-file-foo2" foo2 &&
@@ -291,13 +291,13 @@ test_expect_success 'rebase should not fail with a touched file' '
        git svn rebase
 '
 
-test_expect_success 'able to set-tree to a subdirectory' "
+test_expect_success 'able to set-tree to a subdirectory' '
        echo cba > d &&
        git update-index d &&
-       git commit -m 'update /bar/d' &&
+       git commit -m "update /bar/d" &&
        git svn set-tree -i bar HEAD &&
-       test -z \"\$(git diff refs/heads/my-bar refs/remotes/bar)\"
-       "
+       test -z "$(git diff refs/heads/my-bar refs/remotes/bar)"
+'
 
 test_expect_success 'git-svn works in a bare repository' '
        mkdir bare-repo &&
index e8173d5fefa205fe74aae88a1e32247a71340bfb..07bfb63777c80f7e508e632faa9f9ce1823d90ea 100755 (executable)
@@ -73,11 +73,11 @@ test_expect_success 'fetch revisions from svn' 'git svn fetch'
 
 name='test svn:keywords ignoring'
 test_expect_success "$name" \
-       'git checkout -b mybranch ${remotes_git_svn} &&
+       'git checkout -b mybranch remotes/git-svn &&
        echo Hi again >> kw.c &&
        git commit -a -m "test keywords ignoring" &&
-       git svn set-tree ${remotes_git_svn}..mybranch &&
-       git pull . ${remotes_git_svn}'
+       git svn set-tree remotes/git-svn..mybranch &&
+       git pull . remotes/git-svn'
 
 expect='/* $Id$ */'
 got="$(sed -ne 2p kw.c)"
@@ -95,7 +95,7 @@ test_expect_success "propset CR on crlf files" '
 
 test_expect_success 'fetch and pull latest from svn and checkout a new wc' \
        'git svn fetch &&
-        git pull . ${remotes_git_svn} &&
+        git pull . remotes/git-svn &&
         svn_cmd co "$svnrepo" new_wc'
 
 for i in crlf ne_crlf lf ne_lf cr ne_cr empty_cr empty_lf empty empty_crlf
@@ -117,7 +117,7 @@ cd test_wc
         svn_cmd commit -m "propset CRLF on cr files"'
 cd ..
 test_expect_success 'fetch and pull latest from svn' \
-       'git svn fetch && git pull . ${remotes_git_svn}'
+       'git svn fetch && git pull . remotes/git-svn'
 
 b_cr="$(git hash-object cr)"
 b_ne_cr="$(git hash-object ne_cr)"
@@ -168,7 +168,7 @@ cat >create-ignore-index.expect <<\EOF
 EOF
 
 test_expect_success 'test create-ignore' "
-       git svn fetch && git pull . ${remotes_git_svn} &&
+       git svn fetch && git pull . remotes/git-svn &&
        git svn create-ignore &&
        cmp ./.gitignore create-ignore.expect &&
        cmp ./deeply/.gitignore create-ignore.expect &&
index eb70f4839ccb065425b5ad8d40b270178dedef1b..66cd51102c8b6f93e4614a13a20bb02dcb0633e5 100755 (executable)
@@ -17,7 +17,7 @@ test_expect_success 'initialize repo' '
 test_expect_success 'mirror via git svn' '
        git svn init "$svnrepo" &&
        git svn fetch &&
-       git checkout -f -b test-rmdir ${remotes_git_svn}
+       git checkout -f -b test-rmdir remotes/git-svn
        '
 
 test_expect_success 'Try a commit on rmdir' '
index 3413164cb127da9e8d601775769b3b049816173e..b28271345c63e752c02d6b3b867f8c59aba08ff8 100755 (executable)
@@ -23,17 +23,19 @@ test_expect_success 'make history for tracking' '
 
 test_expect_success 'clone repo with git' '
        git svn clone -s "$svnrepo" x &&
-       test -f x/FOLLOWME &&
-       test ! -f x/README
+       test_path_is_file x/FOLLOWME &&
+       test_path_is_missing x/README
 '
 
-test_expect_success 'make sure r2 still has old file' "
-       cd x &&
-               test -n \"\$(git svn find-rev r1)\" &&
-               git reset --hard \$(git svn find-rev r1) &&
-               test -f README &&
-               test ! -f FOLLOWME &&
-               test x\$(git svn find-rev r2) = x
-"
+test_expect_success 'make sure r2 still has old file' '
+       (
+               cd x &&
+               test -n "$(git svn find-rev r1)" &&
+               git reset --hard "$(git svn find-rev r1)" &&
+               test_path_is_file README &&
+               test_path_is_missing FOLLOWME &&
+               test -z "$(git svn find-rev r2)"
+       )
+'
 
 test_done
index f6d7ac7c5f67871ce332716561ea6aba7a6f920f..dbe8deac0d2f28099241957e8944e3f714ee2fde 100755 (executable)
@@ -44,7 +44,7 @@ test_expect_success 'commit complementing change from git' '
 test_expect_success 'dcommit fails to commit because of conflict' '
        git svn init "$svnrepo" &&
        git svn fetch &&
-       git reset --hard refs/${remotes_git_svn} &&
+       git reset --hard refs/remotes/git-svn &&
        svn_cmd co "$svnrepo" t.svn &&
        (
                cd t.svn &&
@@ -59,7 +59,7 @@ test_expect_success 'dcommit fails to commit because of conflict' '
        '
 
 test_expect_success 'dcommit does the svn equivalent of an index merge' "
-       git reset --hard refs/${remotes_git_svn} &&
+       git reset --hard refs/remotes/git-svn &&
        echo 'index merge' > file2 &&
        git update-index --add file2 &&
        git commit -a -m 'index merge' &&
@@ -81,7 +81,7 @@ test_expect_success 'commit another change from svn side' '
        '
 
 test_expect_success 'multiple dcommit from git svn will not clobber svn' "
-       git reset --hard refs/${remotes_git_svn} &&
+       git reset --hard refs/remotes/git-svn &&
        echo new file >> new-file &&
        git update-index --add new-file &&
        git commit -a -m 'new file' &&
index 906019803797b01a4b447858d7766486bba951aa..9f3ef8f2ef614814887f94b7f53e4a7e96f6bbcf 100755 (executable)
@@ -19,13 +19,14 @@ test_expect_success 'setup old-looking metadata' '
        git svn init "$svnrepo" &&
        git svn fetch &&
        rm -rf "$GIT_DIR"/svn &&
-       git update-ref refs/heads/git-svn-HEAD refs/${remotes_git_svn} &&
-       git update-ref refs/heads/svn-HEAD refs/${remotes_git_svn} &&
-       git update-ref -d refs/${remotes_git_svn} refs/${remotes_git_svn}
+       git update-ref refs/heads/git-svn-HEAD refs/remotes/git-svn &&
+       git update-ref refs/heads/svn-HEAD refs/remotes/git-svn &&
+       git update-ref -d refs/remotes/git-svn refs/remotes/git-svn
        '
 
-head=$(git rev-parse --verify refs/heads/git-svn-HEAD^0)
-test_expect_success 'git-svn-HEAD is a real HEAD' "test -n '$head'"
+test_expect_success 'git-svn-HEAD is a real HEAD' '
+       git rev-parse --verify refs/heads/git-svn-HEAD^0
+'
 
 svnrepo_escaped=$(echo $svnrepo | sed 's/ /%20/')
 
@@ -35,11 +36,11 @@ test_expect_success 'initialize old-style (v0) git svn layout' '
        echo "$svnrepo" > "$GIT_DIR"/svn/info/url &&
        git svn migrate &&
        ! test -d "$GIT_DIR"/git-svn &&
-       git rev-parse --verify refs/${remotes_git_svn}^0 &&
+       git rev-parse --verify refs/remotes/git-svn^0 &&
        git rev-parse --verify refs/remotes/svn^0 &&
        test "$(git config --get svn-remote.svn.url)" = "$svnrepo_escaped" &&
        test $(git config --get svn-remote.svn.fetch) = \
-             ":refs/${remotes_git_svn}"
+               ":refs/remotes/git-svn"
        '
 
 test_expect_success 'initialize a multi-repository repo' '
@@ -56,9 +57,11 @@ test_expect_success 'initialize a multi-repository repo' '
                                "^tags/\*:refs/remotes/origin/tags/\*$" &&
        git config --add svn-remote.svn.fetch "branches/a:refs/remotes/origin/a" &&
        git config --add svn-remote.svn.fetch "branches/b:refs/remotes/origin/b" &&
-       for i in tags/0.1 tags/0.2 tags/0.3; do
+       for i in tags/0.1 tags/0.2 tags/0.3
+       do
                git config --add svn-remote.svn.fetch \
-                                $i:refs/remotes/origin/$i || exit 1; done &&
+                       $i:refs/remotes/origin/$i || return 1
+       done &&
        git config --get-all svn-remote.svn.fetch > fetch.out &&
        grep "^trunk:refs/remotes/origin/trunk$" fetch.out &&
        grep "^branches/a:refs/remotes/origin/a$" fetch.out &&
@@ -66,34 +69,42 @@ test_expect_success 'initialize a multi-repository repo' '
        grep "^tags/0\.1:refs/remotes/origin/tags/0\.1$" fetch.out &&
        grep "^tags/0\.2:refs/remotes/origin/tags/0\.2$" fetch.out &&
        grep "^tags/0\.3:refs/remotes/origin/tags/0\.3$" fetch.out &&
-       grep "^:refs/${remotes_git_svn}" fetch.out
+       grep "^:refs/remotes/git-svn" fetch.out
        '
 
 # refs should all be different, but the trees should all be the same:
-test_expect_success 'multi-fetch works on partial urls + paths' "
+test_expect_success 'multi-fetch works on partial urls + paths' '
+       refs="trunk a b tags/0.1 tags/0.2 tags/0.3" &&
        git svn multi-fetch &&
-       for i in trunk a b tags/0.1 tags/0.2 tags/0.3; do
-               git rev-parse --verify refs/remotes/origin/\$i^0 >> refs.out || exit 1;
-           done &&
-       test -z \"\$(sort < refs.out | uniq -d)\" &&
-       for i in trunk a b tags/0.1 tags/0.2 tags/0.3; do
-         for j in trunk a b tags/0.1 tags/0.2 tags/0.3; do
-               if test \$j != \$i; then continue; fi
-           test -z \"\$(git diff refs/remotes/origin/\$i \
-                                refs/remotes/origin/\$j)\" ||exit 1; done; done
-       "
+       for i in $refs
+       do
+               git rev-parse --verify refs/remotes/origin/$i^0 || return 1;
+       done >refs.out &&
+       test -z "$(sort <refs.out | uniq -d)" &&
+       for i in $refs
+       do
+               for j in $refs
+               do
+                       git diff --exit-code refs/remotes/origin/$i \
+                                            refs/remotes/origin/$j ||
+                               return 1
+               done
+       done
+'
 
 test_expect_success 'migrate --minimize on old inited layout' '
        git config --unset-all svn-remote.svn.fetch &&
        git config --unset-all svn-remote.svn.url &&
        rm -rf "$GIT_DIR"/svn &&
-       for i in $(cat fetch.out); do
+       for i in $(cat fetch.out)
+       do
                path=$(expr $i : "\([^:]*\):.*$")
                ref=$(expr $i : "[^:]*:\(refs/remotes/.*\)$")
                if test -z "$ref"; then continue; fi
                if test -n "$path"; then path="/$path"; fi
-               ( mkdir -p "$GIT_DIR"/svn/$ref/info/ &&
-               echo "$svnrepo"$path > "$GIT_DIR"/svn/$ref/info/url ) || exit 1;
+               mkdir -p "$GIT_DIR"/svn/$ref/info/ &&
+               echo "$svnrepo"$path >"$GIT_DIR"/svn/$ref/info/url ||
+               return 1
        done &&
        git svn migrate --minimize &&
        test -z "$(git config -l | grep "^svn-remote\.git-svn\.")" &&
@@ -104,7 +115,7 @@ test_expect_success 'migrate --minimize on old inited layout' '
        grep "^tags/0\.1:refs/remotes/origin/tags/0\.1$" fetch.out &&
        grep "^tags/0\.2:refs/remotes/origin/tags/0\.2$" fetch.out &&
        grep "^tags/0\.3:refs/remotes/origin/tags/0\.3$" fetch.out &&
-       grep "^:refs/${remotes_git_svn}" fetch.out
+       grep "^:refs/remotes/git-svn" fetch.out
        '
 
 test_expect_success  ".rev_db auto-converted to .rev_map.UUID" '
index 29fbdfdd3f02c2dfb6219e749dadd8a495a6c97d..dde0a3c2229abab27d1592e740db35ebfed544aa 100755 (executable)
@@ -22,31 +22,31 @@ uuid=161ce429-a9dd-4828-af4a-52023f968c89
 bar_url=http://mayonaise/svnrepo/bar
 test_expect_success 'verify metadata for /bar' "
        git cat-file commit refs/remotes/bar | \
-          grep '^${git_svn_id}: $bar_url@12 $uuid$' &&
+          grep '^git-svn-id: $bar_url@12 $uuid$' &&
        git cat-file commit refs/remotes/bar~1 | \
-          grep '^${git_svn_id}: $bar_url@11 $uuid$' &&
+          grep '^git-svn-id: $bar_url@11 $uuid$' &&
        git cat-file commit refs/remotes/bar~2 | \
-          grep '^${git_svn_id}: $bar_url@10 $uuid$' &&
+          grep '^git-svn-id: $bar_url@10 $uuid$' &&
        git cat-file commit refs/remotes/bar~3 | \
-          grep '^${git_svn_id}: $bar_url@9 $uuid$' &&
+          grep '^git-svn-id: $bar_url@9 $uuid$' &&
        git cat-file commit refs/remotes/bar~4 | \
-          grep '^${git_svn_id}: $bar_url@6 $uuid$' &&
+          grep '^git-svn-id: $bar_url@6 $uuid$' &&
        git cat-file commit refs/remotes/bar~5 | \
-          grep '^${git_svn_id}: $bar_url@1 $uuid$'
+          grep '^git-svn-id: $bar_url@1 $uuid$'
        "
 
 e_url=http://mayonaise/svnrepo/dir/a/b/c/d/e
 test_expect_success 'verify metadata for /dir/a/b/c/d/e' "
        git cat-file commit refs/remotes/e | \
-          grep '^${git_svn_id}: $e_url@1 $uuid$'
+          grep '^git-svn-id: $e_url@1 $uuid$'
        "
 
 dir_url=http://mayonaise/svnrepo/dir
 test_expect_success 'verify metadata for /dir' "
        git cat-file commit refs/remotes/dir | \
-          grep '^${git_svn_id}: $dir_url@2 $uuid$' &&
+          grep '^git-svn-id: $dir_url@2 $uuid$' &&
        git cat-file commit refs/remotes/dir~1 | \
-          grep '^${git_svn_id}: $dir_url@1 $uuid$'
+          grep '^git-svn-id: $dir_url@1 $uuid$'
        "
 
 test_expect_success 'find commit based on SVN revision number' "
index bd081c2ec39686196e7b2873610df9a6466355b3..22b6e5ee7d8c274b7fe60c9f10de45eaf4d3c385 100755 (executable)
@@ -21,31 +21,31 @@ uuid=161ce429-a9dd-4828-af4a-52023f968c89
 bar_url=http://mayonaise/svnrepo/bar
 test_expect_success 'verify metadata for /bar' "
        git cat-file commit refs/remotes/bar | \
-          grep '^${git_svn_id}: $bar_url@12 $uuid$' &&
+          grep '^git-svn-id: $bar_url@12 $uuid$' &&
        git cat-file commit refs/remotes/bar~1 | \
-          grep '^${git_svn_id}: $bar_url@11 $uuid$' &&
+          grep '^git-svn-id: $bar_url@11 $uuid$' &&
        git cat-file commit refs/remotes/bar~2 | \
-          grep '^${git_svn_id}: $bar_url@10 $uuid$' &&
+          grep '^git-svn-id: $bar_url@10 $uuid$' &&
        git cat-file commit refs/remotes/bar~3 | \
-          grep '^${git_svn_id}: $bar_url@9 $uuid$' &&
+          grep '^git-svn-id: $bar_url@9 $uuid$' &&
        git cat-file commit refs/remotes/bar~4 | \
-          grep '^${git_svn_id}: $bar_url@6 $uuid$' &&
+          grep '^git-svn-id: $bar_url@6 $uuid$' &&
        git cat-file commit refs/remotes/bar~5 | \
-          grep '^${git_svn_id}: $bar_url@1 $uuid$'
+          grep '^git-svn-id: $bar_url@1 $uuid$'
        "
 
 e_url=http://mayonaise/svnrepo/dir/a/b/c/d/e
 test_expect_success 'verify metadata for /dir/a/b/c/d/e' "
        git cat-file commit refs/remotes/e | \
-          grep '^${git_svn_id}: $e_url@1 $uuid$'
+          grep '^git-svn-id: $e_url@1 $uuid$'
        "
 
 dir_url=http://mayonaise/svnrepo/dir
 test_expect_success 'verify metadata for /dir' "
        git cat-file commit refs/remotes/dir | \
-          grep '^${git_svn_id}: $dir_url@2 $uuid$' &&
+          grep '^git-svn-id: $dir_url@2 $uuid$' &&
        git cat-file commit refs/remotes/dir~1 | \
-          grep '^${git_svn_id}: $dir_url@1 $uuid$'
+          grep '^git-svn-id: $dir_url@1 $uuid$'
        "
 
 test_done
index 1c84ce1023cedc5005009d3632784125180be075..59465b147eb9cdc7da1fb891ff695cbc4442209a 100755 (executable)
@@ -22,7 +22,7 @@ test_expect_success 'test clone with percent escapes' '
        git svn clone "$svnrepo/pr%20ject" clone &&
        (
                cd clone &&
-               git rev-parse refs/${remotes_git_svn}
+               git rev-parse refs/remotes/git-svn
        )
 '
 
@@ -42,7 +42,7 @@ test_expect_success 'test clone trunk with percent escapes and minimize-url' '
        git svn clone --minimize-url "$svnrepo/pr%20ject/trunk" minimize &&
        (
                cd minimize &&
-               git rev-parse refs/${remotes_git_svn}
+               git rev-parse refs/remotes/git-svn
        )
 '
 
@@ -50,7 +50,7 @@ test_expect_success 'test clone trunk with percent escapes' '
        git svn clone "$svnrepo/pr%20ject/trunk" trunk &&
        (
                cd trunk &&
-               git rev-parse refs/${remotes_git_svn}
+               git rev-parse refs/remotes/git-svn
        )
 '
 
index fd8184787fba13bbbf3f1ef377ad3f979ef8f47a..ead404589eb622edd30adda06ad9684517b7e1f4 100755 (executable)
@@ -17,7 +17,7 @@ rm -rf import
 test_expect_success 'init, fetch and checkout repository' '
        git svn init --rewrite-root=http://invalid.invalid/ "$svnrepo" &&
        git svn fetch &&
-       git checkout -b mybranch ${remotes_git_svn}
+       git checkout -b mybranch remotes/git-svn
        '
 
 test_expect_success 'remove rev_map' '
index 88a2cfa233e54e405956303ca67b807192b16465..372ef156850098928ac1ed402ab2228460af3c00 100755 (executable)
@@ -17,9 +17,9 @@ test_expect_success 'load svn repo' "
 
 test_expect_success 'verify uuid' "
        git cat-file commit refs/remotes/git-svn~0 | \
-          grep '^${git_svn_id}: .*@2 $uuid$' &&
+          grep '^git-svn-id: .*@2 $uuid$' &&
        git cat-file commit refs/remotes/git-svn~1 | \
-          grep '^${git_svn_id}: .*@1 $uuid$'
+          grep '^git-svn-id: .*@1 $uuid$'
        "
 
 test_done
index 25bb60b2814320b628d3d12af88ee1d394a94217..4bca35c2594bbff4a659e1d6d1dcc5e746956a84 100755 (executable)
@@ -2650,6 +2650,21 @@ test_expect_success 'R: ignore non-git options' '
        git fast-import <input
 '
 
+test_expect_success 'R: corrupt lines do not mess marks file' '
+       rm -f io.marks &&
+       blob=$(echo hi | git hash-object --stdin) &&
+       cat >expect <<-EOF &&
+       :3 0000000000000000000000000000000000000000
+       :1 $blob
+       :2 $blob
+       EOF
+       cp expect io.marks &&
+       test_must_fail git fast-import --import-marks=io.marks --export-marks=io.marks <<-\EOF &&
+
+       EOF
+       test_cmp expect io.marks
+'
+
 ##
 ## R: very large blobs
 ##
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" &&
index be12960d396a1e3e0a0d6babeb1ad853e21b2727..fa8b9daf1fafba7498583e916cfea52d3c7207f5 100755 (executable)
@@ -47,23 +47,23 @@ test_expect_success 'Clone repo root path with all history' '
                git init . &&
                git p4 clone --use-client-spec --destination="$git" //depot@all &&
                cat >expect <<-\EOF &&
-Remove file 4
-[git-p4: depot-paths = "//depot/": change = 6]
+               Remove file 4
+               [git-p4: depot-paths = "//depot/": change = 6]
 
-Remove file 3
-[git-p4: depot-paths = "//depot/": change = 5]
+               Remove file 3
+               [git-p4: depot-paths = "//depot/": change = 5]
 
-Add file 4
-[git-p4: depot-paths = "//depot/": change = 4]
+               Add file 4
+               [git-p4: depot-paths = "//depot/": change = 4]
 
-Add file 3
-[git-p4: depot-paths = "//depot/": change = 3]
+               Add file 3
+               [git-p4: depot-paths = "//depot/": change = 3]
 
-Add file 2
-[git-p4: depot-paths = "//depot/": change = 2]
+               Add file 2
+               [git-p4: depot-paths = "//depot/": change = 2]
 
-Add file 1
-[git-p4: depot-paths = "//depot/": change = 1]
+               Add file 1
+               [git-p4: depot-paths = "//depot/": change = 1]
 
                EOF
                git log --format=%B >actual &&
@@ -80,23 +80,23 @@ test_expect_success 'Clone repo subdir with all history but keep empty commits'
                git config git-p4.keepEmptyCommits true &&
                git p4 clone --use-client-spec --destination="$git" //depot@all &&
                cat >expect <<-\EOF &&
-Remove file 4
-[git-p4: depot-paths = "//depot/": change = 6]
+               Remove file 4
+               [git-p4: depot-paths = "//depot/": change = 6]
 
-Remove file 3
-[git-p4: depot-paths = "//depot/": change = 5]
+               Remove file 3
+               [git-p4: depot-paths = "//depot/": change = 5]
 
-Add file 4
-[git-p4: depot-paths = "//depot/": change = 4]
+               Add file 4
+               [git-p4: depot-paths = "//depot/": change = 4]
 
-Add file 3
-[git-p4: depot-paths = "//depot/": change = 3]
+               Add file 3
+               [git-p4: depot-paths = "//depot/": change = 3]
 
-Add file 2
-[git-p4: depot-paths = "//depot/": change = 2]
+               Add file 2
+               [git-p4: depot-paths = "//depot/": change = 2]
 
-Add file 1
-[git-p4: depot-paths = "//depot/": change = 1]
+               Add file 1
+               [git-p4: depot-paths = "//depot/": change = 1]
 
                EOF
                git log --format=%B >actual &&
@@ -112,14 +112,14 @@ test_expect_success 'Clone repo subdir with all history' '
                git init . &&
                git p4 clone --use-client-spec --destination="$git" --verbose //depot@all &&
                cat >expect <<-\EOF &&
-Remove file 3
-[git-p4: depot-paths = "//depot/": change = 5]
+               Remove file 3
+               [git-p4: depot-paths = "//depot/": change = 5]
 
-Add file 3
-[git-p4: depot-paths = "//depot/": change = 3]
+               Add file 3
+               [git-p4: depot-paths = "//depot/": change = 3]
 
-Add file 1
-[git-p4: depot-paths = "//depot/": change = 1]
+               Add file 1
+               [git-p4: depot-paths = "//depot/": change = 1]
 
                EOF
                git log --format=%B >actual &&
diff --git a/t/t9828-git-p4-map-user.sh b/t/t9828-git-p4-map-user.sh
new file mode 100755 (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
diff --git a/t/t9829-git-p4-jobs.sh b/t/t9829-git-p4-jobs.sh
new file mode 100755 (executable)
index 0000000..971aeee
--- /dev/null
@@ -0,0 +1,99 @@
+#!/bin/sh
+
+test_description='git p4 retrieve job info'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+       start_p4d
+'
+
+test_expect_success 'add p4 jobs' '
+       (
+               p4_add_job TESTJOB-A &&
+               p4_add_job TESTJOB-B
+       )
+'
+
+test_expect_success 'add p4 files' '
+       client_view "//depot/... //client/..." &&
+       (
+               cd "$cli" &&
+               >file1 &&
+               p4 add file1 &&
+               p4 submit -d "Add file 1"
+       )
+'
+
+test_expect_success 'check log message of changelist with no jobs' '
+       client_view "//depot/... //client/..." &&
+       test_when_finished cleanup_git &&
+       (
+               cd "$git" &&
+               git init . &&
+               git p4 clone --use-client-spec --destination="$git" //depot@all &&
+               cat >expect <<-\EOF &&
+               Add file 1
+               [git-p4: depot-paths = "//depot/": change = 1]
+
+               EOF
+               git log --format=%B >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'add TESTJOB-A to change 1' '
+       (
+               cd "$cli" &&
+               p4 fix -c 1 TESTJOB-A
+       )
+'
+
+test_expect_success 'check log message of changelist with one job' '
+       client_view "//depot/... //client/..." &&
+       test_when_finished cleanup_git &&
+       (
+               cd "$git" &&
+               git init . &&
+               git p4 clone --use-client-spec --destination="$git" //depot@all &&
+               cat >expect <<-\EOF &&
+               Add file 1
+               Jobs: TESTJOB-A
+               [git-p4: depot-paths = "//depot/": change = 1]
+
+               EOF
+               git log --format=%B >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'add TESTJOB-B to change 1' '
+       (
+               cd "$cli" &&
+               p4 fix -c 1 TESTJOB-B
+       )
+'
+
+test_expect_success 'check log message of changelist with more jobs' '
+       client_view "//depot/... //client/..." &&
+       test_when_finished cleanup_git &&
+       (
+               cd "$git" &&
+               git init . &&
+               git p4 clone --use-client-spec --destination="$git" //depot@all &&
+               cat >expect <<-\EOF &&
+               Add file 1
+               Jobs: TESTJOB-A TESTJOB-B
+               [git-p4: depot-paths = "//depot/": change = 1]
+
+               EOF
+               git log --format=%B >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'kill p4d' '
+       kill_p4d
+'
+
+test_done
index 39c70f03269c263fb4872efed88a4142aec25be1..0055ebba46d539f30b9484335501571c3b4879bd 100644 (file)
@@ -867,10 +867,10 @@ test -d "$GIT_BUILD_DIR"/templates/blt || {
        error "You haven't built things yet, have you?"
 }
 
-if ! test -x "$GIT_BUILD_DIR"/test-chmtime
+if ! test -x "$GIT_BUILD_DIR"/t/helper/test-chmtime
 then
        echo >&2 'You need to build test-chmtime:'
-       echo >&2 'Run "make test-chmtime" in the source (toplevel) directory'
+       echo >&2 'Run "make t/helper/test-chmtime" in the source (toplevel) directory'
        exit 1
 fi
 
diff --git a/tag.c b/tag.c
index d72f742af9a4a6a76c8b0f6fd314473dea244b2a..d1dcd18cd7b53e21fa15bab9baad05cf16a3b9de 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -6,6 +6,59 @@
 
 const char *tag_type = "tag";
 
+static int run_gpg_verify(const char *buf, unsigned long size, unsigned flags)
+{
+       struct signature_check sigc;
+       size_t payload_size;
+       int ret;
+
+       memset(&sigc, 0, sizeof(sigc));
+
+       payload_size = parse_signature(buf, size);
+
+       if (size == payload_size) {
+               if (flags & GPG_VERIFY_VERBOSE)
+                       write_in_full(1, buf, payload_size);
+               return error("no signature found");
+       }
+
+       ret = check_signature(buf, payload_size, buf + payload_size,
+                               size - payload_size, &sigc);
+       print_signature_buffer(&sigc, flags);
+
+       signature_check_clear(&sigc);
+       return ret;
+}
+
+int gpg_verify_tag(const unsigned char *sha1, const char *name_to_report,
+               unsigned flags)
+{
+       enum object_type type;
+       char *buf;
+       unsigned long size;
+       int ret;
+
+       type = sha1_object_info(sha1, NULL);
+       if (type != OBJ_TAG)
+               return error("%s: cannot verify a non-tag object of type %s.",
+                               name_to_report ?
+                               name_to_report :
+                               find_unique_abbrev(sha1, DEFAULT_ABBREV),
+                               typename(type));
+
+       buf = read_sha1_file(sha1, &type, &size);
+       if (!buf)
+               return error("%s: unable to read file.",
+                               name_to_report ?
+                               name_to_report :
+                               find_unique_abbrev(sha1, DEFAULT_ABBREV));
+
+       ret = run_gpg_verify(buf, size, flags);
+
+       free(buf);
+       return ret;
+}
+
 struct object *deref_tag(struct object *o, const char *warn, int warnlen)
 {
        while (o && o->type == OBJ_TAG)
diff --git a/tag.h b/tag.h
index f4580aea42633d4e6d849735cf1eedcbc4730ed2..a5721b6731eb89e1db78e41fcf6b8dfd8784841e 100644 (file)
--- a/tag.h
+++ b/tag.h
@@ -17,5 +17,7 @@ extern int parse_tag_buffer(struct tag *item, const void *data, unsigned long si
 extern int parse_tag(struct tag *item);
 extern struct object *deref_tag(struct object *, const char *, int);
 extern struct object *deref_tag_noverify(struct object *);
+extern int gpg_verify_tag(const unsigned char *sha1,
+               const char *name_to_report, unsigned flags);
 
 #endif /* TAG_H */
diff --git a/test-chmtime.c b/test-chmtime.c
deleted file mode 100644 (file)
index dfe8a83..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * This program can either change modification time of the given
- * file(s) or just print it. The program does not change atime or
- * ctime (their values are explicitly preserved).
- *
- * The mtime can be changed to an absolute value:
- *
- *     test-chmtime =<seconds> file...
- *
- * Relative to the current time as returned by time(3):
- *
- *     test-chmtime =+<seconds> (or =-<seconds>) file...
- *
- * Or relative to the current mtime of the file:
- *
- *     test-chmtime <seconds> file...
- *     test-chmtime +<seconds> (or -<seconds>) file...
- *
- * Examples:
- *
- * To just print the mtime use --verbose and set the file mtime offset to 0:
- *
- *     test-chmtime -v +0 file
- *
- * To set the mtime to current time:
- *
- *     test-chmtime =+0 file
- *
- */
-#include "git-compat-util.h"
-#include <utime.h>
-
-static const char usage_str[] = "-v|--verbose (+|=|=+|=-|-)<seconds> <file>...";
-
-static int timespec_arg(const char *arg, long int *set_time, int *set_eq)
-{
-       char *test;
-       const char *timespec = arg;
-       *set_eq = (*timespec == '=') ? 1 : 0;
-       if (*set_eq) {
-               timespec++;
-               if (*timespec == '+') {
-                       *set_eq = 2; /* relative "in the future" */
-                       timespec++;
-               }
-       }
-       *set_time = strtol(timespec, &test, 10);
-       if (*test) {
-               fprintf(stderr, "Not a base-10 integer: %s\n", arg + 1);
-               return 0;
-       }
-       if ((*set_eq && *set_time < 0) || *set_eq == 2) {
-               time_t now = time(NULL);
-               *set_time += now;
-       }
-       return 1;
-}
-
-int main(int argc, char *argv[])
-{
-       static int verbose;
-
-       int i = 1;
-       /* no mtime change by default */
-       int set_eq = 0;
-       long int set_time = 0;
-
-       if (argc < 3)
-               goto usage;
-
-       if (strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0) {
-               verbose = 1;
-               ++i;
-       }
-       if (timespec_arg(argv[i], &set_time, &set_eq))
-               ++i;
-       else
-               goto usage;
-
-       for (; i < argc; i++) {
-               struct stat sb;
-               struct utimbuf utb;
-
-               if (stat(argv[i], &sb) < 0) {
-                       fprintf(stderr, "Failed to stat %s: %s\n",
-                               argv[i], strerror(errno));
-                       return 1;
-               }
-
-#ifdef GIT_WINDOWS_NATIVE
-               if (!(sb.st_mode & S_IWUSR) &&
-                               chmod(argv[i], sb.st_mode | S_IWUSR)) {
-                       fprintf(stderr, "Could not make user-writable %s: %s",
-                               argv[i], strerror(errno));
-                       return 1;
-               }
-#endif
-
-               utb.actime = sb.st_atime;
-               utb.modtime = set_eq ? set_time : sb.st_mtime + set_time;
-
-               if (verbose) {
-                       uintmax_t mtime = utb.modtime < 0 ? 0: utb.modtime;
-                       printf("%"PRIuMAX"\t%s\n", mtime, argv[i]);
-               }
-
-               if (utb.modtime != sb.st_mtime && utime(argv[i], &utb) < 0) {
-                       fprintf(stderr, "Failed to modify time on %s: %s\n",
-                               argv[i], strerror(errno));
-                       return 1;
-               }
-       }
-
-       return 0;
-
-usage:
-       fprintf(stderr, "usage: %s %s\n", argv[0], usage_str);
-       return 1;
-}
diff --git a/test-config.c b/test-config.c
deleted file mode 100644 (file)
index 6a77552..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-#include "cache.h"
-#include "string-list.h"
-
-/*
- * This program exposes the C API of the configuration mechanism
- * as a set of simple commands in order to facilitate testing.
- *
- * Reads stdin and prints result of command to stdout:
- *
- * get_value -> prints the value with highest priority for the entered key
- *
- * get_value_multi -> prints all values for the entered key in increasing order
- *                  of priority
- *
- * get_int -> print integer value for the entered key or die
- *
- * get_bool -> print bool value for the entered key or die
- *
- * get_string -> print string value for the entered key or die
- *
- * configset_get_value -> returns value with the highest priority for the entered key
- *                     from a config_set constructed from files entered as arguments.
- *
- * configset_get_value_multi -> returns value_list for the entered key sorted in
- *                             ascending order of priority from a config_set
- *                             constructed from files entered as arguments.
- *
- * Examples:
- *
- * To print the value with highest priority for key "foo.bAr Baz.rock":
- *     test-config get_value "foo.bAr Baz.rock"
- *
- */
-
-
-int main(int argc, char **argv)
-{
-       int i, val;
-       const char *v;
-       const struct string_list *strptr;
-       struct config_set cs;
-       git_configset_init(&cs);
-
-       if (argc < 2) {
-               fprintf(stderr, "Please, provide a command name on the command-line\n");
-               goto exit1;
-       } else if (argc == 3 && !strcmp(argv[1], "get_value")) {
-               if (!git_config_get_value(argv[2], &v)) {
-                       if (!v)
-                               printf("(NULL)\n");
-                       else
-                               printf("%s\n", v);
-                       goto exit0;
-               } else {
-                       printf("Value not found for \"%s\"\n", argv[2]);
-                       goto exit1;
-               }
-       } else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) {
-               strptr = git_config_get_value_multi(argv[2]);
-               if (strptr) {
-                       for (i = 0; i < strptr->nr; i++) {
-                               v = strptr->items[i].string;
-                               if (!v)
-                                       printf("(NULL)\n");
-                               else
-                                       printf("%s\n", v);
-                       }
-                       goto exit0;
-               } else {
-                       printf("Value not found for \"%s\"\n", argv[2]);
-                       goto exit1;
-               }
-       } else if (argc == 3 && !strcmp(argv[1], "get_int")) {
-               if (!git_config_get_int(argv[2], &val)) {
-                       printf("%d\n", val);
-                       goto exit0;
-               } else {
-                       printf("Value not found for \"%s\"\n", argv[2]);
-                       goto exit1;
-               }
-       } else if (argc == 3 && !strcmp(argv[1], "get_bool")) {
-               if (!git_config_get_bool(argv[2], &val)) {
-                       printf("%d\n", val);
-                       goto exit0;
-               } else {
-                       printf("Value not found for \"%s\"\n", argv[2]);
-                       goto exit1;
-               }
-       } else if (argc == 3 && !strcmp(argv[1], "get_string")) {
-               if (!git_config_get_string_const(argv[2], &v)) {
-                       printf("%s\n", v);
-                       goto exit0;
-               } else {
-                       printf("Value not found for \"%s\"\n", argv[2]);
-                       goto exit1;
-               }
-       } else if (!strcmp(argv[1], "configset_get_value")) {
-               for (i = 3; i < argc; i++) {
-                       int err;
-                       if ((err = git_configset_add_file(&cs, argv[i]))) {
-                               fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
-                               goto exit2;
-                       }
-               }
-               if (!git_configset_get_value(&cs, argv[2], &v)) {
-                       if (!v)
-                               printf("(NULL)\n");
-                       else
-                               printf("%s\n", v);
-                       goto exit0;
-               } else {
-                       printf("Value not found for \"%s\"\n", argv[2]);
-                       goto exit1;
-               }
-       } else if (!strcmp(argv[1], "configset_get_value_multi")) {
-               for (i = 3; i < argc; i++) {
-                       int err;
-                       if ((err = git_configset_add_file(&cs, argv[i]))) {
-                               fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
-                               goto exit2;
-                       }
-               }
-               strptr = git_configset_get_value_multi(&cs, argv[2]);
-               if (strptr) {
-                       for (i = 0; i < strptr->nr; i++) {
-                               v = strptr->items[i].string;
-                               if (!v)
-                                       printf("(NULL)\n");
-                               else
-                                       printf("%s\n", v);
-                       }
-                       goto exit0;
-               } else {
-                       printf("Value not found for \"%s\"\n", argv[2]);
-                       goto exit1;
-               }
-       }
-
-       die("%s: Please check the syntax and the function name", argv[0]);
-
-exit0:
-       git_configset_clear(&cs);
-       return 0;
-
-exit1:
-       git_configset_clear(&cs);
-       return 1;
-
-exit2:
-       git_configset_clear(&cs);
-       return 2;
-}
diff --git a/test-ctype.c b/test-ctype.c
deleted file mode 100644 (file)
index 707a821..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#include "cache.h"
-
-static int rc;
-
-static void report_error(const char *class, int ch)
-{
-       printf("%s classifies char %d (0x%02x) wrongly\n", class, ch, ch);
-       rc = 1;
-}
-
-static int is_in(const char *s, int ch)
-{
-       /* We can't find NUL using strchr.  It's classless anyway. */
-       if (ch == '\0')
-               return 0;
-       return !!strchr(s, ch);
-}
-
-#define TEST_CLASS(t,s) {                      \
-       int i;                                  \
-       for (i = 0; i < 256; i++) {             \
-               if (is_in(s, i) != t(i))        \
-                       report_error(#t, i);    \
-       }                                       \
-}
-
-#define DIGIT "0123456789"
-#define LOWER "abcdefghijklmnopqrstuvwxyz"
-#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-
-int main(int argc, char **argv)
-{
-       TEST_CLASS(isdigit, DIGIT);
-       TEST_CLASS(isspace, " \n\r\t");
-       TEST_CLASS(isalpha, LOWER UPPER);
-       TEST_CLASS(isalnum, LOWER UPPER DIGIT);
-       TEST_CLASS(is_glob_special, "*?[\\");
-       TEST_CLASS(is_regex_special, "$()*+.?[\\^{|");
-       TEST_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~");
-
-       return rc;
-}
diff --git a/test-date.c b/test-date.c
deleted file mode 100644 (file)
index 63f3735..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "cache.h"
-
-static const char *usage_msg = "\n"
-"  test-date show [time_t]...\n"
-"  test-date parse [date]...\n"
-"  test-date approxidate [date]...\n";
-
-static void show_dates(char **argv, struct timeval *now)
-{
-       struct strbuf buf = STRBUF_INIT;
-
-       for (; *argv; argv++) {
-               time_t t = atoi(*argv);
-               show_date_relative(t, 0, now, &buf);
-               printf("%s -> %s\n", *argv, buf.buf);
-       }
-       strbuf_release(&buf);
-}
-
-static void parse_dates(char **argv, struct timeval *now)
-{
-       struct strbuf result = STRBUF_INIT;
-
-       for (; *argv; argv++) {
-               unsigned long t;
-               int tz;
-
-               strbuf_reset(&result);
-               parse_date(*argv, &result);
-               if (sscanf(result.buf, "%lu %d", &t, &tz) == 2)
-                       printf("%s -> %s\n",
-                              *argv, show_date(t, tz, DATE_MODE(ISO8601)));
-               else
-                       printf("%s -> bad\n", *argv);
-       }
-       strbuf_release(&result);
-}
-
-static void parse_approxidate(char **argv, struct timeval *now)
-{
-       for (; *argv; argv++) {
-               time_t t;
-               t = approxidate_relative(*argv, now);
-               printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601)));
-       }
-}
-
-int main(int argc, char **argv)
-{
-       struct timeval now;
-       const char *x;
-
-       x = getenv("TEST_DATE_NOW");
-       if (x) {
-               now.tv_sec = atoi(x);
-               now.tv_usec = 0;
-       }
-       else
-               gettimeofday(&now, NULL);
-
-       argv++;
-       if (!*argv)
-               usage(usage_msg);
-       if (!strcmp(*argv, "show"))
-               show_dates(argv+1, &now);
-       else if (!strcmp(*argv, "parse"))
-               parse_dates(argv+1, &now);
-       else if (!strcmp(*argv, "approxidate"))
-               parse_approxidate(argv+1, &now);
-       else
-               usage(usage_msg);
-       return 0;
-}
diff --git a/test-delta.c b/test-delta.c
deleted file mode 100644 (file)
index 4595cd6..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * test-delta.c: test code to exercise diff-delta.c and patch-delta.c
- *
- * (C) 2005 Nicolas Pitre <nico@fluxnic.net>
- *
- * This code is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include "git-compat-util.h"
-#include "delta.h"
-#include "cache.h"
-
-static const char usage_str[] =
-       "test-delta (-d|-p) <from_file> <data_file> <out_file>";
-
-int main(int argc, char *argv[])
-{
-       int fd;
-       struct stat st;
-       void *from_buf, *data_buf, *out_buf;
-       unsigned long from_size, data_size, out_size;
-
-       if (argc != 5 || (strcmp(argv[1], "-d") && strcmp(argv[1], "-p"))) {
-               fprintf(stderr, "usage: %s\n", usage_str);
-               return 1;
-       }
-
-       fd = open(argv[2], O_RDONLY);
-       if (fd < 0 || fstat(fd, &st)) {
-               perror(argv[2]);
-               return 1;
-       }
-       from_size = st.st_size;
-       from_buf = mmap(NULL, from_size, PROT_READ, MAP_PRIVATE, fd, 0);
-       if (from_buf == MAP_FAILED) {
-               perror(argv[2]);
-               close(fd);
-               return 1;
-       }
-       close(fd);
-
-       fd = open(argv[3], O_RDONLY);
-       if (fd < 0 || fstat(fd, &st)) {
-               perror(argv[3]);
-               return 1;
-       }
-       data_size = st.st_size;
-       data_buf = mmap(NULL, data_size, PROT_READ, MAP_PRIVATE, fd, 0);
-       if (data_buf == MAP_FAILED) {
-               perror(argv[3]);
-               close(fd);
-               return 1;
-       }
-       close(fd);
-
-       if (argv[1][1] == 'd')
-               out_buf = diff_delta(from_buf, from_size,
-                                    data_buf, data_size,
-                                    &out_size, 0);
-       else
-               out_buf = patch_delta(from_buf, from_size,
-                                     data_buf, data_size,
-                                     &out_size);
-       if (!out_buf) {
-               fprintf(stderr, "delta operation failed (returned NULL)\n");
-               return 1;
-       }
-
-       fd = open (argv[4], O_WRONLY|O_CREAT|O_TRUNC, 0666);
-       if (fd < 0 || write_in_full(fd, out_buf, out_size) != out_size) {
-               perror(argv[4]);
-               return 1;
-       }
-
-       return 0;
-}
diff --git a/test-dump-cache-tree.c b/test-dump-cache-tree.c
deleted file mode 100644 (file)
index bb53c0a..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-#include "cache.h"
-#include "tree.h"
-#include "cache-tree.h"
-
-
-static void dump_one(struct cache_tree *it, const char *pfx, const char *x)
-{
-       if (it->entry_count < 0)
-               printf("%-40s %s%s (%d subtrees)\n",
-                      "invalid", x, pfx, it->subtree_nr);
-       else
-               printf("%s %s%s (%d entries, %d subtrees)\n",
-                      sha1_to_hex(it->sha1), x, pfx,
-                      it->entry_count, it->subtree_nr);
-}
-
-static int dump_cache_tree(struct cache_tree *it,
-                          struct cache_tree *ref,
-                          const char *pfx)
-{
-       int i;
-       int errs = 0;
-
-       if (!it || !ref)
-               /* missing in either */
-               return 0;
-
-       if (it->entry_count < 0) {
-               /* invalid */
-               dump_one(it, pfx, "");
-               dump_one(ref, pfx, "#(ref) ");
-       }
-       else {
-               dump_one(it, pfx, "");
-               if (hashcmp(it->sha1, ref->sha1) ||
-                   ref->entry_count != it->entry_count ||
-                   ref->subtree_nr != it->subtree_nr) {
-                       /* claims to be valid but is lying */
-                       dump_one(ref, pfx, "#(ref) ");
-                       errs = 1;
-               }
-       }
-
-       for (i = 0; i < it->subtree_nr; i++) {
-               char path[PATH_MAX];
-               struct cache_tree_sub *down = it->down[i];
-               struct cache_tree_sub *rdwn;
-
-               rdwn = cache_tree_sub(ref, down->name);
-               xsnprintf(path, sizeof(path), "%s%.*s/", pfx, down->namelen, down->name);
-               if (dump_cache_tree(down->cache_tree, rdwn->cache_tree, path))
-                       errs = 1;
-       }
-       return errs;
-}
-
-int main(int ac, char **av)
-{
-       struct index_state istate;
-       struct cache_tree *another = cache_tree();
-       if (read_cache() < 0)
-               die("unable to read index file");
-       istate = the_index;
-       istate.cache_tree = another;
-       cache_tree_update(&istate, WRITE_TREE_DRY_RUN);
-       return dump_cache_tree(active_cache_tree, another, "");
-}
diff --git a/test-dump-split-index.c b/test-dump-split-index.c
deleted file mode 100644 (file)
index 861d28c..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "cache.h"
-#include "split-index.h"
-#include "ewah/ewok.h"
-
-static void show_bit(size_t pos, void *data)
-{
-       printf(" %d", (int)pos);
-}
-
-int main(int ac, char **av)
-{
-       struct split_index *si;
-       int i;
-
-       do_read_index(&the_index, av[1], 1);
-       printf("own %s\n", sha1_to_hex(the_index.sha1));
-       si = the_index.split_index;
-       if (!si) {
-               printf("not a split index\n");
-               return 0;
-       }
-       printf("base %s\n", sha1_to_hex(si->base_sha1));
-       for (i = 0; i < the_index.cache_nr; i++) {
-               struct cache_entry *ce = the_index.cache[i];
-               printf("%06o %s %d\t%s\n", ce->ce_mode,
-                      sha1_to_hex(ce->sha1), ce_stage(ce), ce->name);
-       }
-       printf("replacements:");
-       if (si->replace_bitmap)
-               ewah_each_bit(si->replace_bitmap, show_bit, NULL);
-       printf("\ndeletions:");
-       if (si->delete_bitmap)
-               ewah_each_bit(si->delete_bitmap, show_bit, NULL);
-       printf("\n");
-       return 0;
-}
diff --git a/test-dump-untracked-cache.c b/test-dump-untracked-cache.c
deleted file mode 100644 (file)
index 0a1c285..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#include "cache.h"
-#include "dir.h"
-
-static int compare_untracked(const void *a_, const void *b_)
-{
-       const char *const *a = a_;
-       const char *const *b = b_;
-       return strcmp(*a, *b);
-}
-
-static int compare_dir(const void *a_, const void *b_)
-{
-       const struct untracked_cache_dir *const *a = a_;
-       const struct untracked_cache_dir *const *b = b_;
-       return strcmp((*a)->name, (*b)->name);
-}
-
-static void dump(struct untracked_cache_dir *ucd, struct strbuf *base)
-{
-       int i, len;
-       qsort(ucd->untracked, ucd->untracked_nr, sizeof(*ucd->untracked),
-             compare_untracked);
-       qsort(ucd->dirs, ucd->dirs_nr, sizeof(*ucd->dirs),
-             compare_dir);
-       len = base->len;
-       strbuf_addf(base, "%s/", ucd->name);
-       printf("%s %s", base->buf,
-              sha1_to_hex(ucd->exclude_sha1));
-       if (ucd->recurse)
-               fputs(" recurse", stdout);
-       if (ucd->check_only)
-               fputs(" check_only", stdout);
-       if (ucd->valid)
-               fputs(" valid", stdout);
-       printf("\n");
-       for (i = 0; i < ucd->untracked_nr; i++)
-               printf("%s\n", ucd->untracked[i]);
-       for (i = 0; i < ucd->dirs_nr; i++)
-               dump(ucd->dirs[i], base);
-       strbuf_setlen(base, len);
-}
-
-int main(int ac, char **av)
-{
-       struct untracked_cache *uc;
-       struct strbuf base = STRBUF_INIT;
-
-       /* Hack to avoid modifying the untracked cache when we read it */
-       ignore_untracked_cache_config = 1;
-
-       setup_git_directory();
-       if (read_cache() < 0)
-               die("unable to read index file");
-       uc = the_index.untracked;
-       if (!uc) {
-               printf("no untracked cache\n");
-               return 0;
-       }
-       printf("info/exclude %s\n", sha1_to_hex(uc->ss_info_exclude.sha1));
-       printf("core.excludesfile %s\n", sha1_to_hex(uc->ss_excludes_file.sha1));
-       printf("exclude_per_dir %s\n", uc->exclude_per_dir);
-       printf("flags %08x\n", uc->dir_flags);
-       if (uc->root)
-               dump(uc->root, &base);
-       return 0;
-}
diff --git a/test-fake-ssh.c b/test-fake-ssh.c
deleted file mode 100644 (file)
index 980de21..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#include "git-compat-util.h"
-#include "run-command.h"
-#include "strbuf.h"
-
-int main(int argc, char **argv)
-{
-       const char *trash_directory = getenv("TRASH_DIRECTORY");
-       struct strbuf buf = STRBUF_INIT;
-       FILE *f;
-       int i;
-       const char *child_argv[] = { NULL, NULL };
-
-       /* First, print all parameters into $TRASH_DIRECTORY/ssh-output */
-       if (!trash_directory)
-               die("Need a TRASH_DIRECTORY!");
-       strbuf_addf(&buf, "%s/ssh-output", trash_directory);
-       f = fopen(buf.buf, "w");
-       if (!f)
-               die("Could not write to %s", buf.buf);
-       for (i = 0; i < argc; i++)
-               fprintf(f, "%s%s", i > 0 ? " " : "", i > 0 ? argv[i] : "ssh:");
-       fprintf(f, "\n");
-       fclose(f);
-
-       /* Now, evaluate the *last* parameter */
-       if (argc < 2)
-               return 0;
-       child_argv[0] = argv[argc - 1];
-       return run_command_v_opt(child_argv, RUN_USING_SHELL);
-}
diff --git a/test-genrandom.c b/test-genrandom.c
deleted file mode 100644 (file)
index 54824d0..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Simple random data generator used to create reproducible test files.
- * This is inspired from POSIX.1-2001 implementation example for rand().
- * Copyright (C) 2007 by Nicolas Pitre, licensed under the GPL version 2.
- */
-
-#include "git-compat-util.h"
-
-int main(int argc, char *argv[])
-{
-       unsigned long count, next = 0;
-       unsigned char *c;
-
-       if (argc < 2 || argc > 3) {
-               fprintf(stderr, "usage: %s <seed_string> [<size>]\n", argv[0]);
-               return 1;
-       }
-
-       c = (unsigned char *) argv[1];
-       do {
-               next = next * 11 + *c;
-       } while (*c++);
-
-       count = (argc == 3) ? strtoul(argv[2], NULL, 0) : -1L;
-
-       while (count--) {
-               next = next * 1103515245 + 12345;
-               if (putchar((next >> 16) & 0xff) == EOF)
-                       return -1;
-       }
-
-       return 0;
-}
diff --git a/test-hashmap.c b/test-hashmap.c
deleted file mode 100644 (file)
index cc2891d..0000000
+++ /dev/null
@@ -1,264 +0,0 @@
-#include "git-compat-util.h"
-#include "hashmap.h"
-
-struct test_entry
-{
-       struct hashmap_entry ent;
-       /* key and value as two \0-terminated strings */
-       char key[FLEX_ARRAY];
-};
-
-static const char *get_value(const struct test_entry *e)
-{
-       return e->key + strlen(e->key) + 1;
-}
-
-static int test_entry_cmp(const struct test_entry *e1,
-               const struct test_entry *e2, const char* key)
-{
-       return strcmp(e1->key, key ? key : e2->key);
-}
-
-static int test_entry_cmp_icase(const struct test_entry *e1,
-               const struct test_entry *e2, const char* key)
-{
-       return strcasecmp(e1->key, key ? key : e2->key);
-}
-
-static struct test_entry *alloc_test_entry(int hash, char *key, int klen,
-               char *value, int vlen)
-{
-       struct test_entry *entry = malloc(sizeof(struct test_entry) + klen
-                       + vlen + 2);
-       hashmap_entry_init(entry, hash);
-       memcpy(entry->key, key, klen + 1);
-       memcpy(entry->key + klen + 1, value, vlen + 1);
-       return entry;
-}
-
-#define HASH_METHOD_FNV 0
-#define HASH_METHOD_I 1
-#define HASH_METHOD_IDIV10 2
-#define HASH_METHOD_0 3
-#define HASH_METHOD_X2 4
-#define TEST_SPARSE 8
-#define TEST_ADD 16
-#define TEST_SIZE 100000
-
-static unsigned int hash(unsigned int method, unsigned int i, const char *key)
-{
-       unsigned int hash = 0;
-       switch (method & 3)
-       {
-       case HASH_METHOD_FNV:
-               hash = strhash(key);
-               break;
-       case HASH_METHOD_I:
-               hash = i;
-               break;
-       case HASH_METHOD_IDIV10:
-               hash = i / 10;
-               break;
-       case HASH_METHOD_0:
-               hash = 0;
-               break;
-       }
-
-       if (method & HASH_METHOD_X2)
-               hash = 2 * hash;
-       return hash;
-}
-
-/*
- * Test performance of hashmap.[ch]
- * Usage: time echo "perfhashmap method rounds" | test-hashmap
- */
-static void perf_hashmap(unsigned int method, unsigned int rounds)
-{
-       struct hashmap map;
-       char buf[16];
-       struct test_entry **entries;
-       unsigned int *hashes;
-       unsigned int i, j;
-
-       entries = malloc(TEST_SIZE * sizeof(struct test_entry *));
-       hashes = malloc(TEST_SIZE * sizeof(int));
-       for (i = 0; i < TEST_SIZE; i++) {
-               snprintf(buf, sizeof(buf), "%i", i);
-               entries[i] = alloc_test_entry(0, buf, strlen(buf), "", 0);
-               hashes[i] = hash(method, i, entries[i]->key);
-       }
-
-       if (method & TEST_ADD) {
-               /* test adding to the map */
-               for (j = 0; j < rounds; j++) {
-                       hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, 0);
-
-                       /* add entries */
-                       for (i = 0; i < TEST_SIZE; i++) {
-                               hashmap_entry_init(entries[i], hashes[i]);
-                               hashmap_add(&map, entries[i]);
-                       }
-
-                       hashmap_free(&map, 0);
-               }
-       } else {
-               /* test map lookups */
-               hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, 0);
-
-               /* fill the map (sparsely if specified) */
-               j = (method & TEST_SPARSE) ? TEST_SIZE / 10 : TEST_SIZE;
-               for (i = 0; i < j; i++) {
-                       hashmap_entry_init(entries[i], hashes[i]);
-                       hashmap_add(&map, entries[i]);
-               }
-
-               for (j = 0; j < rounds; j++) {
-                       for (i = 0; i < TEST_SIZE; i++) {
-                               hashmap_get_from_hash(&map, hashes[i],
-                                                     entries[i]->key);
-                       }
-               }
-
-               hashmap_free(&map, 0);
-       }
-}
-
-#define DELIM " \t\r\n"
-
-/*
- * Read stdin line by line and print result of commands to stdout:
- *
- * hash key -> strhash(key) memhash(key) strihash(key) memihash(key)
- * put key value -> NULL / old value
- * get key -> NULL / value
- * remove key -> NULL / old value
- * iterate -> key1 value1\nkey2 value2\n...
- * size -> tablesize numentries
- *
- * perfhashmap method rounds -> test hashmap.[ch] performance
- */
-int main(int argc, char *argv[])
-{
-       char line[1024];
-       struct hashmap map;
-       int icase;
-
-       /* init hash map */
-       icase = argc > 1 && !strcmp("ignorecase", argv[1]);
-       hashmap_init(&map, (hashmap_cmp_fn) (icase ? test_entry_cmp_icase
-                       : test_entry_cmp), 0);
-
-       /* process commands from stdin */
-       while (fgets(line, sizeof(line), stdin)) {
-               char *cmd, *p1 = NULL, *p2 = NULL;
-               int l1 = 0, l2 = 0, hash = 0;
-               struct test_entry *entry;
-
-               /* break line into command and up to two parameters */
-               cmd = strtok(line, DELIM);
-               /* ignore empty lines */
-               if (!cmd || *cmd == '#')
-                       continue;
-
-               p1 = strtok(NULL, DELIM);
-               if (p1) {
-                       l1 = strlen(p1);
-                       hash = icase ? strihash(p1) : strhash(p1);
-                       p2 = strtok(NULL, DELIM);
-                       if (p2)
-                               l2 = strlen(p2);
-               }
-
-               if (!strcmp("hash", cmd) && l1) {
-
-                       /* print results of different hash functions */
-                       printf("%u %u %u %u\n", strhash(p1), memhash(p1, l1),
-                                       strihash(p1), memihash(p1, l1));
-
-               } else if (!strcmp("add", cmd) && l1 && l2) {
-
-                       /* create entry with key = p1, value = p2 */
-                       entry = alloc_test_entry(hash, p1, l1, p2, l2);
-
-                       /* add to hashmap */
-                       hashmap_add(&map, entry);
-
-               } else if (!strcmp("put", cmd) && l1 && l2) {
-
-                       /* create entry with key = p1, value = p2 */
-                       entry = alloc_test_entry(hash, p1, l1, p2, l2);
-
-                       /* add / replace entry */
-                       entry = hashmap_put(&map, entry);
-
-                       /* print and free replaced entry, if any */
-                       puts(entry ? get_value(entry) : "NULL");
-                       free(entry);
-
-               } else if (!strcmp("get", cmd) && l1) {
-
-                       /* lookup entry in hashmap */
-                       entry = hashmap_get_from_hash(&map, hash, p1);
-
-                       /* print result */
-                       if (!entry)
-                               puts("NULL");
-                       while (entry) {
-                               puts(get_value(entry));
-                               entry = hashmap_get_next(&map, entry);
-                       }
-
-               } else if (!strcmp("remove", cmd) && l1) {
-
-                       /* setup static key */
-                       struct hashmap_entry key;
-                       hashmap_entry_init(&key, hash);
-
-                       /* remove entry from hashmap */
-                       entry = hashmap_remove(&map, &key, p1);
-
-                       /* print result and free entry*/
-                       puts(entry ? get_value(entry) : "NULL");
-                       free(entry);
-
-               } else if (!strcmp("iterate", cmd)) {
-
-                       struct hashmap_iter iter;
-                       hashmap_iter_init(&map, &iter);
-                       while ((entry = hashmap_iter_next(&iter)))
-                               printf("%s %s\n", entry->key, get_value(entry));
-
-               } else if (!strcmp("size", cmd)) {
-
-                       /* print table sizes */
-                       printf("%u %u\n", map.tablesize, map.size);
-
-               } else if (!strcmp("intern", cmd) && l1) {
-
-                       /* test that strintern works */
-                       const char *i1 = strintern(p1);
-                       const char *i2 = strintern(p1);
-                       if (strcmp(i1, p1))
-                               printf("strintern(%s) returns %s\n", p1, i1);
-                       else if (i1 == p1)
-                               printf("strintern(%s) returns input pointer\n", p1);
-                       else if (i1 != i2)
-                               printf("strintern(%s) != strintern(%s)", i1, i2);
-                       else
-                               printf("%s\n", i1);
-
-               } else if (!strcmp("perfhashmap", cmd) && l1 && l2) {
-
-                       perf_hashmap(atoi(p1), atoi(p2));
-
-               } else {
-
-                       printf("Unknown command %s\n", cmd);
-
-               }
-       }
-
-       hashmap_free(&map, 1);
-       return 0;
-}
diff --git a/test-index-version.c b/test-index-version.c
deleted file mode 100644 (file)
index 05d4699..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#include "cache.h"
-
-int main(int argc, char **argv)
-{
-       struct cache_header hdr;
-       int version;
-
-       memset(&hdr,0,sizeof(hdr));
-       if (read(0, &hdr, sizeof(hdr)) != sizeof(hdr))
-               return 0;
-       version = ntohl(hdr.hdr_version);
-       printf("%d\n", version);
-       return 0;
-}
diff --git a/test-line-buffer.c b/test-line-buffer.c
deleted file mode 100644 (file)
index 1e58f04..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * test-line-buffer.c: code to exercise the svn importer's input helper
- */
-
-#include "git-compat-util.h"
-#include "strbuf.h"
-#include "vcs-svn/line_buffer.h"
-
-static uint32_t strtouint32(const char *s)
-{
-       char *end;
-       uintmax_t n = strtoumax(s, &end, 10);
-       if (*s == '\0' || *end != '\0')
-               die("invalid count: %s", s);
-       return (uint32_t) n;
-}
-
-static void handle_command(const char *command, const char *arg, struct line_buffer *buf)
-{
-       switch (*command) {
-       case 'b':
-               if (starts_with(command, "binary ")) {
-                       struct strbuf sb = STRBUF_INIT;
-                       strbuf_addch(&sb, '>');
-                       buffer_read_binary(buf, &sb, strtouint32(arg));
-                       fwrite(sb.buf, 1, sb.len, stdout);
-                       strbuf_release(&sb);
-                       return;
-               }
-       case 'c':
-               if (starts_with(command, "copy ")) {
-                       buffer_copy_bytes(buf, strtouint32(arg));
-                       return;
-               }
-       case 's':
-               if (starts_with(command, "skip ")) {
-                       buffer_skip_bytes(buf, strtouint32(arg));
-                       return;
-               }
-       default:
-               die("unrecognized command: %s", command);
-       }
-}
-
-static void handle_line(const char *line, struct line_buffer *stdin_buf)
-{
-       const char *arg = strchr(line, ' ');
-       if (!arg)
-               die("no argument in line: %s", line);
-       handle_command(line, arg + 1, stdin_buf);
-}
-
-int main(int argc, char *argv[])
-{
-       struct line_buffer stdin_buf = LINE_BUFFER_INIT;
-       struct line_buffer file_buf = LINE_BUFFER_INIT;
-       struct line_buffer *input = &stdin_buf;
-       const char *filename;
-       char *s;
-
-       if (argc == 1)
-               filename = NULL;
-       else if (argc == 2)
-               filename = argv[1];
-       else
-               usage("test-line-buffer [file | &fd] < script");
-
-       if (buffer_init(&stdin_buf, NULL))
-               die_errno("open error");
-       if (filename) {
-               if (*filename == '&') {
-                       if (buffer_fdinit(&file_buf, strtouint32(filename + 1)))
-                               die_errno("error opening fd %s", filename + 1);
-               } else {
-                       if (buffer_init(&file_buf, filename))
-                               die_errno("error opening %s", filename);
-               }
-               input = &file_buf;
-       }
-
-       while ((s = buffer_read_line(&stdin_buf)))
-               handle_line(s, input);
-
-       if (filename && buffer_deinit(&file_buf))
-               die("error reading from %s", filename);
-       if (buffer_deinit(&stdin_buf))
-               die("input error");
-       if (ferror(stdout))
-               die("output error");
-       return 0;
-}
diff --git a/test-match-trees.c b/test-match-trees.c
deleted file mode 100644 (file)
index 4dad709..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "cache.h"
-#include "tree.h"
-
-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))
-               die("cannot parse %s as an object name", av[2]);
-       one = parse_tree_indirect(hash1);
-       if (!one)
-               die("not a tree-ish %s", av[1]);
-       two = parse_tree_indirect(hash2);
-       if (!two)
-               die("not a tree-ish %s", av[2]);
-
-       shift_tree(one->object.oid.hash, two->object.oid.hash, shifted, -1);
-       printf("shifted: %s\n", sha1_to_hex(shifted));
-
-       exit(0);
-}
diff --git a/test-mergesort.c b/test-mergesort.c
deleted file mode 100644 (file)
index ea3b959..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-#include "cache.h"
-#include "mergesort.h"
-
-struct line {
-       char *text;
-       struct line *next;
-};
-
-static void *get_next(const void *a)
-{
-       return ((const struct line *)a)->next;
-}
-
-static void set_next(void *a, void *b)
-{
-       ((struct line *)a)->next = b;
-}
-
-static int compare_strings(const void *a, const void *b)
-{
-       const struct line *x = a, *y = b;
-       return strcmp(x->text, y->text);
-}
-
-int main(int argc, char **argv)
-{
-       struct line *line, *p = NULL, *lines = NULL;
-       struct strbuf sb = STRBUF_INIT;
-
-       for (;;) {
-               if (strbuf_getwholeline(&sb, stdin, '\n'))
-                       break;
-               line = xmalloc(sizeof(struct line));
-               line->text = strbuf_detach(&sb, NULL);
-               if (p) {
-                       line->next = p->next;
-                       p->next = line;
-               } else {
-                       line->next = NULL;
-                       lines = line;
-               }
-               p = line;
-       }
-
-       lines = llist_mergesort(lines, get_next, set_next, compare_strings);
-
-       while (lines) {
-               printf("%s", lines->text);
-               lines = lines->next;
-       }
-       return 0;
-}
diff --git a/test-mktemp.c b/test-mktemp.c
deleted file mode 100644 (file)
index c8c5421..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * test-mktemp.c: code to exercise the creation of temporary files
- */
-#include "git-compat-util.h"
-
-int main(int argc, char *argv[])
-{
-       if (argc != 2)
-               usage("Expected 1 parameter defining the temporary file template");
-
-       xmkstemp(xstrdup(argv[1]));
-
-       return 0;
-}
diff --git a/test-parse-options.c b/test-parse-options.c
deleted file mode 100644 (file)
index 2c8c8f1..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-#include "cache.h"
-#include "parse-options.h"
-#include "string-list.h"
-
-static int boolean = 0;
-static int integer = 0;
-static unsigned long magnitude = 0;
-static unsigned long timestamp;
-static int abbrev = 7;
-static int verbose = 0, dry_run = 0, quiet = 0;
-static char *string = NULL;
-static char *file = NULL;
-static int ambiguous;
-static struct string_list list;
-
-static int length_callback(const struct option *opt, const char *arg, int unset)
-{
-       printf("Callback: \"%s\", %d\n",
-               (arg ? arg : "not set"), unset);
-       if (unset)
-               return 1; /* do not support unset */
-
-       *(int *)opt->value = strlen(arg);
-       return 0;
-}
-
-static int number_callback(const struct option *opt, const char *arg, int unset)
-{
-       *(int *)opt->value = strtol(arg, NULL, 10);
-       return 0;
-}
-
-int main(int argc, char **argv)
-{
-       const char *prefix = "prefix/";
-       const char *usage[] = {
-               "test-parse-options <options>",
-               NULL
-       };
-       struct option options[] = {
-               OPT_BOOL(0, "yes", &boolean, "get a boolean"),
-               OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"),
-               { OPTION_SET_INT, 'B', "no-fear", &boolean, NULL,
-                 "be brave", PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
-               OPT_COUNTUP('b', "boolean", &boolean, "increment by one"),
-               OPT_BIT('4', "or4", &boolean,
-                       "bitwise-or boolean with ...0100", 4),
-               OPT_NEGBIT(0, "neg-or4", &boolean, "same as --no-or4", 4),
-               OPT_GROUP(""),
-               OPT_INTEGER('i', "integer", &integer, "get a integer"),
-               OPT_INTEGER('j', NULL, &integer, "get a integer, too"),
-               OPT_MAGNITUDE('m', "magnitude", &magnitude, "get a magnitude"),
-               OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23),
-               OPT_DATE('t', NULL, &timestamp, "get timestamp of <time>"),
-               OPT_CALLBACK('L', "length", &integer, "str",
-                       "get length of <str>", length_callback),
-               OPT_FILENAME('F', "file", &file, "set file to <file>"),
-               OPT_GROUP("String options"),
-               OPT_STRING('s', "string", &string, "string", "get a string"),
-               OPT_STRING(0, "string2", &string, "str", "get another string"),
-               OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"),
-               OPT_STRING('o', NULL, &string, "str", "get another string"),
-               OPT_NOOP_NOARG(0, "obsolete"),
-               OPT_STRING_LIST(0, "list", &list, "str", "add str to list"),
-               OPT_GROUP("Magic arguments"),
-               OPT_ARGUMENT("quux", "means --quux"),
-               OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
-                       number_callback),
-               { OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b",
-                 PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH },
-               { OPTION_COUNTUP, 0, "ambiguous", &ambiguous, NULL,
-                 "positive ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG },
-               { OPTION_COUNTUP, 0, "no-ambiguous", &ambiguous, NULL,
-                 "negative ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG },
-               OPT_GROUP("Standard options"),
-               OPT__ABBREV(&abbrev),
-               OPT__VERBOSE(&verbose, "be verbose"),
-               OPT__DRY_RUN(&dry_run, "dry run"),
-               OPT__QUIET(&quiet, "be quiet"),
-               OPT_END(),
-       };
-       int i;
-
-       argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0);
-
-       printf("boolean: %d\n", boolean);
-       printf("integer: %d\n", integer);
-       printf("magnitude: %lu\n", magnitude);
-       printf("timestamp: %lu\n", timestamp);
-       printf("string: %s\n", string ? string : "(not set)");
-       printf("abbrev: %d\n", abbrev);
-       printf("verbose: %d\n", verbose);
-       printf("quiet: %s\n", quiet ? "yes" : "no");
-       printf("dry run: %s\n", dry_run ? "yes" : "no");
-       printf("file: %s\n", file ? file : "(not set)");
-
-       for (i = 0; i < list.nr; i++)
-               printf("list: %s\n", list.items[i].string);
-
-       for (i = 0; i < argc; i++)
-               printf("arg %02d: %s\n", i, argv[i]);
-
-       return 0;
-}
diff --git a/test-path-utils.c b/test-path-utils.c
deleted file mode 100644 (file)
index ba805b3..0000000
+++ /dev/null
@@ -1,262 +0,0 @@
-#include "cache.h"
-#include "string-list.h"
-
-/*
- * A "string_list_each_func_t" function that normalizes an entry from
- * GIT_CEILING_DIRECTORIES.  If the path is unusable for some reason,
- * die with an explanation.
- */
-static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
-{
-       char *ceil = item->string;
-
-       if (!*ceil)
-               die("Empty path is not supported");
-       if (!is_absolute_path(ceil))
-               die("Path \"%s\" is not absolute", ceil);
-       if (normalize_path_copy(ceil, ceil) < 0)
-               die("Path \"%s\" could not be normalized", ceil);
-       return 1;
-}
-
-static void normalize_argv_string(const char **var, const char *input)
-{
-       if (!strcmp(input, "<null>"))
-               *var = NULL;
-       else if (!strcmp(input, "<empty>"))
-               *var = "";
-       else
-               *var = input;
-
-       if (*var && (**var == '<' || **var == '('))
-               die("Bad value: %s\n", input);
-}
-
-struct test_data {
-       const char *from;  /* input:  transform from this ... */
-       const char *to;    /* output: ... to this.            */
-       const char *alternative; /* output: ... or this.      */
-};
-
-static int test_function(struct test_data *data, char *(*func)(char *input),
-       const char *funcname)
-{
-       int failed = 0, i;
-       char buffer[1024];
-       char *to;
-
-       for (i = 0; data[i].to; i++) {
-               if (!data[i].from)
-                       to = func(NULL);
-               else {
-                       xsnprintf(buffer, sizeof(buffer), "%s", data[i].from);
-                       to = func(buffer);
-               }
-               if (!strcmp(to, data[i].to))
-                       continue;
-               if (!data[i].alternative)
-                       error("FAIL: %s(%s) => '%s' != '%s'\n",
-                               funcname, data[i].from, to, data[i].to);
-               else if (!strcmp(to, data[i].alternative))
-                       continue;
-               else
-                       error("FAIL: %s(%s) => '%s' != '%s', '%s'\n",
-                               funcname, data[i].from, to, data[i].to,
-                               data[i].alternative);
-               failed = 1;
-       }
-       return failed;
-}
-
-static struct test_data basename_data[] = {
-       /* --- POSIX type paths --- */
-       { NULL,              "."    },
-       { "",                "."    },
-       { ".",               "."    },
-       { "..",              ".."   },
-       { "/",               "/"    },
-       { "//",              "/", "//" },
-       { "///",             "/", "//" },
-       { "////",            "/", "//" },
-       { "usr",             "usr"  },
-       { "/usr",            "usr"  },
-       { "/usr/",           "usr"  },
-       { "/usr//",          "usr"  },
-       { "/usr/lib",        "lib"  },
-       { "usr/lib",         "lib"  },
-       { "usr/lib///",      "lib"  },
-
-#if defined(__MINGW32__) || defined(_MSC_VER)
-       /* --- win32 type paths --- */
-       { "\\usr",           "usr"  },
-       { "\\usr\\",         "usr"  },
-       { "\\usr\\\\",       "usr"  },
-       { "\\usr\\lib",      "lib"  },
-       { "usr\\lib",        "lib"  },
-       { "usr\\lib\\\\\\",  "lib"  },
-       { "C:/usr",          "usr"  },
-       { "C:/usr",          "usr"  },
-       { "C:/usr/",         "usr"  },
-       { "C:/usr//",        "usr"  },
-       { "C:/usr/lib",      "lib"  },
-       { "C:usr/lib",       "lib"  },
-       { "C:usr/lib///",    "lib"  },
-       { "C:",              "."    },
-       { "C:a",             "a"    },
-       { "C:/",             "/"    },
-       { "C:///",           "/"    },
-       { "\\",              "\\", "/" },
-       { "\\\\",            "\\", "/" },
-       { "\\\\\\",          "\\", "/" },
-#endif
-       { NULL,              NULL   }
-};
-
-static struct test_data dirname_data[] = {
-       /* --- POSIX type paths --- */
-       { NULL,              "."      },
-       { "",                "."      },
-       { ".",               "."      },
-       { "..",              "."      },
-       { "/",               "/"      },
-       { "//",              "/", "//" },
-       { "///",             "/", "//" },
-       { "////",            "/", "//" },
-       { "usr",             "."      },
-       { "/usr",            "/"      },
-       { "/usr/",           "/"      },
-       { "/usr//",          "/"      },
-       { "/usr/lib",        "/usr"   },
-       { "usr/lib",         "usr"    },
-       { "usr/lib///",      "usr"    },
-
-#if defined(__MINGW32__) || defined(_MSC_VER)
-       /* --- win32 type paths --- */
-       { "\\",              "\\"     },
-       { "\\\\",            "\\\\"   },
-       { "\\usr",           "\\"     },
-       { "\\usr\\",         "\\"     },
-       { "\\usr\\\\",       "\\"     },
-       { "\\usr\\lib",      "\\usr"  },
-       { "usr\\lib",        "usr"    },
-       { "usr\\lib\\\\\\",  "usr"    },
-       { "C:a",             "C:."    },
-       { "C:/",             "C:/"    },
-       { "C:///",           "C:/"    },
-       { "C:/usr",          "C:/"    },
-       { "C:/usr/",         "C:/"    },
-       { "C:/usr//",        "C:/"    },
-       { "C:/usr/lib",      "C:/usr" },
-       { "C:usr/lib",       "C:usr"  },
-       { "C:usr/lib///",    "C:usr"  },
-       { "\\\\\\",          "\\"     },
-       { "\\\\\\\\",        "\\"     },
-       { "C:",              "C:.", "." },
-#endif
-       { NULL,              NULL     }
-};
-
-int main(int argc, char **argv)
-{
-       if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
-               char *buf = xmallocz(strlen(argv[2]));
-               int rv = normalize_path_copy(buf, argv[2]);
-               if (rv)
-                       buf = "++failed++";
-               puts(buf);
-               return 0;
-       }
-
-       if (argc >= 2 && !strcmp(argv[1], "real_path")) {
-               while (argc > 2) {
-                       puts(real_path(argv[2]));
-                       argc--;
-                       argv++;
-               }
-               return 0;
-       }
-
-       if (argc >= 2 && !strcmp(argv[1], "absolute_path")) {
-               while (argc > 2) {
-                       puts(absolute_path(argv[2]));
-                       argc--;
-                       argv++;
-               }
-               return 0;
-       }
-
-       if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) {
-               int len;
-               struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
-               char *path = xstrdup(argv[2]);
-
-               /*
-                * We have to normalize the arguments because under
-                * Windows, bash mangles arguments that look like
-                * absolute POSIX paths or colon-separate lists of
-                * absolute POSIX paths into DOS paths (e.g.,
-                * "/foo:/foo/bar" might be converted to
-                * "D:\Src\msysgit\foo;D:\Src\msysgit\foo\bar"),
-                * whereas longest_ancestor_length() requires paths
-                * that use forward slashes.
-                */
-               if (normalize_path_copy(path, path))
-                       die("Path \"%s\" could not be normalized", argv[2]);
-               string_list_split(&ceiling_dirs, argv[3], PATH_SEP, -1);
-               filter_string_list(&ceiling_dirs, 0,
-                                  normalize_ceiling_entry, NULL);
-               len = longest_ancestor_length(path, &ceiling_dirs);
-               string_list_clear(&ceiling_dirs, 0);
-               free(path);
-               printf("%d\n", len);
-               return 0;
-       }
-
-       if (argc >= 4 && !strcmp(argv[1], "prefix_path")) {
-               char *prefix = argv[2];
-               int prefix_len = strlen(prefix);
-               int nongit_ok;
-               setup_git_directory_gently(&nongit_ok);
-               while (argc > 3) {
-                       puts(prefix_path(prefix, prefix_len, argv[3]));
-                       argc--;
-                       argv++;
-               }
-               return 0;
-       }
-
-       if (argc == 4 && !strcmp(argv[1], "strip_path_suffix")) {
-               char *prefix = strip_path_suffix(argv[2], argv[3]);
-               printf("%s\n", prefix ? prefix : "(null)");
-               return 0;
-       }
-
-       if (argc == 3 && !strcmp(argv[1], "print_path")) {
-               puts(argv[2]);
-               return 0;
-       }
-
-       if (argc == 4 && !strcmp(argv[1], "relative_path")) {
-               struct strbuf sb = STRBUF_INIT;
-               const char *in, *prefix, *rel;
-               normalize_argv_string(&in, argv[2]);
-               normalize_argv_string(&prefix, argv[3]);
-               rel = relative_path(in, prefix, &sb);
-               if (!rel)
-                       puts("(null)");
-               else
-                       puts(strlen(rel) > 0 ? rel : "(empty)");
-               strbuf_release(&sb);
-               return 0;
-       }
-
-       if (argc == 2 && !strcmp(argv[1], "basename"))
-               return test_function(basename_data, basename, argv[1]);
-
-       if (argc == 2 && !strcmp(argv[1], "dirname"))
-               return test_function(dirname_data, dirname, argv[1]);
-
-       fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
-               argv[1] ? argv[1] : "(there was none)");
-       return 1;
-}
diff --git a/test-prio-queue.c b/test-prio-queue.c
deleted file mode 100644 (file)
index 7be72f0..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#include "cache.h"
-#include "prio-queue.h"
-
-static int intcmp(const void *va, const void *vb, void *data)
-{
-       const int *a = va, *b = vb;
-       return *a - *b;
-}
-
-static void show(int *v)
-{
-       if (!v)
-               printf("NULL\n");
-       else
-               printf("%d\n", *v);
-       free(v);
-}
-
-int main(int argc, char **argv)
-{
-       struct prio_queue pq = { intcmp };
-
-       while (*++argv) {
-               if (!strcmp(*argv, "get"))
-                       show(prio_queue_get(&pq));
-               else if (!strcmp(*argv, "dump")) {
-                       int *v;
-                       while ((v = prio_queue_get(&pq)))
-                              show(v);
-               }
-               else {
-                       int *v = malloc(sizeof(*v));
-                       *v = atoi(*argv);
-                       prio_queue_put(&pq, v);
-               }
-       }
-
-       return 0;
-}
diff --git a/test-read-cache.c b/test-read-cache.c
deleted file mode 100644 (file)
index b25bcf1..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "cache.h"
-
-int main (int argc, char **argv)
-{
-       int i, cnt = 1;
-       if (argc == 2)
-               cnt = strtol(argv[1], NULL, 0);
-       for (i = 0; i < cnt; i++) {
-               read_cache();
-               discard_cache();
-       }
-       return 0;
-}
diff --git a/test-regex.c b/test-regex.c
deleted file mode 100644 (file)
index 0dc598e..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#include "git-compat-util.h"
-
-int main(int argc, char **argv)
-{
-       char *pat = "[^={} \t]+";
-       char *str = "={}\nfred";
-       regex_t r;
-       regmatch_t m[1];
-
-       if (regcomp(&r, pat, REG_EXTENDED | REG_NEWLINE))
-               die("failed regcomp() for pattern '%s'", pat);
-       if (regexec(&r, str, 1, m, 0))
-               die("no match of pattern '%s' to string '%s'", pat, str);
-
-       /* http://sourceware.org/bugzilla/show_bug.cgi?id=3957  */
-       if (m[0].rm_so == 3) /* matches '\n' when it should not */
-               die("regex bug confirmed: re-build git with NO_REGEX=1");
-
-       exit(0);
-}
diff --git a/test-revision-walking.c b/test-revision-walking.c
deleted file mode 100644 (file)
index 3d03133..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * test-revision-walking.c: test revision walking API.
- *
- * (C) 2012 Heiko Voigt <hvoigt@hvoigt.net>
- *
- * This code is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include "cache.h"
-#include "commit.h"
-#include "diff.h"
-#include "revision.h"
-
-static void print_commit(struct commit *commit)
-{
-       struct strbuf sb = STRBUF_INIT;
-       struct pretty_print_context ctx = {0};
-       ctx.date_mode.type = DATE_NORMAL;
-       format_commit_message(commit, " %m %s", &sb, &ctx);
-       printf("%s\n", sb.buf);
-       strbuf_release(&sb);
-}
-
-static int run_revision_walk(void)
-{
-       struct rev_info rev;
-       struct commit *commit;
-       const char *argv[] = {NULL, "--all", NULL};
-       int argc = ARRAY_SIZE(argv) - 1;
-       int got_revision = 0;
-
-       init_revisions(&rev, NULL);
-       setup_revisions(argc, argv, &rev, NULL);
-       if (prepare_revision_walk(&rev))
-               die("revision walk setup failed");
-
-       while ((commit = get_revision(&rev)) != NULL) {
-               print_commit(commit);
-               got_revision = 1;
-       }
-
-       reset_revision_walk();
-       return got_revision;
-}
-
-int main(int argc, char **argv)
-{
-       if (argc < 2)
-               return 1;
-
-       setup_git_directory();
-
-       if (!strcmp(argv[1], "run-twice")) {
-               printf("1st\n");
-               if (!run_revision_walk())
-                       return 1;
-               printf("2nd\n");
-               if (!run_revision_walk())
-                       return 1;
-
-               return 0;
-       }
-
-       fprintf(stderr, "check usage\n");
-       return 1;
-}
diff --git a/test-run-command.c b/test-run-command.c
deleted file mode 100644 (file)
index 30a64a9..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * test-run-command.c: test run command API.
- *
- * (C) 2009 Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
- *
- * This code is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include "git-compat-util.h"
-#include "run-command.h"
-#include "argv-array.h"
-#include "strbuf.h"
-#include <string.h>
-#include <errno.h>
-
-static int number_callbacks;
-static int parallel_next(struct child_process *cp,
-                        struct strbuf *err,
-                        void *cb,
-                        void **task_cb)
-{
-       struct child_process *d = cb;
-       if (number_callbacks >= 4)
-               return 0;
-
-       argv_array_pushv(&cp->args, d->argv);
-       strbuf_addf(err, "preloaded output of a child\n");
-       number_callbacks++;
-       return 1;
-}
-
-static int no_job(struct child_process *cp,
-                 struct strbuf *err,
-                 void *cb,
-                 void **task_cb)
-{
-       strbuf_addf(err, "no further jobs available\n");
-       return 0;
-}
-
-static int task_finished(int result,
-                        struct strbuf *err,
-                        void *pp_cb,
-                        void *pp_task_cb)
-{
-       strbuf_addf(err, "asking for a quick stop\n");
-       return 1;
-}
-
-int main(int argc, char **argv)
-{
-       struct child_process proc = CHILD_PROCESS_INIT;
-       int jobs;
-
-       if (argc < 3)
-               return 1;
-       proc.argv = (const char **)argv + 2;
-
-       if (!strcmp(argv[1], "start-command-ENOENT")) {
-               if (start_command(&proc) < 0 && errno == ENOENT)
-                       return 0;
-               fprintf(stderr, "FAIL %s\n", argv[1]);
-               return 1;
-       }
-       if (!strcmp(argv[1], "run-command"))
-               exit(run_command(&proc));
-
-       jobs = atoi(argv[2]);
-       proc.argv = (const char **)argv + 3;
-
-       if (!strcmp(argv[1], "run-command-parallel"))
-               exit(run_processes_parallel(jobs, parallel_next,
-                                           NULL, NULL, &proc));
-
-       if (!strcmp(argv[1], "run-command-abort"))
-               exit(run_processes_parallel(jobs, parallel_next,
-                                           NULL, task_finished, &proc));
-
-       if (!strcmp(argv[1], "run-command-no-jobs"))
-               exit(run_processes_parallel(jobs, no_job,
-                                           NULL, task_finished, &proc));
-
-       fprintf(stderr, "check usage\n");
-       return 1;
-}
diff --git a/test-scrap-cache-tree.c b/test-scrap-cache-tree.c
deleted file mode 100644 (file)
index 6efee31..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#include "cache.h"
-#include "lockfile.h"
-#include "tree.h"
-#include "cache-tree.h"
-
-static struct lock_file index_lock;
-
-int main(int ac, char **av)
-{
-       hold_locked_index(&index_lock, 1);
-       if (read_cache() < 0)
-               die("unable to read index file");
-       active_cache_tree = NULL;
-       if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
-               die("unable to write index file");
-       return 0;
-}
diff --git a/test-sha1-array.c b/test-sha1-array.c
deleted file mode 100644 (file)
index 60ea1d5..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#include "cache.h"
-#include "sha1-array.h"
-
-static void print_sha1(const unsigned char sha1[20], void *data)
-{
-       puts(sha1_to_hex(sha1));
-}
-
-int main(int argc, char **argv)
-{
-       struct sha1_array array = SHA1_ARRAY_INIT;
-       struct strbuf line = STRBUF_INIT;
-
-       while (strbuf_getline(&line, stdin) != EOF) {
-               const char *arg;
-               unsigned char sha1[20];
-
-               if (skip_prefix(line.buf, "append ", &arg)) {
-                       if (get_sha1_hex(arg, sha1))
-                               die("not a hexadecimal SHA1: %s", arg);
-                       sha1_array_append(&array, sha1);
-               } else if (skip_prefix(line.buf, "lookup ", &arg)) {
-                       if (get_sha1_hex(arg, sha1))
-                               die("not a hexadecimal SHA1: %s", arg);
-                       printf("%d\n", sha1_array_lookup(&array, sha1));
-               } else if (!strcmp(line.buf, "clear"))
-                       sha1_array_clear(&array);
-               else if (!strcmp(line.buf, "for_each_unique"))
-                       sha1_array_for_each_unique(&array, print_sha1, NULL);
-               else
-                       die("unknown command: %s", line.buf);
-       }
-       return 0;
-}
diff --git a/test-sha1.c b/test-sha1.c
deleted file mode 100644 (file)
index e57eae1..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#include "cache.h"
-
-int main(int ac, char **av)
-{
-       git_SHA_CTX ctx;
-       unsigned char sha1[20];
-       unsigned bufsz = 8192;
-       int binary = 0;
-       char *buffer;
-
-       if (ac == 2) {
-               if (!strcmp(av[1], "-b"))
-                       binary = 1;
-               else
-                       bufsz = strtoul(av[1], NULL, 10) * 1024 * 1024;
-       }
-
-       if (!bufsz)
-               bufsz = 8192;
-
-       while ((buffer = malloc(bufsz)) == NULL) {
-               fprintf(stderr, "bufsz %u is too big, halving...\n", bufsz);
-               bufsz /= 2;
-               if (bufsz < 1024)
-                       die("OOPS");
-       }
-
-       git_SHA1_Init(&ctx);
-
-       while (1) {
-               ssize_t sz, this_sz;
-               char *cp = buffer;
-               unsigned room = bufsz;
-               this_sz = 0;
-               while (room) {
-                       sz = xread(0, cp, room);
-                       if (sz == 0)
-                               break;
-                       if (sz < 0)
-                               die_errno("test-sha1");
-                       this_sz += sz;
-                       cp += sz;
-                       room -= sz;
-               }
-               if (this_sz == 0)
-                       break;
-               git_SHA1_Update(&ctx, buffer, this_sz);
-       }
-       git_SHA1_Final(sha1, &ctx);
-
-       if (binary)
-               fwrite(sha1, 1, 20, stdout);
-       else
-               puts(sha1_to_hex(sha1));
-       exit(0);
-}
diff --git a/test-sha1.sh b/test-sha1.sh
deleted file mode 100755 (executable)
index cef4bcc..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/bin/sh
-
-dd if=/dev/zero bs=1048576 count=100 2>/dev/null |
-/usr/bin/time ./test-sha1 >/dev/null
-
-while read expect cnt pfx
-do
-       case "$expect" in '#'*) continue ;; esac
-       actual=$(
-               {
-                       test -z "$pfx" || echo "$pfx"
-                       dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null |
-                       perl -pe 'y/\000/g/'
-               } | ./test-sha1 $cnt
-       )
-       if test "$expect" = "$actual"
-       then
-               echo "OK: $expect $cnt $pfx"
-       else
-               echo >&2 "OOPS: $cnt"
-               echo >&2 "expect: $expect"
-               echo >&2 "actual: $actual"
-               exit 1
-       fi
-done <<EOF
-da39a3ee5e6b4b0d3255bfef95601890afd80709 0
-3f786850e387550fdab836ed7e6dc881de23001b 0 a
-5277cbb45a15902137d332d97e89cf8136545485 0 ab
-03cfd743661f07975fa2f1220c5194cbaff48451 0 abc
-3330b4373640f9e4604991e73c7e86bfd8da2dc3 0 abcd
-ec11312386ad561674f724b8cca7cf1796e26d1d 0 abcde
-bdc37c074ec4ee6050d68bc133c6b912f36474df 0 abcdef
-69bca99b923859f2dc486b55b87f49689b7358c7 0 abcdefg
-e414af7161c9554089f4106d6f1797ef14a73666 0 abcdefgh
-0707f2970043f9f7c22029482db27733deaec029 0 abcdefghi
-a4dd8aa74a5636728fe52451636e2e17726033aa 1
-9986b45e2f4d7086372533bb6953a8652fa3644a 1 frotz
-23d8d4f788e8526b4877548a32577543cbaaf51f 10
-8cd23f822ab44c7f481b8c92d591f6d1fcad431c 10 frotz
-f3b5604a4e604899c1233edb3bf1cc0ede4d8c32 512
-b095bd837a371593048136e429e9ac4b476e1bb3 512 frotz
-08fa81d6190948de5ccca3966340cc48c10cceac 1200 xyzzy
-e33a291f42c30a159733dd98b8b3e4ff34158ca0 4090 4G
-#a3bf783bc20caa958f6cb24dd140a7b21984838d 9999 nitfol
-EOF
-
-exit
-
-# generating test vectors
-# inputs are number of megabytes followed by some random string to prefix.
-
-while read cnt pfx
-do
-       actual=$(
-               {
-                       test -z "$pfx" || echo "$pfx"
-                       dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null |
-                       perl -pe 'y/\000/g/'
-               } | sha1sum |
-               sed -e 's/ .*//'
-       )
-       echo "$actual $cnt $pfx"
-done <<EOF
-0
-0 a
-0 ab
-0 abc
-0 abcd
-0 abcde
-0 abcdef
-0 abcdefg
-0 abcdefgh
-0 abcdefghi
-1
-1 frotz
-10
-10 frotz
-512
-512 frotz
-1200 xyzzy
-4090 4G
-9999 nitfol
-EOF
diff --git a/test-sigchain.c b/test-sigchain.c
deleted file mode 100644 (file)
index e499fce..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#include "cache.h"
-#include "sigchain.h"
-
-#define X(f) \
-static void f(int sig) { \
-       puts(#f); \
-       fflush(stdout); \
-       sigchain_pop(sig); \
-       raise(sig); \
-}
-X(one)
-X(two)
-X(three)
-#undef X
-
-int main(int argc, char **argv) {
-       sigchain_push(SIGTERM, one);
-       sigchain_push(SIGTERM, two);
-       sigchain_push(SIGTERM, three);
-       raise(SIGTERM);
-       return 0;
-}
diff --git a/test-string-list.c b/test-string-list.c
deleted file mode 100644 (file)
index 14bdf9d..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-#include "cache.h"
-#include "string-list.h"
-
-/*
- * Parse an argument into a string list.  arg should either be a
- * ':'-separated list of strings, or "-" to indicate an empty string
- * list (as opposed to "", which indicates a string list containing a
- * single empty string).  list->strdup_strings must be set.
- */
-static void parse_string_list(struct string_list *list, const char *arg)
-{
-       if (!strcmp(arg, "-"))
-               return;
-
-       (void)string_list_split(list, arg, ':', -1);
-}
-
-static void write_list(const struct string_list *list)
-{
-       int i;
-       for (i = 0; i < list->nr; i++)
-               printf("[%d]: \"%s\"\n", i, list->items[i].string);
-}
-
-static void write_list_compact(const struct string_list *list)
-{
-       int i;
-       if (!list->nr)
-               printf("-\n");
-       else {
-               printf("%s", list->items[0].string);
-               for (i = 1; i < list->nr; i++)
-                       printf(":%s", list->items[i].string);
-               printf("\n");
-       }
-}
-
-static int prefix_cb(struct string_list_item *item, void *cb_data)
-{
-       const char *prefix = (const char *)cb_data;
-       return starts_with(item->string, prefix);
-}
-
-int main(int argc, char **argv)
-{
-       if (argc == 5 && !strcmp(argv[1], "split")) {
-               struct string_list list = STRING_LIST_INIT_DUP;
-               int i;
-               const char *s = argv[2];
-               int delim = *argv[3];
-               int maxsplit = atoi(argv[4]);
-
-               i = string_list_split(&list, s, delim, maxsplit);
-               printf("%d\n", i);
-               write_list(&list);
-               string_list_clear(&list, 0);
-               return 0;
-       }
-
-       if (argc == 5 && !strcmp(argv[1], "split_in_place")) {
-               struct string_list list = STRING_LIST_INIT_NODUP;
-               int i;
-               char *s = xstrdup(argv[2]);
-               int delim = *argv[3];
-               int maxsplit = atoi(argv[4]);
-
-               i = string_list_split_in_place(&list, s, delim, maxsplit);
-               printf("%d\n", i);
-               write_list(&list);
-               string_list_clear(&list, 0);
-               free(s);
-               return 0;
-       }
-
-       if (argc == 4 && !strcmp(argv[1], "filter")) {
-               /*
-                * Retain only the items that have the specified prefix.
-                * Arguments: list|- prefix
-                */
-               struct string_list list = STRING_LIST_INIT_DUP;
-               const char *prefix = argv[3];
-
-               parse_string_list(&list, argv[2]);
-               filter_string_list(&list, 0, prefix_cb, (void *)prefix);
-               write_list_compact(&list);
-               string_list_clear(&list, 0);
-               return 0;
-       }
-
-       if (argc == 3 && !strcmp(argv[1], "remove_duplicates")) {
-               struct string_list list = STRING_LIST_INIT_DUP;
-
-               parse_string_list(&list, argv[2]);
-               string_list_remove_duplicates(&list, 0);
-               write_list_compact(&list);
-               string_list_clear(&list, 0);
-               return 0;
-       }
-
-       fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
-               argv[1] ? argv[1] : "(there was none)");
-       return 1;
-}
diff --git a/test-submodule-config.c b/test-submodule-config.c
deleted file mode 100644 (file)
index dab8c27..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-#include "cache.h"
-#include "submodule-config.h"
-#include "submodule.h"
-
-static void die_usage(int argc, char **argv, const char *msg)
-{
-       fprintf(stderr, "%s\n", msg);
-       fprintf(stderr, "Usage: %s [<commit> <submodulepath>] ...\n", argv[0]);
-       exit(1);
-}
-
-static int git_test_config(const char *var, const char *value, void *cb)
-{
-       return parse_submodule_config_option(var, value);
-}
-
-int main(int argc, char **argv)
-{
-       char **arg = argv;
-       int my_argc = argc;
-       int output_url = 0;
-       int lookup_name = 0;
-
-       arg++;
-       my_argc--;
-       while (starts_with(arg[0], "--")) {
-               if (!strcmp(arg[0], "--url"))
-                       output_url = 1;
-               if (!strcmp(arg[0], "--name"))
-                       lookup_name = 1;
-               arg++;
-               my_argc--;
-       }
-
-       if (my_argc % 2 != 0)
-               die_usage(argc, argv, "Wrong number of arguments.");
-
-       setup_git_directory();
-       gitmodules_config();
-       git_config(git_test_config, NULL);
-
-       while (*arg) {
-               unsigned char commit_sha1[20];
-               const struct submodule *submodule;
-               const char *commit;
-               const char *path_or_name;
-
-               commit = arg[0];
-               path_or_name = arg[1];
-
-               if (commit[0] == '\0')
-                       hashcpy(commit_sha1, null_sha1);
-               else if (get_sha1(commit, commit_sha1) < 0)
-                       die_usage(argc, argv, "Commit not found.");
-
-               if (lookup_name) {
-                       submodule = submodule_from_name(commit_sha1, path_or_name);
-               } else
-                       submodule = submodule_from_path(commit_sha1, path_or_name);
-               if (!submodule)
-                       die_usage(argc, argv, "Submodule not found.");
-
-               if (output_url)
-                       printf("Submodule url: '%s' for path '%s'\n",
-                                       submodule->url, submodule->path);
-               else
-                       printf("Submodule name: '%s' for path '%s'\n",
-                                       submodule->name, submodule->path);
-
-               arg += 2;
-       }
-
-       submodule_free();
-
-       return 0;
-}
diff --git a/test-subprocess.c b/test-subprocess.c
deleted file mode 100644 (file)
index 56881a0..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#include "cache.h"
-#include "run-command.h"
-
-int main(int argc, char **argv)
-{
-       struct child_process cp = CHILD_PROCESS_INIT;
-       int nogit = 0;
-
-       setup_git_directory_gently(&nogit);
-       if (nogit)
-               die("No git repo found");
-       if (argc > 1 && !strcmp(argv[1], "--setup-work-tree")) {
-               setup_work_tree();
-               argv++;
-       }
-       cp.git_cmd = 1;
-       cp.argv = (const char **)argv + 1;
-       return run_command(&cp);
-}
diff --git a/test-svn-fe.c b/test-svn-fe.c
deleted file mode 100644 (file)
index 120ec96..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * test-svn-fe: Code to exercise the svn import lib
- */
-
-#include "git-compat-util.h"
-#include "vcs-svn/svndump.h"
-#include "vcs-svn/svndiff.h"
-#include "vcs-svn/sliding_window.h"
-#include "vcs-svn/line_buffer.h"
-
-static const char test_svnfe_usage[] =
-       "test-svn-fe (<dumpfile> | [-d] <preimage> <delta> <len>)";
-
-static int apply_delta(int argc, char *argv[])
-{
-       struct line_buffer preimage = LINE_BUFFER_INIT;
-       struct line_buffer delta = LINE_BUFFER_INIT;
-       struct sliding_view preimage_view = SLIDING_VIEW_INIT(&preimage, -1);
-
-       if (argc != 5)
-               usage(test_svnfe_usage);
-
-       if (buffer_init(&preimage, argv[2]))
-               die_errno("cannot open preimage");
-       if (buffer_init(&delta, argv[3]))
-               die_errno("cannot open delta");
-       if (svndiff0_apply(&delta, (off_t) strtoumax(argv[4], NULL, 0),
-                                       &preimage_view, stdout))
-               return 1;
-       if (buffer_deinit(&preimage))
-               die_errno("cannot close preimage");
-       if (buffer_deinit(&delta))
-               die_errno("cannot close delta");
-       strbuf_release(&preimage_view.buf);
-       return 0;
-}
-
-int main(int argc, char *argv[])
-{
-       if (argc == 2) {
-               if (svndump_init(argv[1]))
-                       return 1;
-               svndump_read(NULL, "refs/heads/master", "refs/notes/svn/revs");
-               svndump_deinit();
-               svndump_reset();
-               return 0;
-       }
-
-       if (argc >= 2 && !strcmp(argv[1], "-d"))
-               return apply_delta(argc, argv);
-       usage(test_svnfe_usage);
-}
diff --git a/test-urlmatch-normalization.c b/test-urlmatch-normalization.c
deleted file mode 100644 (file)
index 090bf21..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-#include "git-compat-util.h"
-#include "urlmatch.h"
-
-int main(int argc, char **argv)
-{
-       const char usage[] = "test-urlmatch-normalization [-p | -l] <url1> | <url1> <url2>";
-       char *url1, *url2;
-       int opt_p = 0, opt_l = 0;
-
-       /*
-        * For one url, succeed if url_normalize succeeds on it, fail otherwise.
-        * For two urls, succeed only if url_normalize succeeds on both and
-        * the results compare equal with strcmp.  If -p is given (one url only)
-        * and url_normalize succeeds, print the result followed by "\n".  If
-        * -l is given (one url only) and url_normalize succeeds, print the
-        * returned length in decimal followed by "\n".
-        */
-
-       if (argc > 1 && !strcmp(argv[1], "-p")) {
-               opt_p = 1;
-               argc--;
-               argv++;
-       } else if (argc > 1 && !strcmp(argv[1], "-l")) {
-               opt_l = 1;
-               argc--;
-               argv++;
-       }
-
-       if (argc < 2 || argc > 3)
-               die("%s", usage);
-
-       if (argc == 2) {
-               struct url_info info;
-               url1 = url_normalize(argv[1], &info);
-               if (!url1)
-                       return 1;
-               if (opt_p)
-                       printf("%s\n", url1);
-               if (opt_l)
-                       printf("%u\n", (unsigned)info.url_len);
-               return 0;
-       }
-
-       if (opt_p || opt_l)
-               die("%s", usage);
-
-       url1 = url_normalize(argv[1], NULL);
-       url2 = url_normalize(argv[2], NULL);
-       return (url1 && url2 && !strcmp(url1, url2)) ? 0 : 1;
-}
diff --git a/test-wildmatch.c b/test-wildmatch.c
deleted file mode 100644 (file)
index 578b164..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "cache.h"
-
-int main(int argc, char **argv)
-{
-       int i;
-       for (i = 2; i < argc; i++) {
-               if (argv[i][0] == '/')
-                       die("Forward slash is not allowed at the beginning of the\n"
-                           "pattern because Windows does not like it. Use `XXX/' instead.");
-               else if (!strncmp(argv[i], "XXX/", 4))
-                       argv[i] += 3;
-       }
-       if (!strcmp(argv[1], "wildmatch"))
-               return !!wildmatch(argv[3], argv[2], WM_PATHNAME, NULL);
-       else if (!strcmp(argv[1], "iwildmatch"))
-               return !!wildmatch(argv[3], argv[2], WM_PATHNAME | WM_CASEFOLD, NULL);
-       else if (!strcmp(argv[1], "pathmatch"))
-               return !!wildmatch(argv[3], argv[2], 0, NULL);
-       else
-               return 1;
-}
index 13b7a57a759c8f15d92cd3f61e22cdd0010db570..bd666b29ec12d4657d1939a6b7de61f55d52bd1f 100644 (file)
@@ -1166,7 +1166,7 @@ static int udt_do_read(struct unidirectional_transfer *t)
        bytes = read(t->src, t->buf + t->bufuse, BUFFERSIZE - t->bufuse);
        if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN &&
                errno != EINTR) {
-               error("read(%s) failed: %s", t->src_name, strerror(errno));
+               error_errno("read(%s) failed", t->src_name);
                return -1;
        } else if (bytes == 0) {
                transfer_debug("%s EOF (with %i bytes in buffer)",
@@ -1193,7 +1193,7 @@ static int udt_do_write(struct unidirectional_transfer *t)
        transfer_debug("%s is writable", t->dest_name);
        bytes = xwrite(t->dest, t->buf, t->bufuse);
        if (bytes < 0 && errno != EWOULDBLOCK) {
-               error("write(%s) failed: %s", t->dest_name, strerror(errno));
+               error_errno("write(%s) failed", t->dest_name);
                return -1;
        } else if (bytes > 0) {
                t->bufuse -= bytes;
@@ -1306,7 +1306,7 @@ static int tloop_join(pid_t pid, const char *name)
 {
        int tret;
        if (waitpid(pid, &tret, 0) < 0) {
-               error("%s process failed to wait: %s", name, strerror(errno));
+               error_errno("%s process failed to wait", name);
                return 1;
        }
        if (!WIFEXITED(tret) || WEXITSTATUS(tret)) {
index 4dda9a14abacbf38ba19fcf89b2ef54576a26538..ff4e0d3cb60b51080d2f6100e38d1476e6eb6dcf 100644 (file)
@@ -183,7 +183,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,
 
        if (t) {
                /* path present in resulting tree */
-               sha1 = tree_entry_extract(t, &path, &mode);
+               sha1 = tree_entry_extract(t, &path, &mode)->hash;
                pathlen = tree_entry_len(&t->entry);
                isdir = S_ISDIR(mode);
        } else {
@@ -229,7 +229,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,
                                                DIFF_STATUS_ADDED;
 
                        if (tpi_valid) {
-                               sha1_i = tp[i].entry.sha1;
+                               sha1_i = tp[i].entry.oid->hash;
                                mode_i = tp[i].entry.mode;
                        }
                        else {
@@ -270,7 +270,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,
                        /* same rule as in emitthis */
                        int tpi_valid = tp && !(tp[i].entry.mode & S_IFXMIN_NEQ);
 
-                       parents_sha1[i] = tpi_valid ? tp[i].entry.sha1
+                       parents_sha1[i] = tpi_valid ? tp[i].entry.oid->hash
                                                    : NULL;
                }
 
@@ -482,7 +482,7 @@ static struct combine_diff_path *ll_diff_tree_paths(
                                                continue;
 
                                        /* diff(t,pi) != Ã¸ */
-                                       if (hashcmp(t.entry.sha1, tp[i].entry.sha1) ||
+                                       if (oidcmp(t.entry.oid, tp[i].entry.oid) ||
                                            (t.entry.mode != tp[i].entry.mode))
                                                continue;
 
index cd4bb2c38bdf40f497ece781ce3a9e61de6b4399..ce278424391b03d1d4e41eb3fc14750f70908fe5 100644 (file)
@@ -38,7 +38,7 @@ static void decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned
        /* Initialize the descriptor entry */
        desc->entry.path = path;
        desc->entry.mode = canon_mode(mode);
-       desc->entry.sha1 = (const unsigned char *)(path + len);
+       desc->entry.oid  = (const struct object_id *)(path + len);
 }
 
 void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
@@ -76,7 +76,7 @@ static void entry_extract(struct tree_desc *t, struct name_entry *a)
 void update_tree_entry(struct tree_desc *desc)
 {
        const void *buf = desc->buffer;
-       const unsigned char *end = desc->entry.sha1 + 20;
+       const unsigned char *end = desc->entry.oid->hash + 20;
        unsigned long size = desc->size;
        unsigned long len = end - (const unsigned char *)buf;
 
@@ -110,7 +110,7 @@ void setup_traverse_info(struct traverse_info *info, const char *base)
                pathlen--;
        info->pathlen = pathlen ? pathlen + 1 : 0;
        info->name.path = base;
-       info->name.sha1 = (void *)(base + pathlen + 1);
+       info->name.oid = (void *)(base + pathlen + 1);
        if (pathlen)
                info->prev = &dummy;
 }
@@ -433,10 +433,10 @@ static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char
        int namelen = strlen(name);
        while (t->size) {
                const char *entry;
-               const unsigned char *sha1;
+               const struct object_id *oid;
                int entrylen, cmp;
 
-               sha1 = tree_entry_extract(t, &entry, mode);
+               oid = tree_entry_extract(t, &entry, mode);
                entrylen = tree_entry_len(&t->entry);
                update_tree_entry(t);
                if (entrylen > namelen)
@@ -447,7 +447,7 @@ static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char
                if (cmp < 0)
                        break;
                if (entrylen == namelen) {
-                       hashcpy(result, sha1);
+                       hashcpy(result, oid->hash);
                        return 0;
                }
                if (name[entrylen] != '/')
@@ -455,10 +455,10 @@ static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char
                if (!S_ISDIR(*mode))
                        break;
                if (++entrylen == namelen) {
-                       hashcpy(result, sha1);
+                       hashcpy(result, oid->hash);
                        return 0;
                }
-               return get_tree_entry(sha1, name + entrylen, result, mode);
+               return get_tree_entry(oid->hash, name + entrylen, result, mode);
        }
        return -1;
 }
index 174eb617dfb9d6a85bcf1e309227c8b724089765..97a7d6957e002e854d3b93831902776c994b6208 100644 (file)
@@ -2,7 +2,7 @@
 #define TREE_WALK_H
 
 struct name_entry {
-       const unsigned char *sha1;
+       const struct object_id *oid;
        const char *path;
        unsigned int mode;
 };
@@ -13,16 +13,16 @@ struct tree_desc {
        unsigned int size;
 };
 
-static inline const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
+static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
 {
        *pathp = desc->entry.path;
        *modep = desc->entry.mode;
-       return desc->entry.sha1;
+       return desc->entry.oid;
 }
 
 static inline int tree_entry_len(const struct name_entry *ne)
 {
-       return (const char *)ne->sha1 - ne->path - 1;
+       return (const char *)ne->oid - ne->path - 1;
 }
 
 void update_tree_entry(struct tree_desc *);
diff --git a/tree.c b/tree.c
index f79ff9813e37529cf7adbcda307027b0495cfb1c..0089e52d9dff41bece11056e92625b923515c462 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -76,7 +76,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
                                continue;
                }
 
-               switch (fn(entry.sha1, base,
+               switch (fn(entry.oid->hash, base,
                           entry.path, entry.mode, stage, context)) {
                case 0:
                        continue;
@@ -87,19 +87,19 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
                }
 
                if (S_ISDIR(entry.mode))
-                       hashcpy(sha1, entry.sha1);
+                       hashcpy(sha1, entry.oid->hash);
                else if (S_ISGITLINK(entry.mode)) {
                        struct commit *commit;
 
-                       commit = lookup_commit(entry.sha1);
+                       commit = lookup_commit(entry.oid->hash);
                        if (!commit)
                                die("Commit %s in submodule path %s%s not found",
-                                   sha1_to_hex(entry.sha1),
+                                   oid_to_hex(entry.oid),
                                    base->buf, entry.path);
 
                        if (parse_commit(commit))
                                die("Invalid commit %s in submodule path %s%s",
-                                   sha1_to_hex(entry.sha1),
+                                   oid_to_hex(entry.oid),
                                    base->buf, entry.path);
 
                        hashcpy(sha1, commit->tree->object.oid.hash);
index aea9aa749f2904739b72fef285f148b8b2aa83a8..6bc9512a4516396c7ac0cc0fd5b5cebb01c60cbd 100644 (file)
@@ -509,7 +509,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
        for (i = 0; i < n; i++, dirmask >>= 1) {
                const unsigned char *sha1 = NULL;
                if (dirmask & 1)
-                       sha1 = names[i].sha1;
+                       sha1 = names[i].oid->hash;
                buf[i] = fill_tree_descriptor(t+i, sha1);
        }
 
@@ -625,7 +625,7 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info, con
        ce->ce_mode = create_ce_mode(n->mode);
        ce->ce_flags = create_ce_flags(stage);
        ce->ce_namelen = len;
-       hashcpy(ce->sha1, n->sha1);
+       hashcpy(ce->sha1, n->oid->hash);
        make_traverse_path(ce->name, info, n);
 
        return ce;
@@ -1533,8 +1533,7 @@ static int verify_absent_1(const struct cache_entry *ce,
 
                path = xmemdupz(ce->name, len);
                if (lstat(path, &st))
-                       ret = error("cannot stat '%s': %s", path,
-                                       strerror(errno));
+                       ret = error_errno("cannot stat '%s'", path);
                else
                        ret = check_ok_to_remove(path, len, DT_UNKNOWN, NULL,
                                                 &st, error_type, o);
@@ -1542,8 +1541,7 @@ static int verify_absent_1(const struct cache_entry *ce,
                return ret;
        } else if (lstat(ce->name, &st)) {
                if (errno != ENOENT)
-                       return error("cannot stat '%s': %s", ce->name,
-                                    strerror(errno));
+                       return error_errno("cannot stat '%s'", ce->name);
                return 0;
        } else {
                return check_ok_to_remove(ce->name, ce_namelen(ce),
index dc802a07c2225463c2e4ee923b9cf144d57a00cc..f19444df7bc9c19da4d2f3bf831525f7a5d6034d 100644 (file)
@@ -174,8 +174,7 @@ static void create_pack_file(void)
 
                if (ret < 0) {
                        if (errno != EINTR) {
-                               error("poll failed, resuming: %s",
-                                     strerror(errno));
+                               error_errno("poll failed, resuming");
                                sleep(1);
                        }
                        continue;
diff --git a/usage.c b/usage.c
index 82ff13163b542aab07e68bfa1056dc46316634a1..1dad03fb5c9b7f8f7785915d004aff43e39b0d2a 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -109,19 +109,11 @@ void NORETURN die(const char *err, ...)
        va_end(params);
 }
 
-void NORETURN die_errno(const char *fmt, ...)
+static const char *fmt_with_err(char *buf, int n, const char *fmt)
 {
-       va_list params;
-       char fmt_with_err[1024];
        char str_error[256], *err;
        int i, j;
 
-       if (die_is_recursing()) {
-               fputs("fatal: recursion detected in die_errno handler\n",
-                       stderr);
-               exit(128);
-       }
-
        err = strerror(errno);
        for (i = j = 0; err[i] && j < sizeof(str_error) - 1; ) {
                if ((str_error[j++] = err[i++]) != '%')
@@ -136,13 +128,37 @@ void NORETURN die_errno(const char *fmt, ...)
                }
        }
        str_error[j] = 0;
-       snprintf(fmt_with_err, sizeof(fmt_with_err), "%s: %s", fmt, str_error);
+       snprintf(buf, n, "%s: %s", fmt, str_error);
+       return buf;
+}
+
+void NORETURN die_errno(const char *fmt, ...)
+{
+       char buf[1024];
+       va_list params;
+
+       if (die_is_recursing()) {
+               fputs("fatal: recursion detected in die_errno handler\n",
+                       stderr);
+               exit(128);
+       }
 
        va_start(params, fmt);
-       die_routine(fmt_with_err, params);
+       die_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
        va_end(params);
 }
 
+int error_errno(const char *fmt, ...)
+{
+       char buf[1024];
+       va_list params;
+
+       va_start(params, fmt);
+       error_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
+       va_end(params);
+       return -1;
+}
+
 #undef error
 int error(const char *err, ...)
 {
@@ -154,6 +170,16 @@ int error(const char *err, ...)
        return -1;
 }
 
+void warning_errno(const char *warn, ...)
+{
+       char buf[1024];
+       va_list params;
+
+       va_start(params, warn);
+       warn_routine(fmt_with_err(buf, sizeof(buf), warn), params);
+       va_end(params);
+}
+
 void warning(const char *warn, ...)
 {
        va_list params;
index 57cc1cec033f638e6d408370039b46b653289e84..e416caf8a4fc744232ca95450bce2b6109388aad 100644 (file)
@@ -53,9 +53,9 @@ long buffer_tmpfile_prepare_to_read(struct line_buffer *buf)
 {
        long pos = ftell(buf->infile);
        if (pos < 0)
-               return error("ftell error: %s", strerror(errno));
+               return error_errno("ftell error");
        if (fseek(buf->infile, 0, SEEK_SET))
-               return error("seek error: %s", strerror(errno));
+               return error_errno("seek error");
        return pos;
 }
 
index f11d490995ee7e3367c1d18e1058f2e322cfa177..06d273c9e8ce2b6b8ddbf967693e5305355d5be6 100644 (file)
@@ -12,7 +12,7 @@ static int input_error(struct line_buffer *file)
 {
        if (!buffer_ferror(file))
                return error("delta preimage ends early");
-       return error("cannot read delta preimage: %s", strerror(errno));
+       return error_errno("cannot read delta preimage");
 }
 
 static int skip_or_whine(struct line_buffer *file, off_t gap)
index 74c97c4543d1fa707961aab1453edfb69b2679d2..75c753162ab3b39293b258ec51f06dcc8d07ec3a 100644 (file)
@@ -64,13 +64,13 @@ static int write_strbuf(struct strbuf *sb, FILE *out)
 {
        if (fwrite(sb->buf, 1, sb->len, out) == sb->len)        /* Success. */
                return 0;
-       return error("cannot write delta postimage: %s", strerror(errno));
+       return error_errno("cannot write delta postimage");
 }
 
 static int error_short_read(struct line_buffer *input)
 {
        if (buffer_ferror(input))
-               return error("error reading delta: %s", strerror(errno));
+               return error_errno("error reading delta");
        return error("invalid delta: unexpected end of file");
 }
 
index 31d1d83d451e0e78f19067799d14e555a62e2cc8..e4b395963b9457a680a369c3e1997c799f8793ab 100644 (file)
@@ -501,7 +501,7 @@ static void init(int report_fd)
 int svndump_init(const char *filename)
 {
        if (buffer_init(&input, filename))
-               return error("cannot open %s: %s", filename ? filename : "NULL", strerror(errno));
+               return error_errno("cannot open %s", filename ? filename : "NULL");
        init(REPORT_FILENO);
        return 0;
 }
@@ -509,7 +509,7 @@ int svndump_init(const char *filename)
 int svndump_init_fd(int in_fd, int back_fd)
 {
        if(buffer_fdinit(&input, xdup(in_fd)))
-               return error("cannot open fd %d: %s", in_fd, strerror(errno));
+               return error_errno("cannot open fd %d", in_fd);
        init(xdup(back_fd));
        return 0;
 }
index 08773d419f17a131c35e8e671bcfe4b174a9ceb3..d95b007294c84dafcc391edb7813c15523b03633 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -43,12 +43,12 @@ static int process_tree(struct walker *walker, struct tree *tree)
                if (S_ISGITLINK(entry.mode))
                        continue;
                if (S_ISDIR(entry.mode)) {
-                       struct tree *tree = lookup_tree(entry.sha1);
+                       struct tree *tree = lookup_tree(entry.oid->hash);
                        if (tree)
                                obj = &tree->object;
                }
                else {
-                       struct blob *blob = lookup_blob(entry.sha1);
+                       struct blob *blob = lookup_blob(entry.oid->hash);
                        if (blob)
                                obj = &blob->object;
                }
index 89ebe67a505f6e7772f74bd92363230955dc2b74..199b1ef94ba84aad094a5a0fe95360c2df631003 100644 (file)
@@ -2,6 +2,8 @@
 #include "refs.h"
 #include "strbuf.h"
 #include "worktree.h"
+#include "dir.h"
+#include "wt-status.h"
 
 void free_worktrees(struct worktree **worktrees)
 {
@@ -9,7 +11,7 @@ void free_worktrees(struct worktree **worktrees)
 
        for (i = 0; worktrees[i]; i++) {
                free(worktrees[i]->path);
-               free(worktrees[i]->git_dir);
+               free(worktrees[i]->id);
                free(worktrees[i]->head_ref);
                free(worktrees[i]);
        }
@@ -74,13 +76,11 @@ static struct worktree *get_main_worktree(void)
        struct worktree *worktree = NULL;
        struct strbuf path = STRBUF_INIT;
        struct strbuf worktree_path = STRBUF_INIT;
-       struct strbuf gitdir = STRBUF_INIT;
        struct strbuf head_ref = STRBUF_INIT;
        int is_bare = 0;
        int is_detached = 0;
 
-       strbuf_addf(&gitdir, "%s", absolute_path(get_git_common_dir()));
-       strbuf_addbuf(&worktree_path, &gitdir);
+       strbuf_addstr(&worktree_path, absolute_path(get_git_common_dir()));
        is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
        if (is_bare)
                strbuf_strip_suffix(&worktree_path, "/.");
@@ -92,15 +92,15 @@ static struct worktree *get_main_worktree(void)
 
        worktree = xmalloc(sizeof(struct worktree));
        worktree->path = strbuf_detach(&worktree_path, NULL);
-       worktree->git_dir = strbuf_detach(&gitdir, NULL);
+       worktree->id = NULL;
        worktree->is_bare = is_bare;
        worktree->head_ref = NULL;
        worktree->is_detached = is_detached;
+       worktree->is_current = 0;
        add_head_info(&head_ref, worktree);
 
 done:
        strbuf_release(&path);
-       strbuf_release(&gitdir);
        strbuf_release(&worktree_path);
        strbuf_release(&head_ref);
        return worktree;
@@ -111,16 +111,13 @@ static struct worktree *get_linked_worktree(const char *id)
        struct worktree *worktree = NULL;
        struct strbuf path = STRBUF_INIT;
        struct strbuf worktree_path = STRBUF_INIT;
-       struct strbuf gitdir = STRBUF_INIT;
        struct strbuf head_ref = STRBUF_INIT;
        int is_detached = 0;
 
        if (!id)
                die("Missing linked worktree name");
 
-       strbuf_addf(&gitdir, "%s/worktrees/%s",
-                       absolute_path(get_git_common_dir()), id);
-       strbuf_addf(&path, "%s/gitdir", gitdir.buf);
+       strbuf_git_common_path(&path, "worktrees/%s/gitdir", id);
        if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
                /* invalid gitdir file */
                goto done;
@@ -140,20 +137,39 @@ static struct worktree *get_linked_worktree(const char *id)
 
        worktree = xmalloc(sizeof(struct worktree));
        worktree->path = strbuf_detach(&worktree_path, NULL);
-       worktree->git_dir = strbuf_detach(&gitdir, NULL);
+       worktree->id = xstrdup(id);
        worktree->is_bare = 0;
        worktree->head_ref = NULL;
        worktree->is_detached = is_detached;
+       worktree->is_current = 0;
        add_head_info(&head_ref, worktree);
 
 done:
        strbuf_release(&path);
-       strbuf_release(&gitdir);
        strbuf_release(&worktree_path);
        strbuf_release(&head_ref);
        return worktree;
 }
 
+static void mark_current_worktree(struct worktree **worktrees)
+{
+       struct strbuf git_dir = STRBUF_INIT;
+       struct strbuf path = STRBUF_INIT;
+       int i;
+
+       strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
+       for (i = 0; worktrees[i]; i++) {
+               struct worktree *wt = worktrees[i];
+               strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
+               wt->is_current = !fspathcmp(git_dir.buf, path.buf);
+               strbuf_reset(&path);
+               if (wt->is_current)
+                       break;
+       }
+       strbuf_release(&git_dir);
+       strbuf_release(&path);
+}
+
 struct worktree **get_worktrees(void)
 {
        struct worktree **list = NULL;
@@ -185,35 +201,105 @@ struct worktree **get_worktrees(void)
        }
        ALLOC_GROW(list, counter + 1, alloc);
        list[counter] = NULL;
+
+       mark_current_worktree(list);
        return list;
 }
 
-char *find_shared_symref(const char *symref, const char *target)
+const char *get_worktree_git_dir(const struct worktree *wt)
+{
+       if (!wt)
+               return get_git_dir();
+       else if (!wt->id)
+               return get_git_common_dir();
+       else
+               return git_common_path("worktrees/%s", wt->id);
+}
+
+int is_worktree_being_rebased(const struct worktree *wt,
+                             const char *target)
+{
+       struct wt_status_state state;
+       int found_rebase;
+
+       memset(&state, 0, sizeof(state));
+       found_rebase = wt_status_check_rebase(wt, &state) &&
+               ((state.rebase_in_progress ||
+                 state.rebase_interactive_in_progress) &&
+                state.branch &&
+                starts_with(target, "refs/heads/") &&
+                !strcmp(state.branch, target + strlen("refs/heads/")));
+       free(state.branch);
+       free(state.onto);
+       return found_rebase;
+}
+
+int is_worktree_being_bisected(const struct worktree *wt,
+                              const char *target)
 {
-       char *existing = NULL;
+       struct wt_status_state state;
+       int found_rebase;
+
+       memset(&state, 0, sizeof(state));
+       found_rebase = wt_status_check_bisect(wt, &state) &&
+               state.branch &&
+               starts_with(target, "refs/heads/") &&
+               !strcmp(state.branch, target + strlen("refs/heads/"));
+       free(state.branch);
+       return found_rebase;
+}
+
+/*
+ * note: this function should be able to detect shared symref even if
+ * HEAD is temporarily detached (e.g. in the middle of rebase or
+ * bisect). New commands that do similar things should update this
+ * function as well.
+ */
+const struct worktree *find_shared_symref(const char *symref,
+                                         const char *target)
+{
+       const struct worktree *existing = NULL;
        struct strbuf path = STRBUF_INIT;
        struct strbuf sb = STRBUF_INIT;
-       struct worktree **worktrees = get_worktrees();
+       static struct worktree **worktrees;
        int i = 0;
 
+       if (worktrees)
+               free_worktrees(worktrees);
+       worktrees = get_worktrees();
+
        for (i = 0; worktrees[i]; i++) {
+               struct worktree *wt = worktrees[i];
+
+               if (wt->is_detached && !strcmp(symref, "HEAD")) {
+                       if (is_worktree_being_rebased(wt, target)) {
+                               existing = wt;
+                               break;
+                       }
+                       if (is_worktree_being_bisected(wt, target)) {
+                               existing = wt;
+                               break;
+                       }
+               }
+
                strbuf_reset(&path);
                strbuf_reset(&sb);
-               strbuf_addf(&path, "%s/%s", worktrees[i]->git_dir, symref);
+               strbuf_addf(&path, "%s/%s",
+                           get_worktree_git_dir(wt),
+                           symref);
 
                if (parse_ref(path.buf, &sb, NULL)) {
                        continue;
                }
 
                if (!strcmp(sb.buf, target)) {
-                       existing = xstrdup(worktrees[i]->path);
+                       existing = wt;
                        break;
                }
        }
 
        strbuf_release(&path);
        strbuf_release(&sb);
-       free_worktrees(worktrees);
 
        return existing;
 }
index b4b3dda79280707d2d65bf790be15c7a1e662b5e..13949093cc1610fcafbefb22d1f8b9441fbd4ade 100644 (file)
@@ -3,11 +3,12 @@
 
 struct worktree {
        char *path;
-       char *git_dir;
+       char *id;
        char *head_ref;
        unsigned char head_sha1[20];
        int is_detached;
        int is_bare;
+       int is_current;
 };
 
 /* Functions for acting on the information about worktrees. */
@@ -22,6 +23,12 @@ struct worktree {
  */
 extern struct worktree **get_worktrees(void);
 
+/*
+ * Return git dir of the worktree. Note that the path may be relative.
+ * If wt is NULL, git dir of current worktree is returned.
+ */
+extern const char *get_worktree_git_dir(const struct worktree *wt);
+
 /*
  * Free up the memory for worktree(s)
  */
@@ -29,10 +36,21 @@ extern void free_worktrees(struct worktree **);
 
 /*
  * Check if a per-worktree symref points to a ref in the main worktree
- * or any linked worktree, and return the path to the exising worktree
- * if it is.  Returns NULL if there is no existing ref.  The caller is
- * responsible for freeing the returned path.
+ * or any linked worktree, and return the worktree that holds the ref,
+ * or NULL otherwise. The result may be destroyed by the next call.
+ */
+extern const struct worktree *find_shared_symref(const char *symref,
+                                                const char *target);
+
+int is_worktree_being_rebased(const struct worktree *wt, const char *target);
+int is_worktree_being_bisected(const struct worktree *wt, const char *target);
+
+/*
+ * Similar to git_path() but can produce paths for a specified
+ * worktree instead of current one
  */
-extern char *find_shared_symref(const char *symref, const char *target);
+extern const char *worktree_git_path(const struct worktree *wt,
+                                    const char *fmt, ...)
+       __attribute__((format (printf, 2, 3)));
 
 #endif
index db0ec6a7370fd56e4e11c49a4b9e5e15f461e1f9..22b6e4948fbfa97da33dd0299da2130944940a42 100644 (file)
@@ -17,6 +17,7 @@ fi
 GITPERLLIB='@@BUILD_DIR@@/perl/blib/lib'"${GITPERLLIB:+:$GITPERLLIB}"
 GIT_TEXTDOMAINDIR='@@BUILD_DIR@@/po/build/locale'
 PATH='@@BUILD_DIR@@/bin-wrappers:'"$PATH"
+
 export GIT_EXEC_PATH GITPERLLIB PATH GIT_TEXTDOMAINDIR
 
 if test -n "$GIT_TEST_GDB"
index 9009f8bd3d32a768454ad5fbe6cb8f7b88781a24..5dc4e15aa9bf73bb2ce0a7af1837e092e0cab585 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -555,7 +555,7 @@ static int warn_if_unremovable(const char *op, const char *file, int rc)
        if (!rc || errno == ENOENT)
                return 0;
        err = errno;
-       warning("unable to %s %s: %s", op, file, strerror(errno));
+       warning_errno("unable to %s %s", op, file);
        errno = err;
        return rc;
 }
@@ -591,7 +591,7 @@ int remove_or_warn(unsigned int mode, const char *file)
 
 void warn_on_inaccessible(const char *path)
 {
-       warning(_("unable to access '%s': %s"), path, strerror(errno));
+       warning_errno(_("unable to access '%s'"), path);
 }
 
 static int access_error_is_ok(int err, unsigned flag)
index 1ea2ebe4c00d23885515ac48f43844fdb03f70b9..4f27bd62af992122d83df911c42b585342ac1066 100644 (file)
@@ -15,6 +15,7 @@
 #include "column.h"
 #include "strbuf.h"
 #include "utf8.h"
+#include "worktree.h"
 
 static const char cut_line[] =
 "------------------------ >8 ------------------------\n";
@@ -950,6 +951,7 @@ static void show_merge_in_progress(struct wt_status *s,
                        status_printf_ln(s, color,
                                _("  (fix conflicts and run \"git commit\")"));
        } else {
+               s-> commitable = 1;
                status_printf_ln(s, color,
                        _("All conflicts fixed but you are still merging."));
                if (s->hints)
@@ -1262,13 +1264,13 @@ static void show_bisect_in_progress(struct wt_status *s,
 /*
  * Extract branch information from rebase/bisect
  */
-static char *read_and_strip_branch(const char *path)
+static char *get_branch(const struct worktree *wt, const char *path)
 {
        struct strbuf sb = STRBUF_INIT;
        unsigned char sha1[20];
        const char *branch_name;
 
-       if (strbuf_read_file(&sb, git_path("%s", path), 0) <= 0)
+       if (strbuf_read_file(&sb, worktree_git_path(wt, "%s", path), 0) <= 0)
                goto got_nothing;
 
        while (sb.len && sb.buf[sb.len - 1] == '\n')
@@ -1360,40 +1362,62 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
        strbuf_release(&cb.buf);
 }
 
-void wt_status_get_state(struct wt_status_state *state,
-                        int get_detached_from)
+int wt_status_check_rebase(const struct worktree *wt,
+                          struct wt_status_state *state)
 {
        struct stat st;
-       unsigned char sha1[20];
 
-       if (!stat(git_path_merge_head(), &st)) {
-               state->merge_in_progress = 1;
-       } else if (!stat(git_path("rebase-apply"), &st)) {
-               if (!stat(git_path("rebase-apply/applying"), &st)) {
+       if (!stat(worktree_git_path(wt, "rebase-apply"), &st)) {
+               if (!stat(worktree_git_path(wt, "rebase-apply/applying"), &st)) {
                        state->am_in_progress = 1;
-                       if (!stat(git_path("rebase-apply/patch"), &st) && !st.st_size)
+                       if (!stat(worktree_git_path(wt, "rebase-apply/patch"), &st) && !st.st_size)
                                state->am_empty_patch = 1;
                } else {
                        state->rebase_in_progress = 1;
-                       state->branch = read_and_strip_branch("rebase-apply/head-name");
-                       state->onto = read_and_strip_branch("rebase-apply/onto");
+                       state->branch = get_branch(wt, "rebase-apply/head-name");
+                       state->onto = get_branch(wt, "rebase-apply/onto");
                }
-       } else if (!stat(git_path("rebase-merge"), &st)) {
-               if (!stat(git_path("rebase-merge/interactive"), &st))
+       } else if (!stat(worktree_git_path(wt, "rebase-merge"), &st)) {
+               if (!stat(worktree_git_path(wt, "rebase-merge/interactive"), &st))
                        state->rebase_interactive_in_progress = 1;
                else
                        state->rebase_in_progress = 1;
-               state->branch = read_and_strip_branch("rebase-merge/head-name");
-               state->onto = read_and_strip_branch("rebase-merge/onto");
+               state->branch = get_branch(wt, "rebase-merge/head-name");
+               state->onto = get_branch(wt, "rebase-merge/onto");
+       } else
+               return 0;
+       return 1;
+}
+
+int wt_status_check_bisect(const struct worktree *wt,
+                          struct wt_status_state *state)
+{
+       struct stat st;
+
+       if (!stat(worktree_git_path(wt, "BISECT_LOG"), &st)) {
+               state->bisect_in_progress = 1;
+               state->branch = get_branch(wt, "BISECT_START");
+               return 1;
+       }
+       return 0;
+}
+
+void wt_status_get_state(struct wt_status_state *state,
+                        int get_detached_from)
+{
+       struct stat st;
+       unsigned char sha1[20];
+
+       if (!stat(git_path_merge_head(), &st)) {
+               state->merge_in_progress = 1;
+       } else if (wt_status_check_rebase(NULL, state)) {
+               ;               /* all set */
        } else if (!stat(git_path_cherry_pick_head(), &st) &&
                        !get_sha1("CHERRY_PICK_HEAD", sha1)) {
                state->cherry_pick_in_progress = 1;
                hashcpy(state->cherry_pick_head_sha1, sha1);
        }
-       if (!stat(git_path("BISECT_LOG"), &st)) {
-               state->bisect_in_progress = 1;
-               state->branch = read_and_strip_branch("BISECT_START");
-       }
+       wt_status_check_bisect(NULL, state);
        if (!stat(git_path_revert_head(), &st) &&
            !get_sha1("REVERT_HEAD", sha1)) {
                state->revert_in_progress = 1;
index c9b3b744e923f2f559f64f1bf4a1f2159b5060d6..2ca93f6957f69cd5652ddc764f5afe81b598a2a9 100644 (file)
@@ -6,6 +6,8 @@
 #include "color.h"
 #include "pathspec.h"
 
+struct worktree;
+
 enum color_wt_status {
        WT_STATUS_HEADER = 0,
        WT_STATUS_UPDATED,
@@ -100,6 +102,10 @@ void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);
 void wt_status_collect(struct wt_status *s);
 void wt_status_get_state(struct wt_status_state *state, int get_detached_from);
+int wt_status_check_rebase(const struct worktree *wt,
+                          struct wt_status_state *state);
+int wt_status_check_bisect(const struct worktree *wt,
+                          struct wt_status_state *state);
 
 void wt_shortstatus_print(struct wt_status *s);
 void wt_porcelain_print(struct wt_status *s);
index 4fb7e79410c22fba1fb390af2e09008e932f5ea8..7423f77fc8b2682c121e8d4adb855337e107b29e 100644 (file)
@@ -41,6 +41,8 @@ extern "C" {
 
 #define XDF_IGNORE_BLANK_LINES (1 << 7)
 
+#define XDF_COMPACTION_HEURISTIC (1 << 8)
+
 #define XDL_EMIT_FUNCNAMES (1 << 0)
 #define XDL_EMIT_FUNCCONTEXT (1 << 2)
 
index 2358a2d6326e54308413cb8a5e6b61eba06324e9..b3c684887502c3c473a614114e4acdfbc57ea094 100644 (file)
@@ -400,9 +400,23 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,
 }
 
 
+static int is_blank_line(xrecord_t **recs, long ix, long flags)
+{
+       return xdl_blankline(recs[ix]->ptr, recs[ix]->size, flags);
+}
+
+static int recs_match(xrecord_t **recs, long ixs, long ix, long flags)
+{
+       return (recs[ixs]->ha == recs[ix]->ha &&
+               xdl_recmatch(recs[ixs]->ptr, recs[ixs]->size,
+                            recs[ix]->ptr, recs[ix]->size,
+                            flags));
+}
+
 int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
        long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec;
        char *rchg = xdf->rchg, *rchgo = xdfo->rchg;
+       unsigned int blank_lines;
        xrecord_t **recs = xdf->recs;
 
        /*
@@ -436,14 +450,14 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
 
                do {
                        grpsiz = ix - ixs;
+                       blank_lines = 0;
 
                        /*
                         * If the line before the current change group, is equal to
                         * the last line of the current change group, shift backward
                         * the group.
                         */
-                       while (ixs > 0 && recs[ixs - 1]->ha == recs[ix - 1]->ha &&
-                              xdl_recmatch(recs[ixs - 1]->ptr, recs[ixs - 1]->size, recs[ix - 1]->ptr, recs[ix - 1]->size, flags)) {
+                       while (ixs > 0 && recs_match(recs, ixs - 1, ix - 1, flags)) {
                                rchg[--ixs] = 1;
                                rchg[--ix] = 0;
 
@@ -470,8 +484,9 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
                         * the line next of the current change group, shift forward
                         * the group.
                         */
-                       while (ix < nrec && recs[ixs]->ha == recs[ix]->ha &&
-                              xdl_recmatch(recs[ixs]->ptr, recs[ixs]->size, recs[ix]->ptr, recs[ix]->size, flags)) {
+                       while (ix < nrec && recs_match(recs, ixs, ix, flags)) {
+                               blank_lines += is_blank_line(recs, ix, flags);
+
                                rchg[ixs++] = 0;
                                rchg[ix++] = 1;
 
@@ -498,6 +513,23 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
                        rchg[--ix] = 0;
                        while (rchgo[--ixo]);
                }
+
+               /*
+                * If a group can be moved back and forth, see if there is a
+                * blank line in the moving space. If there is a blank line,
+                * make sure the last blank line is the end of the group.
+                *
+                * As we already shifted the group forward as far as possible
+                * in the earlier loop, we need to shift it back only if at all.
+                */
+               if ((flags & XDF_COMPACTION_HEURISTIC) && blank_lines) {
+                       while (ixs > 0 &&
+                              !is_blank_line(recs, ix - 1, flags) &&
+                              recs_match(recs, ixs - 1, ix - 1, flags)) {
+                               rchg[--ixs] = 1;
+                               rchg[--ix] = 0;
+                       }
+               }
        }
 
        return 0;